Open sn01615 opened 6 years ago
I do have the same problem? Any ideas?
I also have similar doubts.
see https://code.google.com/archive/p/crypto-js/ I know default mode is 'CBC', and default padding is 'Pkcs7'. If i use custom Key and IV, like this:
var key = CryptoJS.enc.Hex.parse('000102030405060708090a0b0c0d0e0f'); var iv = CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f'); var encrypted = CryptoJS.AES.encrypt("Message", key, { iv: iv });
I can achieve the same way in java.
But, if like this: "CryptoJS.AES.encrypt('my message', 'secret key 123');" I do not know how to convert the password to key bytes, and default IV bytes.
I was curious too so I did some digging. The result from a call like:
CryptoJS.AES.encrypt('my message', 'secret key 123');
returns an object that has all of the needed information to decrypt the ciphertext (even the key itself! so don't save that object anywhere! )
For example here I can build a lookup object with the key, iv and ciphertext in base64 encoded format:
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123');
console.log(JSON.stringify({key:ciphertext.key.toString(CryptoJS.enc.Base64), iv:ciphertext.iv.toString(CryptoJS.enc.Base64), ciphertext:ciphertext.ciphertext.toString(CryptoJS.enc.Base64)}))
I can then take that object, and get the original plaintext in, for example, python using it's cryptography library:
import os
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from base64 import b64encode, b64decode
js = {"key":"FseXqPPUgnuscV5t4xajK+TyaIzhCr6ezjKHgeTO7HI=","iv":"JrMAW5tzT9umMrlWSdtSwg==","ciphertext":"3UIxU1zMdAjtAQ4XHltVWA=="}
# base64 decode the buffers for everything
iv = b64decode(js["iv"])
key = b64decode(js["key"])
ciphertext = b64decode(js["ciphertext"])
# construct a decryptor with the IV and key
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
d = cipher.decryptor()
#get the (padded) plaintext out
padded_plaintext = d.update(ciphertext) + d.finalize()
#remove the padding to get the original plaintext
unpadder = padding.PKCS7(128).unpadder()
plaintext = unpadder.update(padded_plaintext) + unpadder.finalize()
print(plaintext)
@DanBeard it does not work. The error is in the penultimate line: plaintext = unpadder.update(padded_plaintext) + unpadder.finalize();
Thanks @Sk3pper - misnamed variable. Somehow a semicolon snuck in there and I forgot to import b64encode. I wonder how I got that snippet to work on my machine in the first place!
Help for android studio aes-256-cbc
I was curious too so I did some digging. The result from a call like:
CryptoJS.AES.encrypt('my message', 'secret key 123');
returns an object that has all of the needed information to decrypt the ciphertext (even the key itself! so don't save that object anywhere! )
For example here I can build a lookup object with the key, iv and ciphertext in base64 encoded format:
var ciphertext = CryptoJS.AES.encrypt('my message', 'secret key 123'); console.log(JSON.stringify({key:ciphertext.key.toString(CryptoJS.enc.Base64), iv:ciphertext.iv.toString(CryptoJS.enc.Base64), ciphertext:ciphertext.ciphertext.toString(CryptoJS.enc.Base64)}))
I can then take that object, and get the original plaintext in, for example, python using it's cryptography library:
import os from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import padding from base64 import b64encode, b64decode js = {"key":"FseXqPPUgnuscV5t4xajK+TyaIzhCr6ezjKHgeTO7HI=","iv":"JrMAW5tzT9umMrlWSdtSwg==","ciphertext":"3UIxU1zMdAjtAQ4XHltVWA=="} # base64 decode the buffers for everything iv = b64decode(js["iv"]) key = b64decode(js["key"]) ciphertext = b64decode(js["ciphertext"]) # construct a decryptor with the IV and key cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) d = cipher.decryptor() #get the (padded) plaintext out padded_plaintext = d.update(ciphertext) + d.finalize() #remove the padding to get the original plaintext unpadder = padding.PKCS7(128).unpadder() plaintext = unpadder.update(padded_plaintext) + unpadder.finalize() print(plaintext)
If someone is interested in the reverse i.e get the cipher text
userinput = b'helloworld'
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) encryptor=cipher.encryptor() padder = padding.PKCS7(128).padder() padded_data = padder.update(userinput) padded_data += padder.finalize() ct = b64encode(encryptor.update(padded_data)) print(ct)
I see that this article implements the above AES encryption and decryption operation in python. And realize the conversion from password to key and iv in python.
Encryption and decryption of AES algorithm for Crypto-JS and Python
It helps me understand the operation flow that crypto-js uses passwords instead of key and iv to encrypt and decrypt AES. Maybe this codes can help others.
The following code comes from the above article
when use CryptoJS:
var text = "123456";
var passphrase = "0123456789asdfgh"; // 十六位字符串作为密钥
var encryptedMessage = CryptoJS.AES.encrypt(text, passphrase).toString();
console.log("encrypt:", encryptedMessage);
// encrypt: U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4=
var decryptedMessage = CryptoJS.AES.decrypt(encryptedMessage, passphrase).toString(CryptoJS.enc.Utf8);
console.log("decrypt:", decryptedMessage);
// decrypt: 123456
when use pycryptodome(python3):
import base64
from Crypto.Cipher import AES
from Crypto import Random
def pad(s):
return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode()
def unpad(s):
return s[0:-ord(s[len(s)-1:])]
def bytes_to_key(data, salt, output=48):
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt(data, passphrase):
salt = Random.new().read(8)
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
cipherbyte = base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(data)))
return cipherbyte
def decrypt(data, passphrase):
data = base64.b64decode(data)
assert data[:8] == b'Salted__'
salt = data[8:16]
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
plainbyte = unpad(aes.decrypt(data[16:]))
return plainbyte
if __name__ == '__main__':
data = b'123456'
passphrase = b'0123456789asdfgh'
# encrypt_data = encrypt(data, passphrase)
# print('encrypt_data:', encrypt_data)
encrypt_data = b"U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4="
decrypt_data = decrypt(encrypt_data, passphrase)
print('decrypt_data:', decrypt_data)
when use cryptography(python3):
import os
import base64
from hashlib import md5
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def pad(s):
return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode()
def unpad(s):
return s[0:-ord(s[len(s)-1:])]
def bytes_to_key(data, salt, output=48):
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt(data, passphrase):
salt = os.urandom(8)
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
encryptor = cipher.encryptor()
encrypted = encryptor.update(pad(data)) + encryptor.finalize()
cipherbyte = base64.b64encode(b"Salted__" + salt + encrypted)
return cipherbyte
def decrypt(data, passphrase):
data = base64.b64decode(data)
assert data[:8] == b'Salted__'
salt = data[8:16]
key_iv = bytes_to_key(passphrase, salt, 32+16)
key = key_iv[:32]
iv = key_iv[32:]
cipher = Cipher(algorithms.AES(key), modes.CBC(iv))
decryptor = cipher.decryptor()
plainbyte = unpad(decryptor.update(data[16:]) + decryptor.finalize())
return plainbyte
if __name__ == '__main__':
data = b'123456'
passphrase = b'0123456789asdfgh'
encrypt_data = encrypt(data, passphrase)
print('encrypt_data:', encrypt_data)
#encrypt_data = b"U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4="
decrypt_data = decrypt(encrypt_data, passphrase)
print('decrypt_data:', decrypt_data)
when use CryptoJS:
function Str2Bytes(str) {
var bytes = [];
for (var i = 0; i < str.length; i++) {
var charCode = str.charCodeAt(i);
bytes.push(charCode);
}
return bytes;
}
function Bytes2Str(bytes) {
var str = "";
for (var i = 0; i < bytes.length; i++) {
var char = String.fromCharCode(bytes[i]);
str = str + char;
}
return str;
}
/**
* 密码 `0` 填充
* @param {string} key 密码
* @param {Number} keySize 填充长度, 值: 16,32,64,128
*/
function fillKey(key, keySize) {
keySize = keySize || 128;
var buffer = new ArrayBuffer(keySize);
var view = new Int8Array(buffer);
var bytes = Str2Bytes(key);
var keys = Int8Array.from(bytes);
if (keys.length < view.length) {
for (var i = 0; i < view.length; i++) {
if (keys[i] == undefined) {
view[i] = 48;
} else {
view[i] = keys[i];
}
}
return Bytes2Str(view);
} else {
return key;
}
}
function unfillKey(key) {
var bytes = [];
var keys = Str2Bytes(key);
for (var i = 0; i < keys.length; i++) {
if (keys[i] != 48) {
bytes.push(keys[i]);
}
}
return Bytes2Str(bytes);
}
var text = "123456";
//var key = fillKey(key, 16);
//var iv = fillKey(iv, 16);
const key = CryptoJS.enc.Utf8.parse("1234123412ASDFGH"); //十六位字符串作为密钥
const iv = CryptoJS.enc.Utf8.parse('ASDFGH1234123412'); //十六位字符串作为偏移量
var encrypted = CryptoJS.AES.encrypt(text, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 转换为字符串
encryptedStr = encrypted.toString();
console.log("encrypted:", encryptedStr);
// encrypted:D6AFFsf6c1De2EsRDEOPBA==
var decrypted = CryptoJS.AES.decrypt(encryptedStr, key, {
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 转换为 utf8 字符串
decryptedStr = CryptoJS.enc.Utf8.stringify(decrypted);
console.log("decrypted:", decryptedStr);
// decrypted:123456
when use pycryptodome(python3):
import base64
from Crypto.Cipher import AES
from Crypto import Random
def pad(s):
return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode()
def unpad(s):
return s[0:-ord(s[len(s)-1:])]
def encrypt(data, key, iv):
aes = AES.new(key, AES.MODE_CBC, iv)
cipherbyte = base64.b64encode(aes.encrypt(pad(data)))
return cipherbyte
def decrypt(data, key, iv):
data = base64.b64decode(data)
aes = AES.new(key, AES.MODE_CBC, iv)
plainbyte = unpad(aes.decrypt(data))
return plainbyte
if __name__ == '__main__':
key = b"1234123412ASDFGH"
iv = b"ASDFGH1234123412"
data = b"123456"
# encrypt_data = encrypt(data, key, iv)
# print("encrypt_data", encrypt_data)
encrypt_data = b"D6AFFsf6c1De2EsRDEOPBA=="
decrypt_data = decrypt(encrypt_data, key, iv)
print('decrypt_data:', decrypt_data)
Thanks @julydate! This worked for me!
I see that this article implements the above AES encryption and decryption operation in python. And realize the conversion from password to key and iv in python.
Encryption and decryption of AES algorithm for Crypto-JS and Python
It helps me understand the operation flow that crypto-js uses passwords instead of key and iv to encrypt and decrypt AES. Maybe this codes can help others.
The following code comes from the above article
Use password only
when use CryptoJS:
var text = "123456"; var passphrase = "0123456789asdfgh"; // 十六位字符串作为密钥 var encryptedMessage = CryptoJS.AES.encrypt(text, passphrase).toString(); console.log("encrypt:", encryptedMessage); // encrypt: U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4= var decryptedMessage = CryptoJS.AES.decrypt(encryptedMessage, passphrase).toString(CryptoJS.enc.Utf8); console.log("decrypt:", decryptedMessage); // decrypt: 123456
when use pycryptodome(python3):
import base64 from Crypto.Cipher import AES from Crypto import Random def pad(s): return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode() def unpad(s): return s[0:-ord(s[len(s)-1:])] def bytes_to_key(data, salt, output=48): assert len(salt) == 8, len(salt) data += salt key = md5(data).digest() final_key = key while len(final_key) < output: key = md5(key + data).digest() final_key += key return final_key[:output] def encrypt(data, passphrase): salt = Random.new().read(8) key_iv = bytes_to_key(passphrase, salt, 32+16) key = key_iv[:32] iv = key_iv[32:] aes = AES.new(key, AES.MODE_CBC, iv) cipherbyte = base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(data))) return cipherbyte def decrypt(data, passphrase): data = base64.b64decode(data) assert data[:8] == b'Salted__' salt = data[8:16] key_iv = bytes_to_key(passphrase, salt, 32+16) key = key_iv[:32] iv = key_iv[32:] aes = AES.new(key, AES.MODE_CBC, iv) plainbyte = unpad(aes.decrypt(data[16:])) return plainbyte if __name__ == '__main__': data = b'123456' passphrase = b'0123456789asdfgh' # encrypt_data = encrypt(data, passphrase) # print('encrypt_data:', encrypt_data) encrypt_data = b"U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4=" decrypt_data = decrypt(encrypt_data, passphrase) print('decrypt_data:', decrypt_data)
when use cryptography(python3):
import os import base64 from hashlib import md5 from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes def pad(s): return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode() def unpad(s): return s[0:-ord(s[len(s)-1:])] def bytes_to_key(data, salt, output=48): assert len(salt) == 8, len(salt) data += salt key = md5(data).digest() final_key = key while len(final_key) < output: key = md5(key + data).digest() final_key += key return final_key[:output] def encrypt(data, passphrase): salt = os.urandom(8) key_iv = bytes_to_key(passphrase, salt, 32+16) key = key_iv[:32] iv = key_iv[32:] cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) encryptor = cipher.encryptor() encrypted = encryptor.update(pad(data)) + encryptor.finalize() cipherbyte = base64.b64encode(b"Salted__" + salt + encrypted) return cipherbyte def decrypt(data, passphrase): data = base64.b64decode(data) assert data[:8] == b'Salted__' salt = data[8:16] key_iv = bytes_to_key(passphrase, salt, 32+16) key = key_iv[:32] iv = key_iv[32:] cipher = Cipher(algorithms.AES(key), modes.CBC(iv)) decryptor = cipher.decryptor() plainbyte = unpad(decryptor.update(data[16:]) + decryptor.finalize()) return plainbyte if __name__ == '__main__': data = b'123456' passphrase = b'0123456789asdfgh' encrypt_data = encrypt(data, passphrase) print('encrypt_data:', encrypt_data) #encrypt_data = b"U2FsdGVkX18hyuQnNnZyAe7emBZrUR/YGmy90QN1DI4=" decrypt_data = decrypt(encrypt_data, passphrase) print('decrypt_data:', decrypt_data)
Use key and iv
when use CryptoJS:
function Str2Bytes(str) { var bytes = []; for (var i = 0; i < str.length; i++) { var charCode = str.charCodeAt(i); bytes.push(charCode); } return bytes; } function Bytes2Str(bytes) { var str = ""; for (var i = 0; i < bytes.length; i++) { var char = String.fromCharCode(bytes[i]); str = str + char; } return str; } /** * 密码 `0` 填充 * @param {string} key 密码 * @param {Number} keySize 填充长度, 值: 16,32,64,128 */ function fillKey(key, keySize) { keySize = keySize || 128; var buffer = new ArrayBuffer(keySize); var view = new Int8Array(buffer); var bytes = Str2Bytes(key); var keys = Int8Array.from(bytes); if (keys.length < view.length) { for (var i = 0; i < view.length; i++) { if (keys[i] == undefined) { view[i] = 48; } else { view[i] = keys[i]; } } return Bytes2Str(view); } else { return key; } } function unfillKey(key) { var bytes = []; var keys = Str2Bytes(key); for (var i = 0; i < keys.length; i++) { if (keys[i] != 48) { bytes.push(keys[i]); } } return Bytes2Str(bytes); } var text = "123456"; //var key = fillKey(key, 16); //var iv = fillKey(iv, 16); const key = CryptoJS.enc.Utf8.parse("1234123412ASDFGH"); //十六位字符串作为密钥 const iv = CryptoJS.enc.Utf8.parse('ASDFGH1234123412'); //十六位字符串作为偏移量 var encrypted = CryptoJS.AES.encrypt(text, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 转换为字符串 encryptedStr = encrypted.toString(); console.log("encrypted:", encryptedStr); // encrypted:D6AFFsf6c1De2EsRDEOPBA== var decrypted = CryptoJS.AES.decrypt(encryptedStr, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); // 转换为 utf8 字符串 decryptedStr = CryptoJS.enc.Utf8.stringify(decrypted); console.log("decrypted:", decryptedStr); // decrypted:123456
when use pycryptodome(python3):
import base64 from Crypto.Cipher import AES from Crypto import Random def pad(s): return s + (16 - len(s) % 16) * chr(16 - len(s) % 16).encode() def unpad(s): return s[0:-ord(s[len(s)-1:])] def encrypt(data, key, iv): aes = AES.new(key, AES.MODE_CBC, iv) cipherbyte = base64.b64encode(aes.encrypt(pad(data))) return cipherbyte def decrypt(data, key, iv): data = base64.b64decode(data) aes = AES.new(key, AES.MODE_CBC, iv) plainbyte = unpad(aes.decrypt(data)) return plainbyte if __name__ == '__main__': key = b"1234123412ASDFGH" iv = b"ASDFGH1234123412" data = b"123456" # encrypt_data = encrypt(data, key, iv) # print("encrypt_data", encrypt_data) encrypt_data = b"D6AFFsf6c1De2EsRDEOPBA==" decrypt_data = decrypt(encrypt_data, key, iv) print('decrypt_data:', decrypt_data)
I done some implementation on java https://github.com/orthlus/java-cryptojs-helper Maybe helpful
php code
javascript code