nodejs 中的 AES-256-ECB 加密与 php MCrypt 模块不匹配
AES-256-ECB encryption in nodejs does not match php MCrypt module
我正在接收来自外部方的加密消息。他们使用 php 和 mcrypt
通过以下配置进行加密
- 算法:rijndael-256
- 模式:欧洲央行
- 示例加密消息:
"a364cb3bb55c2788b25f04ef6867d770771a7d0fdd44462b40233ea76a8bd00d"
。它的原始消息是"thank you"
下图显示配置
在我这边,我正在使用 nodejs 来解密。我有一个 node-mcrypt binding 的工作版本。但我想避免使用原生模块,所以我正在寻找一个纯粹的 javascript 解决方案。
我试过节点的 crypto
但它的 aes-256-ecb
产生了不同的(不可读的)输出
在 nodejs 中使用 MCrypt 绑定的工作代码:
const decryptAES256 = (encrypted: string, password: string): string => {
try {
const desEcb = new MCrypt('rijndael-256', 'ecb');
desEcb.open(md5(password));
const ciphertext = desEcb.decrypt(new Buffer(encrypted, 'hex'));
const plain = ciphertext.toString();
const isUTF8 = plain.split('').every(c => c.charCodeAt(0) <= 256);
if (!isUTF8) {
throw new Error('Invalid Input');
}
const unpadding = plain
.split('')
.filter(c => c.charCodeAt(0))
.join('');
return unpadding;
} catch (ex) {
console.error('Invalid token');
}
};
当您声明 "rijndael-256" 时,可能指定了一个非 AES 的 256 位块大小。 AES 有一种块大小:128 位。
这似乎是由 32 二进制字节的加密输出产生的,对于 "thank you" 的输入,输出应该是一个块,所以看起来块大小是 32 字节(256-位)。
- 不要使用 mcrypt,它只会导致互操作性问题。
- 确保您使用的 AES 可以是 128 位块大小的 rijndael。
- 不要混淆密钥大小和块大小。
- 如果输入数据不总是块大小的倍数,则必须将填充添加到输入数据],指定填充,PKCS#7(née PKCS#5)填充可能是默认值。 mcrypt 不支持标准填充。如果必须与 mcrypt 互操作,则需要指定无填充并自行执行填充。
在意识到 rijndael 256 不等同于 aes 256(128 版本是)之后,我选择在纯 javascript 中找到一个 rijndael 特定的库,这是一个工作代码:
const Rijndael = require('rijndael-js');
describe('cipher', () => {
it('should work', () => {
const text = 'thank you';
// + String.fromCharCode(0).repeat(5)
const key = '94a08da1fecbb6e8b46990538c7b50b2';
const cipher = new Rijndael(key, 'ecb');
const ciphertext = cipher.encrypt(text, 256);
expect(ciphertext.toString('hex')).toBe(
'a364cb3bb55c2788b25f04ef6867d770771a7d0fdd44462b40233ea76a8bd00d'
);
const plaintext = cipher.decrypt(ciphertext, 256);
const unpadded = plaintext.toString().replace(/\u0000*$/g, '');
expect(unpadded).toBe(text);
});
});
我正在接收来自外部方的加密消息。他们使用 php 和 mcrypt
通过以下配置进行加密
- 算法:rijndael-256
- 模式:欧洲央行
- 示例加密消息:
"a364cb3bb55c2788b25f04ef6867d770771a7d0fdd44462b40233ea76a8bd00d"
。它的原始消息是"thank you"
下图显示配置
在我这边,我正在使用 nodejs 来解密。我有一个 node-mcrypt binding 的工作版本。但我想避免使用原生模块,所以我正在寻找一个纯粹的 javascript 解决方案。
我试过节点的 crypto
但它的 aes-256-ecb
产生了不同的(不可读的)输出
在 nodejs 中使用 MCrypt 绑定的工作代码:
const decryptAES256 = (encrypted: string, password: string): string => {
try {
const desEcb = new MCrypt('rijndael-256', 'ecb');
desEcb.open(md5(password));
const ciphertext = desEcb.decrypt(new Buffer(encrypted, 'hex'));
const plain = ciphertext.toString();
const isUTF8 = plain.split('').every(c => c.charCodeAt(0) <= 256);
if (!isUTF8) {
throw new Error('Invalid Input');
}
const unpadding = plain
.split('')
.filter(c => c.charCodeAt(0))
.join('');
return unpadding;
} catch (ex) {
console.error('Invalid token');
}
};
当您声明 "rijndael-256" 时,可能指定了一个非 AES 的 256 位块大小。 AES 有一种块大小:128 位。
这似乎是由 32 二进制字节的加密输出产生的,对于 "thank you" 的输入,输出应该是一个块,所以看起来块大小是 32 字节(256-位)。
- 不要使用 mcrypt,它只会导致互操作性问题。
- 确保您使用的 AES 可以是 128 位块大小的 rijndael。
- 不要混淆密钥大小和块大小。
- 如果输入数据不总是块大小的倍数,则必须将填充添加到输入数据],指定填充,PKCS#7(née PKCS#5)填充可能是默认值。 mcrypt 不支持标准填充。如果必须与 mcrypt 互操作,则需要指定无填充并自行执行填充。
在意识到 rijndael 256 不等同于 aes 256(128 版本是)之后,我选择在纯 javascript 中找到一个 rijndael 特定的库,这是一个工作代码:
const Rijndael = require('rijndael-js');
describe('cipher', () => {
it('should work', () => {
const text = 'thank you';
// + String.fromCharCode(0).repeat(5)
const key = '94a08da1fecbb6e8b46990538c7b50b2';
const cipher = new Rijndael(key, 'ecb');
const ciphertext = cipher.encrypt(text, 256);
expect(ciphertext.toString('hex')).toBe(
'a364cb3bb55c2788b25f04ef6867d770771a7d0fdd44462b40233ea76a8bd00d'
);
const plaintext = cipher.decrypt(ciphertext, 256);
const unpadded = plaintext.toString().replace(/\u0000*$/g, '');
expect(unpadded).toBe(text);
});
});