【问题标题】:Why does decipherIv.final() fail in node.js when decrypting a value encrypted using PHP?解密使用 PHP 加密的值时,为什么 decipherIv.final() 在 node.js 中失败?
【发布时间】:2019-04-17 00:16:35
【问题描述】:

PHP 版本: 5.6.39

Node.js 版本: 10.9.0

目标:用PHP加密,用Node.js解密

卡点:我目前假设与 OpenSSL 的 PHP 和 Node.js 绑定都使用 PKCS7 填充。 PHP 和 Node.js 是否使用不兼容的 OpenSSL 绑定?

PHP加密/解密代码示例:

class SymmetricEncryption {

   public static function simpleEncrypt($key, $plaintext) {
      $iv = openssl_random_pseudo_bytes(16);
      $ciphertext = openssl_encrypt($plaintext, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv)

      return base64_encode($iv) . "." . base64_encode($ciphertext);
   }

   public static function simpleDecrypt($key, $token) {
       list($iv, $ciphertext) = explode(".", $token);

        return openssl_decrypt(
           base64_decode($ciphertext),
           "aes-256-cbc",
           $key,
           OPENSSL_RAW_DATA,
           base64_decode($iv)
        );
   }
}

示例 Node.js 加密/解密代码:

class SymmetricEncryption {

    static simpleEncrypt(key: Buffer, plaintext: string): string {
        const iv = randomBytes(16)
        const cipher = createCipheriv('aes-256-cbc', key, iv)
        const encrypted = cipher.update(plaintext)
        const token = Buffer.concat([encrypted, cipher.final()])

        return iv.toString('base64') + "." + token.toString('base64')
    }

    static simpleDecrypt(key: Buffer, token: string): string {
        const [iv, ciphertext] = token.split(".").map(piece => Buffer.from(piece, 'base64'))
        const decipher = createDecipheriv('aes-256-cbc', key, iv)
        const output = decipher.update(ciphertext)
        return Buffer.concat([output, decipher.final()]).toString('utf8')
    }
}

我已经成功地独立测试了每个实现,但是在 PHP 中加密和在 node.js 中解密时,我收到以下错误:

Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt

堆栈跟踪将我指向有问题的行,即 simpleDecrypt 方法中的 decipher.final()

我正在使用以下(失败的)单元测试来验证我的实现

it('should be able to decrypt values from php', () => {
    const testAesKey = Buffer.from('9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8', 'hex')
    const phpToken = 'oMhL/oIPAGQdMvphMyWdJw==.bELyRSIwy+nQGIyLj+aN8A=='
    const decrypted = SymmetricEncryption.simpleDecrypt(testAesKey, phpToken)
    expect(decrypted).toBe('hello world')
})

我在这里使用的phpToken 变量是使用以下代码创建的:

$testAesKey = "9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8";

echo SymmetricEncryption::simpleEncrypt($testAesKey, "hello world");

【问题讨论】:

  • 您能否确认您是如何将密钥传递给openssl_encrypt 的?例如。在您的 PHP 测试中,将什么值 $key 传递给 simpleEncrypt
  • @LukeJoshuaPark 我更新了问题以显示我如何在 PHP 中生成令牌。感谢您的观看!
  • 我认为您的问题可能与此有关。请参阅下面的答案。

标签: php node.js encryption openssl cryptography


【解决方案1】:

我怀疑您的问题是由您在 PHP 中传递密钥的方式引起的。 openssl_encrypt 的文档没有指定将密钥解释为十六进制的任何地方。但是,它确实会截断太长的键。

这里可能发生的情况是 PHP 将 64 个字符的十六进制字符串截断为 32 个字节,并将其用作密钥。尝试在您的 PHP 密钥上使用 hex2bin,然后再使用它 - 这应该可以解决您的问题!

$testAesKey = hex2bin("9E9CEB8356ED0212C37B4D8CEA7C04B6239175420203AF7A345527AF9ADB0EB8");

【讨论】:

  • 嘿@LukeJoshuaPark 我昨晚尝试发表评论,但看起来它没有保存。你在现场,我想说声谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-08-27
  • 2011-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
  • 2023-01-30
  • 2018-11-28
相关资源
最近更新 更多