【问题标题】:PHP & C# compatible Rijndael managed CBC mode, 256 bit encryption/decryptionPHP & C# 兼容 Rijndael 托管 CBC 模式,256 位加密/解密
【发布时间】:2014-10-14 20:15:20
【问题描述】:

这是我第一次尝试密码学,我在将加密从 PHP 移植到 C# 时遇到了麻烦。

我已经在互联网上搜索了解决我的问题的有效解决方案,但我尝试过的所有方法都不起作用。我在两种语言之间得到了不同的结果。

在 PHP 中我有以下代码:

function encrypt($Key, $strToEncrypt){
    $md5Key = md5(pack("H*", $Key));
    $md5Iv = md5($Key);

    $block = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);

    $padding = $block - (strlen($strToEncrypt) % $block);
    $strToEncrypt .= str_repeat(chr($padding), $padding);

    $enc = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $md5Key, $strToEncrypt, MCRYPT_MODE_CBC, $md5Iv);
    $enc2 = base64_encode($enc);

    return $enc2;
}

在 C# 中的代码如下:

    public string Encrypt(string strToEncrypt)
    {
        string ret;
        var pKey = PackH(_appkey);
        var md5Key = CalcMd5(pKey);
        var iv = CalcMd5(_appkey);

        var enc =Encoding.UTF8;
        var eIv = enc.GetBytes(iv);
        var eKey = enc.GetBytes(md5Key);

        using (var rij = new RijndaelManaged { BlockSize = 256, KeySize = 256, IV = eIv, Key = eKey, Mode = CipherMode.CBC, Padding = PaddingMode.Zeros})
        using (var memoryStream = new MemoryStream())
        using (var cryptoStream = new CryptoStream(memoryStream, rij.CreateEncryptor(eKey, eIv), CryptoStreamMode.Write))
        {
            using (var sw = new StreamWriter(cryptoStream))
            {
                sw.Write(strToEncrypt);

            }
            ret = Convert.ToBase64String(memoryStream.ToArray());
        }


        return ret;
    }

C# Pack 函数:

protected byte[] PackH(string hex)
    {
        if ((hex.Length % 2) == 1) hex += '0';
        var bytes = new byte[hex.Length / 2];
        for (var i = 0; i < hex.Length; i += 2)
        {
            bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
        }
        return bytes;
    }

还有 C# CalcMd5 函数:

protected string CalcMd5(string textToEnc)
    {
        var sB = new StringBuilder();
        using (var mdHash = MD5.Create())
        {
            var cHash = mdHash.ComputeHash(Encoding.UTF8.GetBytes(textToEnc));

            foreach (byte t in cHash)
            {
                sB.Append(t.ToString("x2"));
            }
        }

        return sB.ToString();
    }

我还有另一个接受 byte[] 的 CalcMd5 函数(它与上面的函数类似,但没有 GetBytes 部分)。

需要加密的密钥和字符串在 PHP 和 C# 中都是一样的:

密钥:“24acd2fcc7b20b8bd33ff45176f03061a09b729487e10d2dd38ab917”和

我要编码的字符串:“110114135AB96637711100”

在 C# 中,函数的结果是:"LHTqpxCJrONmbDdUFHyUZZUVf94z1RmSXWo85/wyEew=" 而在 PHP 中是:"5MkCjfs0vp2HSKdY5XPUAuV68YsrP31Q+ddZsd5p7Sc="。

我尝试过在 C# 中修改填充模式,也尝试过在 stackoverflow 网站上找到的不同方法,但都不起作用。

我已经检查过,传递给 mcrypt 函数和 RijndaelManaged 函数的最终密钥和 Iv 是相同的,并且都是 32 字节大小。

奇怪的是,解密函数运行良好(它正在使用 C# 函数解密 PHP 加密字符串,而围绕 C# 加密字符串的另一场战争是使用 PHP 函数解密)。

会不会是编码的问题?或者也许是填充?或者还有其他我忽略的东西吗?

【问题讨论】:

  • 顺便说一句,当我看到在安全上下文中使用 MD5 时,我总是畏缩不前。它不一定总是一个问题,但作为一个哈希函数,它现在被认为是非常不安全的。我相信我们现在处于 SHA-3(尽管即使在那里也存在一些争议)。
  • 哦,我还注意到您的 IV 似乎是键的功能。它应该是随机的。

标签: c# php encryption cryptography rijndaelmanaged


【解决方案1】:

问题似乎出在您的填充上,在 PHP 端您手动执行 PKCS7-Padding:

$padding = $block - (strlen($strToEncrypt) % $block);
$strToEncrypt .= str_repeat(chr($padding), $padding);

在 C# 端,您正在使用:

Padding = PaddingMode.Zeros

要解决此问题,您可以通过删除上述两行来修改 PHP 代码,因为 mcrypt() 会自动为您执行 ZeroBytePadding。

或者您可以将 C# 中的填充更改为:

Padding = PaddingMode.PKCS7

【讨论】:

  • 我建议两边都使用PKCS7。除非您对明文做出假设(它不以零字节结尾),否则无法删除零填充。良好的加密适用于任何字节序列。
猜你喜欢
  • 1970-01-01
  • 2012-05-06
  • 2011-03-26
  • 2018-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
相关资源
最近更新 更多