AES-256-CBC解密错误说明填充无效,无法删除

时间:2017-03-31 13:54:51

标签: c# php encryption cryptography

我有一个PHP应用程序,使用openSSL将带有Base64编码的AES-256-CBC加密字符串发送到.NET应用程序。我启动了decrypt方法,但是当我尝试实际解密数据时总是出错。错误指出“填充无效且无法删除”。我在这里做错了什么?

PHP函数生成的加密Base64值为: p07cNwcvcYLxvYHCUsmZqKYr40IXXYjEHr7r + JdgXiJT5 / wpDSDmr48JLOXyNEL7

关键是: M2AZULUALPHA

盐是: TripBuilder2017x

PHP函数是:

function encrypt($text) {
$key = "M2AZULUALPHA";
$block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$padding = $block - (strlen($text) % $block);
$text .= str_repeat(chr($padding), $padding);

$crypttext = openssl_encrypt($text,'aes-256-cbc', $key,  OPENSSL_RAW_DATA, 'TripBuilder2017x');
return base64_encode($crypttext);

}

.NET解密功能:

 private string Decrypt(string cipherText)
        {
            string EncryptionKey = "M2AZULUALPHA";
            byte[] saltArray = Encoding.ASCII.GetBytes("TripBuilder2017x");


            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {
                Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, saltArray);
                encryptor.Mode = CipherMode.CBC;
                encryptor.BlockSize = 128;
                encryptor.KeySize = 256;
                encryptor.Padding = PaddingMode.PKCS7;
                encryptor.Key = pdb.GetBytes(32);
                encryptor.IV = pdb.GetBytes(16);

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }


            }
            return cipherText;
        }

更新我使用在线openSSL解密工具,输入了我的IV,Key和base64文本。它生成了正确的字符串,但在字符串中有一些奇怪的块。

更新2 这是更新的PHP代码。我还将密钥大小增加到16个字节。我仍然收到同样的错误。

$key = "M2AZULUALPHAECHO";
$salt = "TripBuilder2017x";
$mode = "aes-256-cbc";
$text = "BrassMonkey";

function encrypt($text,$key,$salt,$mode) {

    return base64_encode(encryptplain($text,$key,$salt,$mode));
}

function encryptplain($text,$key,$salt,$mode) {


    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
    $padding = $block - (strlen($text) % $block);
    $text .= str_repeat(chr($padding), $padding);

    $crypttext = openssl_encrypt($text, $mode, $key, 1, $salt);
    return ($crypttext);
}

更新3:我的.NET代码

private string Decrypt(string cipherText)
        {
            string EncryptionKey = "M2AZULUALPHAECHO";
            byte[] saltArray = Encoding.ASCII.GetBytes("TripBuilder2017x");

            byte[] cipherBytes = Convert.FromBase64String(cipherText);
            using (Aes encryptor = Aes.Create())
            {

                encryptor.Mode = CipherMode.CBC;
                encryptor.BlockSize = 128;
                encryptor.KeySize = 256;
                encryptor.Padding = PaddingMode.PKCS7;
                encryptor.Key = Encoding.ASCII.GetBytes(EncryptionKey);
                encryptor.IV = saltArray;

                using (MemoryStream ms = new MemoryStream())
                {
                    using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        cs.Write(cipherBytes, 0, cipherBytes.Length);
                        cs.Close();
                    }
                    cipherText = Encoding.Unicode.GetString(ms.ToArray());
                }


            }
            return cipherText;
        }

1 个答案:

答案 0 :(得分:2)

我可以看到几个直接的问题:

  1. 您的C#解密代码正在使用PBKDF2 从密码和salt中导出 AES密钥和IV,而您的PHP加密代码使用密码并将salt 直接作为AES密钥和IV。如果您希望它正常工作,您需要在两个地方使用相同的逻辑。
  2. 在计算块长度和填充输入时,您在PHP代码中指定了错误的加密算法 - 各种版本的Rijndael根据其长度命名,而所有版本AES具有128位块长度(即Rijndael-128)并使用不同的长度。因此,如果要进行适当的填充,则需要指定MCRYPT_RIJNDAEL_128。
  3. 除非您还指定了OPENSSL_ZERO_PADDING标志,否则您不应在加密期间自行执行填充 - 如果您未指定此标志,OpenSSL会将其自己的 PKCS#7填充添加到输入文本中,通过另一个块扩展它,你的解密逻辑将只剥离那部分,忽略你手动添加的填充(并导致字符串中的"奇怪的块"你用你的在线OpenSSL解密工具观察到)
  4. 您需要确保指定的AES密钥大小与您指定的算法实际匹配 - PHP的openssl_encrypt()函数扩展您的密钥大小(使用空字节)以匹配您指定的算法,而C#将自动调整算法以匹配您提供的密钥大小。在这种情况下,您的C#代码实际上正在执行AES-128解密,这与您的PHP代码中的AES-256加密不兼容。
  5. 其他评论:

    1. 如果你想要openssl_encrypt()的Base64编码结果,你可以省略OPENSSL_RAW_DATA标志,它将返回Base64编码的密文。
    2. 问题#1的正确解决方案应该是在两个地方执行密钥派生,或者使用二进制密钥和IV值 - 明文密钥具有更少的熵并且安全性显着降低。 / LI>
    3. 您不应该在代码中对密钥和IV进行硬编码 - 密钥应作为参数提供,IV应随机生成,然后与密文相关联。