【问题标题】:AES-256-ECB encryption in nodejs does not match php MCrypt modulenodejs中的AES-256-ECB加密与php MCrypt模块不匹配
【发布时间】:2018-02-23 19:10:55
【问题描述】:

我收到来自外部方的加密消息。他们使用 php 和 mcrypt 进行加密,配置如下

  • 算法:rijndael-256
  • 模式:ecb
  • 示例加密消息:"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');
  }
};

【问题讨论】:

  • 理想情况下,不要使用 mcrypt,任何版本或绑定到它。但是你的问题在细节和代​​码上仍然很短,看看How to Ask
  • 当您声明“rijndael-256”时,可能会指定不是 AES 的 256 位块大小。 AES 有一个块大小:128 位,但是代码在哪里
  • 不要在新作品中使用 ECB 模式并尽快更新旧作品,它不安全,请参阅ECB mode,向下滚动到企鹅。而是使用带有随机IV的CBC模式,只需在加密数据前加上IV用于解密即可,它不需要保密。
  • 最好不要使用 PHP mcrypt,它是废弃软件,多年未更新,不支持标准 PKCS#7(née PKCS#5)填充,仅支持非标准空填充甚至不能用于二进制数据。 mcrypt 有许多出色的 bugs 可以追溯到 2003 年。不推荐使用的 mcrypt 扩展将在 PHP 7.2 中删除。而是考虑使用defuseRNCryptor,它们提供了一个完整的解决方案并且正在维护并且是正确的。

标签: php node.js encryption mcrypt


【解决方案1】:

当您声明“rijndael-256”时,可能会指定不是 AES 的 256 位块大小。 AES 有一个块大小:128 位。

这似乎是由 32 二进制字节的加密输出产生的,对于“谢谢”的输入,输出应该是一个块,因此块大小似乎是 32 字节(256 位)。

  1. 不要使用 mcrypt,它只会导致互操作性问题。
  2. 确保您使用的是 AES,它可以是 128 位块大小的 rijndael。
  3. 不要将密钥大小与块大小混淆。
  4. 如果输入数据不总是块大小的倍数,则必须将填充添加到输入数据],指定填充,PKCS#7(née PKCS#5)填充,这可能是默认值。 mcrypt 不支持标准填充。如果您必须与 mcrypt 互操作,则需要不指定填充并自己执行填充。

【讨论】:

  • 感谢您澄清 rijndael 和 aes 之间的区别。如果没有这个,我仍然会尝试加密/openssl 的aes-256-ecb
【解决方案2】:

在意识到 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);
  });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2018-07-08
    • 1970-01-01
    • 2014-02-06
    • 2012-01-28
    • 2017-08-08
    • 2014-11-30
    相关资源
    最近更新 更多