Node.js AES-256-CBC 加密问题
Node.js AES-256-CBC encryption issue
我正在尝试根据 PHP 中给出的一个示例代码创建加密字符串,我的主要问题是 Node.js crypto
模块不接受密钥长度超过 32 个字节,但 PHP openssl_encrypt
确实如此,这似乎就是我得到 Invalid key size error
.
的原因
这是我的 js 代码:
let iv = sha1(await HelpersService.makeRandomNumber(null, null, 16));
iv = iv.substr(0, 16);
const text = bundledData;
const password = sha1(this.credentials.appSecret);
let salt = sha1(await HelpersService.makeRandomNumber(null, null, 4));
salt = salt.substr(0, 4);
const key = crypto.createHash('sha256').update(password + salt).digest('hex');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encoded = cipher.update(text, 'utf8', 'hex');
encoded += cipher.final('hex');
这是 PHP 示例:
function generateCashOutAPIHashKey($app_secret ){
//remove plus(+) sign from gsm number.
$data = 'text';
$iv = substr(sha1(mt_rand()), 0, 16);
$password = sha1($app_secret);
$salt = substr(sha1(mt_rand()), 0, 4);
$saltWithPassword = hash('sha256', $password . $salt);
$encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv );
return $encrypted;
}
PHP和NodeJS代码生成的key长度为64字节。使用的加密 AES-256 需要一个 32 字节的密钥。在 PHP 代码中,openssl_encrypt()
隐式地 将 64 字节的密钥缩短为 32 字节。在 NodeJS 代码中,必须显式:
const key = crypto.createHash('sha256').update(password + salt).digest('hex').substr(0, 32);
另外,openssl_encrypt()
returns 默认使用Base64编码的密文。在 NodeJS 代码中,结果以十六进制编码返回。在这里,您必须在 update()
和 final()
调用中将输出编码从 'hex'
更改为 'base64'
:
let encoded = cipher.update(text, 'utf8', 'base64');
encoded += cipher.final('base64');
请注意 PHP 参考代码存在多个漏洞:
mt_rand()
is not a cryptographically secure pseudorandom number generator (CSPRNG). PHP provides cryptographically secure methods for deriving a random IV / salt, e.g. random_bytes()
or random_int()
.
- 密钥是使用 SHA-256 推断的。使用可靠的密钥推导函数更安全,例如 PBKDF2. A 4 bytes salt 一般太小。
- SHA1 在大多数情况下被认为是不安全的,不能再使用。
有人对以下问题有疑问吗?
crypto.createHash('sha256').update(password +
salt).digest('hex').substr(0, 32);
我理解需要由 sha256 生成的 32 字节值的意图,但如果您进行十六进制编码,然后取字符串的前 32 个字符,您将显着降低此值。您现在有一个 32 个字符长的字符串,每个字符只有 16 个可能的值 (0-9a-f)。你真的想要一个 32 字节的值,每个字节有 256 个可能的值。您已将加密密钥的密钥安全性从预期的 256^32 (1.1E77) 更改为 16^32 (3.4E38)。
crypto.createHash('sha256').update('').digest('hex');
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
crypto.createHash('sha256').update('').digest('hex').substr(0,32);
'e3b0c44298fc1c149afbf4c8996fb924'
找到一种使用 SHA256 生成的字节值的方法。
我正在尝试根据 PHP 中给出的一个示例代码创建加密字符串,我的主要问题是 Node.js crypto
模块不接受密钥长度超过 32 个字节,但 PHP openssl_encrypt
确实如此,这似乎就是我得到 Invalid key size error
.
这是我的 js 代码:
let iv = sha1(await HelpersService.makeRandomNumber(null, null, 16));
iv = iv.substr(0, 16);
const text = bundledData;
const password = sha1(this.credentials.appSecret);
let salt = sha1(await HelpersService.makeRandomNumber(null, null, 4));
salt = salt.substr(0, 4);
const key = crypto.createHash('sha256').update(password + salt).digest('hex');
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encoded = cipher.update(text, 'utf8', 'hex');
encoded += cipher.final('hex');
这是 PHP 示例:
function generateCashOutAPIHashKey($app_secret ){
//remove plus(+) sign from gsm number.
$data = 'text';
$iv = substr(sha1(mt_rand()), 0, 16);
$password = sha1($app_secret);
$salt = substr(sha1(mt_rand()), 0, 4);
$saltWithPassword = hash('sha256', $password . $salt);
$encrypted = openssl_encrypt("$data", 'aes-256-cbc', "$saltWithPassword", null, $iv );
return $encrypted;
}
PHP和NodeJS代码生成的key长度为64字节。使用的加密 AES-256 需要一个 32 字节的密钥。在 PHP 代码中,openssl_encrypt()
隐式地 将 64 字节的密钥缩短为 32 字节。在 NodeJS 代码中,必须显式:
const key = crypto.createHash('sha256').update(password + salt).digest('hex').substr(0, 32);
另外,openssl_encrypt()
returns 默认使用Base64编码的密文。在 NodeJS 代码中,结果以十六进制编码返回。在这里,您必须在 update()
和 final()
调用中将输出编码从 'hex'
更改为 'base64'
:
let encoded = cipher.update(text, 'utf8', 'base64');
encoded += cipher.final('base64');
请注意 PHP 参考代码存在多个漏洞:
mt_rand()
is not a cryptographically secure pseudorandom number generator (CSPRNG). PHP provides cryptographically secure methods for deriving a random IV / salt, e.g.random_bytes()
orrandom_int()
.- 密钥是使用 SHA-256 推断的。使用可靠的密钥推导函数更安全,例如 PBKDF2. A 4 bytes salt 一般太小。
- SHA1 在大多数情况下被认为是不安全的,不能再使用。
有人对以下问题有疑问吗?
crypto.createHash('sha256').update(password + salt).digest('hex').substr(0, 32);
我理解需要由 sha256 生成的 32 字节值的意图,但如果您进行十六进制编码,然后取字符串的前 32 个字符,您将显着降低此值。您现在有一个 32 个字符长的字符串,每个字符只有 16 个可能的值 (0-9a-f)。你真的想要一个 32 字节的值,每个字节有 256 个可能的值。您已将加密密钥的密钥安全性从预期的 256^32 (1.1E77) 更改为 16^32 (3.4E38)。
crypto.createHash('sha256').update('').digest('hex');
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
crypto.createHash('sha256').update('').digest('hex').substr(0,32);
'e3b0c44298fc1c149afbf4c8996fb924'
找到一种使用 SHA256 生成的字节值的方法。