无法使用来自 AES-256-CBC 的 pgcrypto 解密,但 AES-128-CBC 可以

Posted

技术标签:

【中文标题】无法使用来自 AES-256-CBC 的 pgcrypto 解密,但 AES-128-CBC 可以【英文标题】:Can't decrypt using pgcrypto from AES-256-CBC but AES-128-CBC is OK 【发布时间】:2017-09-18 22:26:20 【问题描述】:

我在解密 pgcrypto 中先前在 php 应用程序中加密的数据时遇到问题。

我尝试了 3 种加密方式: 1) mcrypt - RIJNDAEL 128 CBC 2) mcrypt - RIJNDAEL 256 CBC 3) openssl_encrypt - aes-256-cbc

一切都在 PHP 中加密解密很好,但在 pgcrypto 我可以使用相同的密钥解密,并且仅 iv 1) mcrypt - RIJNDAEL 128 CBC

这是 PHP 部分的示例代码:

<?php
function d ($data, $key, $mode) 
    $data = @base64_decode($data);
    $pad = $mode == MCRYPT_RIJNDAEL_256 ? 32 : 16;
    $iv = mb_substr($data, 0, $pad, "8bit");
    $data = mb_substr($data, $pad, mb_strlen($data, "8bit"), "8bit");

    if ($data === null || $data === "") 
        return $data;
    

    if ($mode == MCRYPT_RIJNDAEL_128 OR $mode == MCRYPT_RIJNDAEL_256) 
        $data = mcrypt_decrypt($mode, $key, $data, MCRYPT_MODE_CBC, $iv);
     else 
        $data = openssl_decrypt($data, "aes-256-cbc", $key, 0, $iv);
    

    if ($data === false) 
        throw new Exception("Unable to decrypt data");
    

    $padding = ord($data[mb_strlen($data, "8bit") - 1]);
    $data = mb_substr($data, 0, mb_strlen($data, "8bit") - $padding, "8bit");

    return $data;

function e ($data, $key, $mode) 
    $pad = $mode == MCRYPT_RIJNDAEL_256 ? 32 : 16;
    $iv = openssl_random_pseudo_bytes($pad);

    $padding = 16 - (strlen($data) % $pad);
    $data .= str_repeat(chr($padding), $padding);

    if ($mode == MCRYPT_RIJNDAEL_128 OR $mode == MCRYPT_RIJNDAEL_256) 
        $data = mcrypt_encrypt($mode, $key, $data, MCRYPT_MODE_CBC, $iv);
     else 
        $data = openssl_encrypt($data, "aes-256-cbc", $key, 0, $iv );
    

    if ($data === false) 
        throw new Exception("Unable to encrypt data");
    

    return base64_encode($iv . $data);


$mode1 = MCRYPT_RIJNDAEL_128;
$key1 = "67pma7BQL01cqb6Nlil2T1436lLXv8Ln";

$key2 = "85f2669023b98a62d1312af75994ddf1";
$mode2 = MCRYPT_RIJNDAEL_256;

$key3 = "85f2669023b98a62d1312af75994ddf1";
$mode3 = "aes-256-cbc";

$data = "test";

$e1 = e($data, $key1, $mode1);
$e2 = e($data, $key2, $mode2);
$e3 = e($data, $key3, $mode3);

$d1 = d($e1, $key1, $mode1); //
$d2 = d($e2, $key2, $mode2); //
$d3 = d($e3, $key3, $mode3); //

//for ($i=1; $i < 4; $i++) 
//   $"e" . $i = e($data, $"key" . $i, $"mode" . $i);
//
//    $"d" . $i = d($"e" . $i, $"key" . $i, $"mode" . $i);
//

用于编码的结果和数据:

1) mcrypt - RIJNDAEL 128 CBC

key = "67pma7BQL01cqb6Nlil2T1436lLXv8Ln" 初始化向量 base64 = "q5gXIfW6maT4zx4tgJQImg==" 加密字符串 base64 = "q5gXIfW6maT4zx4tgJQImtwJgEVK66mTcRPdilkEiHY=" 解密字符串 base64 = "dGVzdA=="

2) mcrypt - RIJNDAEL 256 CBC

key = "85f2669023b98a62d1312af75994ddf1" 初始化向量 base64 = "2EmtyH++cQA5X5mmtY+vpl5FkVwELS9ExrYnFjGGco0=" 加密字符串 base64 = "2EmtyH++cQA5X5mmtY+vpl5FkVwELS9ExrYnFjGGco3B29CC5DpfWs1YAfh8WuY9f0/6OPC1B4sidSV5TojJ1g==" 解密字符串 base64 = "dGVzdAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAA="

3) openssl_encrypt - aes-256-cbc

key = "85f2669023b98a62d1312af75994ddf1" 初始化向量 base64 = "tOi+xXZf6MyPDpQzPZAI6Q==" 加密字符串 base64 = "tOi+xXZf6MyPDpQzPZAI6XJQYmwyNUVzKzdaVnNickc5dEg5MUd1anpBYlpLeW9SQjhpZ29yQzRpWFk9" 解密字符串 base64= "dGVzdA=="

这里我尝试使用相同的密钥和 IV 在 Postgres 中解密这些数据。

SELECT
  -- mcrypt aes 128
  decrypt_iv(
      decode('q5gXIfW6maT4zx4tgJQImtwJgEVK66mTcRPdilkEiHY=', 'base64'),
      '67pma7BQL01cqb6Nlil2T1436lLXv8Ln',
      decode('q5gXIfW6maT4zx4tgJQImg==', 'base64'),
      'aes-cbc'
  ),

  -- mcrypt aes 256
  decrypt_iv(
      decode('2EmtyH++cQA5X5mmtY+vpl5FkVwELS9ExrYnFjGGco3B29CC5DpfWs1YAfh8WuY9f0/6OPC1B4sidSV5TojJ1g==', 'base64'),
      '85f2669023b98a62d1312af75994ddf1',
      decode('2EmtyH++cQA5X5mmtY+vpl5FkVwELS9ExrYnFjGGco0=', 'base64'),
      'aes-cbc'
  ),
--     -- openssl aes 256
-- decrypt_iv(
--   decode('tOi+xXZf6MyPDpQzPZAI6XJQYmwyNUVzKzdaVnNickc5dEg5MUd1anpBYlpLeW9SQjhpZ29yQzRpWFk9', 'base64'),
--   '85f2669023b98a62d1312af75994ddf1',
--   decode('tOi+xXZf6MyPDpQzPZAI6Q==', 'base64'),
--   'aes-cbc'
-- ),
    -- pgcrypto same values as mcrypt aes 128 encrypt then decrypt
  decrypt_iv(
    encrypt_iv(
        'test',
        '67pma7BQL01cqb6Nlil2T1436lLXv8Ln',
        decode('q5gXIfW6maT4zx4tgJQImg==', 'base64'),
        'aes-cbc'),
    '67pma7BQL01cqb6Nlil2T1436lLXv8Ln',
    decode('q5gXIfW6maT4zx4tgJQImg==', 'base64'),
    'aes-cbc'
    )

如您所见,所有 3 个在 PHP 中都已解密。 在 Postgres 中仅第一个 (mcrypt aes128cbc) 解密 OK - 前 16 个字节仍然是 IV,但我可以删除它们并转换为文本。 另外两个(mcrypte AES256CBC 和 openssl256cbc)看起来甚至不像被解密了。 我用 openssl256cbc 注释了块,因为它给了我“[39000] 错误:decrypt_iv 错误:数据不是块大小的倍数”错误。

任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

MCRYPT_RIJNDAEL_256 不是 AES-256。这是块大小为 256 的 Rijndael 密码(因此出现错误)。 AES 是 Rijndael 密码的子集,使用 128 位的块大小和 128、192 和 256 位的 密钥 大小。这也反映在 IV 大小上。

要创建 AES-256 加密密文,您可以使用 MCRYPT_RIJNDAEL_128 和正确的密钥大小(256 位为 32 字节)。 _128 后缀表示要使用的块大小;您仍然可以使用 128、192 或 256 位的任何有效密钥大小。


请注意,mcrypt - 尤其是底层 C 库 - 不再维护。最好使用 openssl 或更高版本的加密库。

mcrypt 和 OpenSSL 包装器也很乐意允许无效的密钥大小,只警告您 - 如果您幸运的话。这当然与任何定义良好的 AES 库都不兼容。

【讨论】:

以上是关于无法使用来自 AES-256-CBC 的 pgcrypto 解密,但 AES-128-CBC 可以的主要内容,如果未能解决你的问题,请参考以下文章

ubuntu16.04SSH无法连接

使用 Java 解密 AES256 CBC

密码“aes256-cbc”是必需的,但它不可用

text 唯一支持的密码是AES-128-CBC和AES-256-CBC,密钥长度正确。

使用OpenSSL进行AES-256 / CBC加密并使用C#进行解密

QT:AES-256-CBC 根据 PHP 代码在 C++ 中加密/解密