代码之家  ›  专栏  ›  技术社区  ›  user3211794

PHP和C#兼容Rijndael管理的CBC模式,256位加密/解密

  •  2
  • user3211794  · 技术社区  · 10 年前

    这是我第一次尝试加密,我在将加密从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();
        }
    

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

    PHP和C#中需要加密的密钥和字符串是相同的:

    密钥:“24acd2fcc7b20b8bd33ff45176f03061a09b729487e10d2dd38ab917”和

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

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

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

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

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

    可能是编码有问题吗?或者是填充物?或者还有什么我忽略了的?

    1 回复  |  直到 10 年前
        1
  •  2
  •   i_turo    10 年前

    问题似乎是你的填充,在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