为什么 Node 的加密模块将密钥和 IV 限制为 16 个字节?
Why does Node's crypto module limit keys and IVs to 16 bytes?
所以我一直在编写一个简单的命令行程序,用于使用 Node 的内置 crypto
模块加密和解密文件:
if( deciphered_object.multibase_digest != null ){
ciphered_object.multibase_digest = deciphered_object.multibase_digest;
try{
iv_buffer = Crypto.randomBytes(32);
try{
cipher = Crypto.createCipheriv( 'aes-256-ofb', secret_keyobject, iv_buffer );
if( cipher != null && typeof(cipher) === 'object' ){
encrypted_buffer = cipher.update( deciphered_object.deciphered_data_buffer );
encrypted_buffer += cipher.final();
if( encrypted_buffer != null && Buffer.isBuffer(encrypted_buffer) === true ){
try{
ciphered_object.ciphered_data_buffer = Buffer.alloc( (1 + iv_buffer.length + encrypted_buffer.length) );
buffer_offset = ciphered_object.ciphered_data_buffer.writeUInt8( iv_buffer.length, 0 );
buffer_offset += iv_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
buffer_offset += encrypted_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
if( buffer_offset === ciphered_object.ciphered_data_buffer.length ){
_return = [0, ciphered_object];
} else{
_return = [-256, 'Error: "buffer_offset" is not equal to "ciphered_object.ciphered_data_buffer.length"'];
}
} catch(error){
_return = [-128, Utility.format('Buffer.alloc threw: %s', error)];
}
} else{
_return = [-64, 'Error: "ciphered_buffer" is either null or not a buffer.'];
}
} else{
_return = [-32, 'Error: "cipher" is either null or not an object.'];
}
} catch(error){
_return = [-16, Utility.format('Crypto.createCipheriv threw: %s', error)];
}
} catch(error){
_return = [-8, Utility.format('Crypto.randomBytes threw: %s', error)];
}
} else{
_return = [-4, 'Error: "deciphered_object.multibase_digest" is either null or undefined.'];
}
然而,正如我通过暴力测试发现的那样,createCipheriv 显然只接受恰好 16 个字节的密钥和 IV 长度;针对 IV 的 32 字节和 Scrypt 派生密钥的 4096 字节,我最初计划使用:
password = function_return[1];
function_return = SaltFile.LoadSaltFile();
if( function_return[0] === 0 ){
try{
scrypt_buffer = Crypto.scryptSync( password, function_return[1], 4096 );
secret_keyobject = Crypto.createSecretKey( scrypt_buffer );
if( secret_keyobject != null && typeof(secret_keyobject) === 'object' ){
_return = [0, secret_keyobject];
} else{
_return = [-32, 'Error: "secret_keyobject" is either null or not an object.'];
}
} catch(error){
_return = [-16,Utility.format('Crypto.scryptSync threw: %s', error)];
}
} else{
_return = [function_return[0], 'SaltFile.LoadSaltFile: '+function_return[1]];
}
最糟糕的是,createCipheriv 的文档中均未提及这些大小限制:https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
所以,我真正想知道的是为什么:为什么这些任意限制?为什么有一个加密套件强制使用任何旧计算机都可以在几分钟内暴力破解的可笑的弱密钥?为什么文档中没有提到这些注意事项?
这个 16 字节的密钥限制对我来说似乎很荒谬,它使整个 crypto
模块变得无用。这可以通过使用不同的算法或其他东西来规避吗?这些限制没有任何意义....
编辑:
AES keys are 16, 24 and 32 bytes, which is 128, 192 and 256 bits respectively. The iv is always 16 bytes. That's the AES spec. (@jww)
为什么 RSA 密钥通常为 4096 位(512 字节)而 AES 的上限为 32 字节?为什么 256 的位熵对 AES 足够,但对 RSA 却不够?是什么让 AES 密钥比 RSA 密钥更不容易受到暴力攻击?
If you want a 32-byte iv, then you need to switch to Rijndael. (@jww)
但我还以为 AES 是 Rijndael
A brush-up on basic symmetric cryptography might help you determine why this question doesn't make much sense. (@Luke Joshua Park)
真的,没有帮助;在问这个问题之前,我应该阅读 "basic symmetric cryptography" 上的哪些内容?显然,我不是专家,也没有声称自己是专家,但是,在我阅读的有关对称密码学的内容中,128 位密钥足够安全是没有意义的对于 AES 但不是 RSA。如果你想让我去 RTFM 那很好,但是在问这个问题之前我应该阅读的这本手册在哪里?我希望有人能解释为什么 16 字节的密钥大小是安全的,尽管这没有多少直观意义。
Why have a crypto suite that forces the use of ridiculously weak keys that any old computer could brute-force in a matter of minutes?
你严重低估了 2^128 的大小。如果您每秒可以尝试一万亿个密钥,那么您将花费 10e18 年的时间来尝试所有密钥,无论是给予还是索取。宇宙不到 14e9 岁。你看到问题了。
假设 AES 密钥(或更普遍的对称密码的所有密钥)是完全随机选择的。只要随机源足够不可预测,一把钥匙就和另一把一样好。因此不存在针对密钥的 "clever" 攻击。它要么是对密钥空间的详尽搜索,要么是攻击算法本身(即独立于任何密钥)。
RSA 是一个完全不同的故事,即非对称密码学。密钥不能完全随机选择。这是a series of computations that start out with two numbers that have specific properties的结果。这种计算是可逆的,至少在原则上是这样。在 RSA 的情况下,它是 well-known 因式分解问题(给定一个数字找到它的所有质因数)。所选数字必须足够大,以便在任何合理的时间内无法进行因式分解。
总而言之,AES 和 RSA 的密钥大小没有可比性,因为 AES 和 RSA 是非常不同的加密算法,对各自密钥的攻击类型也大不相同。
所以我一直在编写一个简单的命令行程序,用于使用 Node 的内置 crypto
模块加密和解密文件:
if( deciphered_object.multibase_digest != null ){
ciphered_object.multibase_digest = deciphered_object.multibase_digest;
try{
iv_buffer = Crypto.randomBytes(32);
try{
cipher = Crypto.createCipheriv( 'aes-256-ofb', secret_keyobject, iv_buffer );
if( cipher != null && typeof(cipher) === 'object' ){
encrypted_buffer = cipher.update( deciphered_object.deciphered_data_buffer );
encrypted_buffer += cipher.final();
if( encrypted_buffer != null && Buffer.isBuffer(encrypted_buffer) === true ){
try{
ciphered_object.ciphered_data_buffer = Buffer.alloc( (1 + iv_buffer.length + encrypted_buffer.length) );
buffer_offset = ciphered_object.ciphered_data_buffer.writeUInt8( iv_buffer.length, 0 );
buffer_offset += iv_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
buffer_offset += encrypted_buffer.copy( ciphered_object.ciphered_data_buffer, buffer_offset );
if( buffer_offset === ciphered_object.ciphered_data_buffer.length ){
_return = [0, ciphered_object];
} else{
_return = [-256, 'Error: "buffer_offset" is not equal to "ciphered_object.ciphered_data_buffer.length"'];
}
} catch(error){
_return = [-128, Utility.format('Buffer.alloc threw: %s', error)];
}
} else{
_return = [-64, 'Error: "ciphered_buffer" is either null or not a buffer.'];
}
} else{
_return = [-32, 'Error: "cipher" is either null or not an object.'];
}
} catch(error){
_return = [-16, Utility.format('Crypto.createCipheriv threw: %s', error)];
}
} catch(error){
_return = [-8, Utility.format('Crypto.randomBytes threw: %s', error)];
}
} else{
_return = [-4, 'Error: "deciphered_object.multibase_digest" is either null or undefined.'];
}
然而,正如我通过暴力测试发现的那样,createCipheriv 显然只接受恰好 16 个字节的密钥和 IV 长度;针对 IV 的 32 字节和 Scrypt 派生密钥的 4096 字节,我最初计划使用:
password = function_return[1];
function_return = SaltFile.LoadSaltFile();
if( function_return[0] === 0 ){
try{
scrypt_buffer = Crypto.scryptSync( password, function_return[1], 4096 );
secret_keyobject = Crypto.createSecretKey( scrypt_buffer );
if( secret_keyobject != null && typeof(secret_keyobject) === 'object' ){
_return = [0, secret_keyobject];
} else{
_return = [-32, 'Error: "secret_keyobject" is either null or not an object.'];
}
} catch(error){
_return = [-16,Utility.format('Crypto.scryptSync threw: %s', error)];
}
} else{
_return = [function_return[0], 'SaltFile.LoadSaltFile: '+function_return[1]];
}
最糟糕的是,createCipheriv 的文档中均未提及这些大小限制:https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options
所以,我真正想知道的是为什么:为什么这些任意限制?为什么有一个加密套件强制使用任何旧计算机都可以在几分钟内暴力破解的可笑的弱密钥?为什么文档中没有提到这些注意事项?
这个 16 字节的密钥限制对我来说似乎很荒谬,它使整个 crypto
模块变得无用。这可以通过使用不同的算法或其他东西来规避吗?这些限制没有任何意义....
编辑:
AES keys are 16, 24 and 32 bytes, which is 128, 192 and 256 bits respectively. The iv is always 16 bytes. That's the AES spec. (@jww)
为什么 RSA 密钥通常为 4096 位(512 字节)而 AES 的上限为 32 字节?为什么 256 的位熵对 AES 足够,但对 RSA 却不够?是什么让 AES 密钥比 RSA 密钥更不容易受到暴力攻击?
If you want a 32-byte iv, then you need to switch to Rijndael. (@jww)
但我还以为 AES 是 Rijndael
A brush-up on basic symmetric cryptography might help you determine why this question doesn't make much sense. (@Luke Joshua Park)
真的,没有帮助;在问这个问题之前,我应该阅读 "basic symmetric cryptography" 上的哪些内容?显然,我不是专家,也没有声称自己是专家,但是,在我阅读的有关对称密码学的内容中,128 位密钥足够安全是没有意义的对于 AES 但不是 RSA。如果你想让我去 RTFM 那很好,但是在问这个问题之前我应该阅读的这本手册在哪里?我希望有人能解释为什么 16 字节的密钥大小是安全的,尽管这没有多少直观意义。
Why have a crypto suite that forces the use of ridiculously weak keys that any old computer could brute-force in a matter of minutes?
你严重低估了 2^128 的大小。如果您每秒可以尝试一万亿个密钥,那么您将花费 10e18 年的时间来尝试所有密钥,无论是给予还是索取。宇宙不到 14e9 岁。你看到问题了。
假设 AES 密钥(或更普遍的对称密码的所有密钥)是完全随机选择的。只要随机源足够不可预测,一把钥匙就和另一把一样好。因此不存在针对密钥的 "clever" 攻击。它要么是对密钥空间的详尽搜索,要么是攻击算法本身(即独立于任何密钥)。
RSA 是一个完全不同的故事,即非对称密码学。密钥不能完全随机选择。这是a series of computations that start out with two numbers that have specific properties的结果。这种计算是可逆的,至少在原则上是这样。在 RSA 的情况下,它是 well-known 因式分解问题(给定一个数字找到它的所有质因数)。所选数字必须足够大,以便在任何合理的时间内无法进行因式分解。
总而言之,AES 和 RSA 的密钥大小没有可比性,因为 AES 和 RSA 是非常不同的加密算法,对各自密钥的攻击类型也大不相同。