LockBox / 节点加密兼容性
LockBox / Node Crypto compatibility
我正在尝试(但失败了)在 Delphi usung LockBox 3 中破译一条使用 Node.js' 加密库加密的消息。
node.js代码:
var crypto = require('crypto');
var cipher = crypto.createCipher('aes-256-ctr', 'my password');
var crypted = cipher.update('hello world', 'utf8', 'base64');
crypted += cipher.final(output_encoding);
console.log(crypted);
结果是
oyC1KRVx3JZBLlI=
Delphi代码:
var
Codec: TCodec;
CipherText: AnsiString;
begin
Codec := TCodec.Create(nil);
try
Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec);
//
Codec.StreamCipherId = 'native.StreamToBlock';
Codec.BlockCipherId = 'native.AES-256';
Codec.ChainModeId = 'native.CTR';
//
Codec.Password := 'my password';
Codec.DecryptAnsiString(CipherText, 'oyC1KRVx3JZBLlI=');
//
Result := string(CipherText);
finally
Codec.Free;
end;
end;
我错过了什么?
有什么问题?
问题是两个库在内部使用不同的密钥和初始化向量 (IV)。
请记住,AES 密码不适用于密码,而是密钥和 IV。
当您向加密库提供密码时,它会使用某种内部机制自动派生密钥和 IV。
这种机制并不明显,但通常在密码库的文档中有所描述。
node.js
中 crypto
模块的文档说它正在使用 OpenSSL 的 EVP_BytesToKey
函数来导出密钥和 IV:
crypto.createCipher(algorithm, password) - Creates and returns a Cipher
object that uses the given algorithm and password.
...
The password is used to derive the cipher key and initialization
vector (IV). The value must be either a 'binary' encoded string or a
[Buffer[].
The implementation of crypto.createCipher() derives keys using the
OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5,
one iteration, and no salt. The lack of salt allows dictionary attacks
as the same password always creates the same key. The low iteration
count and non-cryptographically secure hash algorithm allow passwords
to be tested very rapidly.
引自Node.js v5.6.0 Documentation.
如何解决问题?
正确的解决方案是使用加密安全散列算法从密码中导出密钥,然后手动将密钥和 IV 提供给加密库,以哪个为准。
一个快速而肮脏(并且非常不安全)的解决方案是找到一个等同于 EVP_BytesToKey
的 Delphi 例程,然后使用它来使其工作。
记得还要检查您是否使用了相同的填充方案。 TCodec
应该允许您选择 padPKCS
中的 PaddingScheme
,这应该与 node.js
中的加密模块使用的兼容。如果它不起作用也请尝试其他选项。
另一种选择是在 Delphi 中使用 OpenSSL,它应该已经与 node.js
中使用的兼容。
另外,看到这个问题与你的问题类似:
我正在尝试(但失败了)在 Delphi usung LockBox 3 中破译一条使用 Node.js' 加密库加密的消息。
node.js代码:
var crypto = require('crypto');
var cipher = crypto.createCipher('aes-256-ctr', 'my password');
var crypted = cipher.update('hello world', 'utf8', 'base64');
crypted += cipher.final(output_encoding);
console.log(crypted);
结果是
oyC1KRVx3JZBLlI=
Delphi代码:
var
Codec: TCodec;
CipherText: AnsiString;
begin
Codec := TCodec.Create(nil);
try
Codec.CryptoLibrary := TCryptographicLibrary.Create(Codec);
//
Codec.StreamCipherId = 'native.StreamToBlock';
Codec.BlockCipherId = 'native.AES-256';
Codec.ChainModeId = 'native.CTR';
//
Codec.Password := 'my password';
Codec.DecryptAnsiString(CipherText, 'oyC1KRVx3JZBLlI=');
//
Result := string(CipherText);
finally
Codec.Free;
end;
end;
我错过了什么?
有什么问题?
问题是两个库在内部使用不同的密钥和初始化向量 (IV)。
请记住,AES 密码不适用于密码,而是密钥和 IV。
当您向加密库提供密码时,它会使用某种内部机制自动派生密钥和 IV。
这种机制并不明显,但通常在密码库的文档中有所描述。
node.js
中 crypto
模块的文档说它正在使用 OpenSSL 的 EVP_BytesToKey
函数来导出密钥和 IV:
crypto.createCipher(algorithm, password) - Creates and returns a Cipher object that uses the given algorithm and password.
...
The password is used to derive the cipher key and initialization vector (IV). The value must be either a 'binary' encoded string or a [Buffer[].
The implementation of crypto.createCipher() derives keys using the OpenSSL function EVP_BytesToKey with the digest algorithm set to MD5, one iteration, and no salt. The lack of salt allows dictionary attacks as the same password always creates the same key. The low iteration count and non-cryptographically secure hash algorithm allow passwords to be tested very rapidly.
引自Node.js v5.6.0 Documentation.
如何解决问题?
正确的解决方案是使用加密安全散列算法从密码中导出密钥,然后手动将密钥和 IV 提供给加密库,以哪个为准。
一个快速而肮脏(并且非常不安全)的解决方案是找到一个等同于 EVP_BytesToKey
的 Delphi 例程,然后使用它来使其工作。
记得还要检查您是否使用了相同的填充方案。 TCodec
应该允许您选择 padPKCS
中的 PaddingScheme
,这应该与 node.js
中的加密模块使用的兼容。如果它不起作用也请尝试其他选项。
另一种选择是在 Delphi 中使用 OpenSSL,它应该已经与 node.js
中使用的兼容。
另外,看到这个问题与你的问题类似: