I'm not sure it's a bug or it's my fault, but better be safe than sorry. I tried to encrypt "aaaaaaaaaaa" in aes-256-ecb the results doesn't match an online tool:
var encrypt = function(cryptkey, cleardata) {
var encipher = crypto.createCipher('aes-256-ecb', cryptkey);
return Buffer.concat([
encipher.update(cleardata),
encipher.final()
]);
}
var buf_hex_key = new Buffer([0x2A,0x46,0x29,0x4A,0x40,0x4E,0x63,0x52,0x66,0x55,0x6A,0x58,0x6E,0x32,0x72,
0x35,0x75,0x38,0x78,0x2F,0x41,0x3F,0x44,0x28,0x47,0x2B,0x4B,0x61,0x50,0x64,0x53,0x67])
var buf_hex_plain_text = new Buffer([0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61])
console.log(encrypt(buf_hex_key,buf_hex_plain_text))
//prints
// <Buffer 2c 2d 90 b3 0f ef 3d 6f 73 68 db da 72 34 9c 80>
Here I encrypted the same string "aaaaaaaaaaa" with the same key, the result is:
ad 5b f9 11 82 ce dd 1d 1d b0 da b3 48 c1 c8 b8
(edited by @addaleax: syntax highlighting)
I鈥檓 certainly not an expert, but I cannot see anything wrong with your code.
/cc @nodejs/crypto
crypto.createCipher() does key derivation/stretching (don't ask - https://gist.github.com/bnoordhuis/2de2766d3d3a47ebe41aaaec7e8b14df is the algorithm if you're interested.)
Use crypto.createCipheriv() with a zero-length IV and you should get the expected output. I'll close this out, cheers.
You are using buf_hex_key as a password, not as the key. createCipher derives a key from the password instead of using the password itself as the key: The password is 2a46294a404e635266556a586e3272357538782f413f4428472b4b6150645367 and the generated key is 2445acf9c7c26716c2b58bb94e7357d1e594a5653c66bb056e7545b3d9527499. You should use crypto.createCipheriv:
const crypto = require('crypto');
const key = Buffer.from('2a46294a404e635266556a586e3272357538782f413f4428472b4b6150645367', 'hex');
const plaintext = Buffer.from('aaaaaaaaaaa', 'utf8');
const cipher = crypto.createCipheriv('aes-256-ecb', key , Buffer.from([]));
const ciphertext = Buffer.concat([ cipher.update(plaintext), cipher.final() ]);
console.log(ciphertext.toString('hex'));
// prints 8fc98936ba7b162aa8bc11a4b4cde308
You can verify the result with any other crypto library, e.g. PyCrypto:
from binascii import unhexlify, hexlify
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
key = unhexlify('2A46294A404E635266556A586E3272357538782F413F4428472B4B6150645367')
data = pad(unhexlify('6161616161616161616161'), 16)
cipher = AES.new(key, AES.MODE_ECB)
ciphertext = cipher.encrypt(data)
print(hexlify(ciphertext))
# prints b'8fc98936ba7b162aa8bc11a4b4cde308'
@tniessen thanks, but as you can see the output still doesn't match that online tool.
I even checked with a c++ library, the output should be:
ad 5b f9 11 82 ce dd 1d 1d b0 da b3 48 c1 c8 b8
Two separate crypto libraries agree; you can hardly argue with that. Chances are the bug is in that tool.
I even checked with a c++ library, the output should be:
ad 5b f9 11 82 ce dd 1d 1d b0 da b3 48 c1 c8 b8
Can you show us your code? If you didn't believe me after seeing what node and PyCrypto compute, let's try javax.crypto:
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
Key key = new SecretKeySpec(new byte[] { 0x2A, 0x46, ..., 0x53, 0x67 }, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] ciphertext = cipher.doFinal("aaaaaaaaaaa".getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : ciphertext)
sb.append(String.format("%02x", b & 0xff));
System.out.println(sb);
// prints 8fc98936ba7b162aa8bc11a4b4cde308
Nope, still the same. Let's try a fourth codebase, .NET, just for fun:
using (Aes aes = new AesManaged { Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 })
{
byte[] key = new byte[] { 0x2A, 0x46, ..., 0x53, 0x67 };
byte[] data = new byte[] { 0x61, 0x61, ..., 0x61, 0x61 };
ICryptoTransform s = aes.CreateEncryptor(key, null);
byte[] cipher = s.TransformFinalBlock(data, 0, 11);
Console.Write(BitConverter.ToString(cipher));
// prints 8F-C9-89-36-BA-7B-16-2A-A8-BC-11-A4-B4-CD-E3-08
}
Still correct.
Ah, I got it. It seems like you are (unintentionally) using zero padding (PaddingMode.Zeros in .NET). Unless you know exactly what you are doing, you should probably stick to PKCS#7 padding, but if you really want to use zero padding, you can do the same with node:
const crypto = require('crypto');
const key = Buffer.from('2a46294a404e635266556a586e3272357538782f413f4428472b4b6150645367', 'hex');
const plaintext = Buffer.from('aaaaaaaaaaa', 'utf8');
function zeroPad(data) {
const bs = data.length % 16;
return bs === 0 ? data :
Buffer.concat([ data, Buffer.alloc(16 - bs, 0) ]);
}
const data = zeroPad(plaintext);
const cipher = crypto.createCipheriv('aes-256-ecb', key , Buffer.from([]));
cipher.setAutoPadding(false);
const ciphertext = Buffer.concat([ cipher.update(data), cipher.final() ]);
console.log(ciphertext.toString('hex'));
// prints ad5bf91182cedd1d1db0dab348c1c8b8
Again, neither ECB nor zero padding is an obvious choice, so unless there is a good reason to use either setting, consider using a more secure mode such as CBC with a random IV and PKCS#7 padding.
This is the class I tried https://github.com/Urban82/Aes256.
Very thanks for your time.
This is the class I tried https://github.com/Urban82/Aes256.
I didn't have a closer look, but I would personally advise against using that implementation for various reasons.
thanks, maybe you can recommend a good c++ library?
I choose that library and ECB because they're easy to manage and integrate in my project (it's just an academic proof of concept, the pratical security is not a main issue)
No personal opinion there. Node.js is written in C++ and we are using OpenSSL which is the most popular crypto library there is, though some people might prefer LibreSSL. If you are looking for a more C++-ish library, Crypto++ seems to be a popular choice, but I never used it, so I don't have any insights about usability, features or security.
Ah, I got it. It seems like you are (unintentionally) using zero padding (
PaddingMode.Zerosin .NET). Unless you know exactly what you are doing, you should probably stick to PKCS#7 padding, but if you really want to use zero padding, you can do the same with node:const crypto = require('crypto'); const key = Buffer.from('2a46294a404e635266556a586e3272357538782f413f4428472b4b6150645367', 'hex'); const plaintext = Buffer.from('aaaaaaaaaaa', 'utf8'); function zeroPad(data) { const bs = data.length % 16; return bs === 0 ? data : Buffer.concat([ data, Buffer.alloc(16 - bs, 0) ]); } const data = zeroPad(plaintext); const cipher = crypto.createCipheriv('aes-256-ecb', key , Buffer.from([])); cipher.setAutoPadding(false); const ciphertext = Buffer.concat([ cipher.update(data), cipher.final() ]); console.log(ciphertext.toString('hex')); // prints ad5bf91182cedd1d1db0dab348c1c8b8Again, neither ECB nor zero padding is an obvious choice, so unless there is a good reason to use either setting, consider using a more secure mode such as CBC with a random IV and PKCS#7 padding.
Thanks Tniessen,
Most helpful comment
Ah, I got it. It seems like you are (unintentionally) using zero padding (
PaddingMode.Zerosin .NET). Unless you know exactly what you are doing, you should probably stick to PKCS#7 padding, but if you really want to use zero padding, you can do the same with node:Again, neither ECB nor zero padding is an obvious choice, so unless there is a good reason to use either setting, consider using a more secure mode such as CBC with a random IV and PKCS#7 padding.