无法弄清楚为什么PHP到C#.NET不会对称加密/解密

时间:2014-12-01 11:19:06

标签: c# php .net encryption cryptography

我已经在这方面工作了一段时间,我对PHP或C#都不太了解。我们正在构建一个使用AES 128 CBC模式加密的应用程序来存储数据库中的内容。一部分是PHP和JS,另一部分是C#.NET WPF。

编写PHP的人使用Mcrypt库进行加密/解密。我使用Chilkat库加密/解密。 Chilkat有一个默认的C#示例,它应该模仿PHP Mcrypt。

目前我可以对.Net上的内容进行对称加密/解密,而.Net可以解密PHP中的任何内容。但是PHP方面无法解密我从.Net端加密到数据库的任何内容。

我已经至少缩小了编码问题的一部分,但我不确定如何修复它。 PHP端的解密方案通常会解密为ASCII,但对于我发送的内容,解密为UTF-8。我试图解密它,然后从UTF-8编码为ASCII无效。

我将向您展示输入/输出和功能。 IV被设置为16个ASCII 0,以帮助我调试,即使它不应该真的重要。

从.Net输入到mcrypt_encrypt func:string" 1220" 输出:3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8 =
参数输入到数据库='与上面相同'

输入PHP端解密func = 3tRIG7qUxUsU7WoXDybRRcdQRobOfeFGtQ438V7XRD8 ='与上述相同'

输出mcrypt_decrypt函数=��J���{$�Z'?�u' iconv表示utf-8编码'

要求其他任何东西,如果有帮助的话,我会得到它。我确定这是一个我无法看到的愚蠢容易的问题。

PHP方面 - 如果重要的话,PHP字符集设置为UTF-8

function encrypt($input)
{

    $this->iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);

/*No longer using random iv :(
    $this->iv = mcrypt_create_iv($this->iv_size, MCRYPT_RAND);*/

    //use 16 zeros 
    $this->iv = '0000000000000000';

    $encrypted = $this->iv .mcrypt_encrypt(MCRYPT_RIJNDAEL_128, KEY, $input, MODE, $this->iv);

    //Finally encode this as base 64, see http://php.net/manual/en/function.base64-encode.php
    $encrypted = base64_encode($encrypted);

    return $encrypted;
}



function decrypt($input)
{
    /*Get our message back!!!
    First decode the base 64 string. Note, de/encoding bas 64 != encryption*/
    $ciphertext_dec = base64_decode($input);

    //Get the iv back out for decryption
    $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);

    //$iv_dec = substr($ciphertext_dec, 0, $iv_size);*/
    $iv_dec = '0000000000000000';

    //Now get the text of encrypted message (all but the iv in front)
    $ciphertext_dec = substr($ciphertext_dec, $iv_size);        

    //Now decrypt the message
$plaintext_dec = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, KEY, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);

//Test
//test
    //$plaintext_dec = iconv("UTF-8", "ASCII", $plaintext_dec); 
//echo mb_detect_encoding($plaintext_dec, "auto");
//echo $plaintext_dec;

    /*However, we might now have blank space @ end of output b/c
    remember we de/encrypt via block, so a 10 char long message
    could be padded to 16 char long with blank spaces. Get rid of those.*/
    $plaintext_dec = trim($plaintext_dec);

    //return so we can compare to, i.e., original input
return $plaintext_dec;
}

.NET C#

    public string mcrypt_encrypt(string plainText)
    {
        plainText = Encoding.ASCII.GetString(crypt.IV) + plainText;
        byte[] myText = Encoding.ASCII.GetBytes(plainText);

        //  Do 128-bit AES encryption:
        byte[] cipherText = crypt.EncryptBytes(myText);

        return Convert.ToBase64String(cipherText);
    }

    public string mcrypt_decrypt(string cipher_text)
    {

        byte[] cipher_dec = Convert.FromBase64String(cipher_text);

        byte[] plainBytes = crypt.DecryptBytes(cipher_dec);

        string decrypted = Encoding.ASCII.GetString(plainBytes);

        string plain_text = decrypted.Substring(16, decrypted.Length - 16);

        return plain_text.TrimEnd('\0');
    }

C#Chilkat init:          // AES也称为Rijndael。

          crypt.CryptAlgorithm = "aes";

        //  CipherMode may be "ecb" or "cbc"
        crypt.CipherMode = "cbc";

        //  KeyLength may be 128, 192, 256
        crypt.KeyLength = 128;

        //  Pad with NULL bytes (PHP pads with NULL bytes)
        crypt.PaddingScheme = 3;

        //  EncodingMode specifies the encoding of the output for
        //  encryption, and the input for decryption.
        //  It may be "hex", "url", "base64", or "quoted-printable".
        crypt.EncodingMode = "hex";

        //  The secret key must equal the size of the key.  For
        //  256-bit encryption, the binary secret key is 32 bytes.
        //  For 128-bit encryption, the binary secret key is 16 bytes.
        string keyAscii = @"&=*FS6wksG@Zs3qG";
        crypt.SecretKey = Encoding.UTF8.GetBytes(keyAscii);
        crypt.Charset = "ASCII";

        crypt.SetEncodedIV("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", "ascii");

2 个答案:

答案 0 :(得分:2)

这里有各种不同的问题。导致您在.NET中加密的PHP数据中无法解密的问题是,在PHP版本中,您在密文之前执行了substr解密。代码中的注释表明您正在删除IV,除非您每次(正确地)使用随机IV时,似乎只与代码的先前版本相关 - 现在您只是丢弃了密文的前16个字节,由于操作模式而破坏后续数据块。

另一个问题(虽然在.NET中解密时丢弃了明文数据的前16个字节这一事实掩盖了)是你在.NET中使用的IV({{1}的16个字节})与您在PHP中使用的IV不同(16'0'字符= 0x00的16个字节)。

我建议恢复为每次加密使用随机IV并在加密之后将IV添加到密文当解密时,从密文的第一个字节读取IV然后解密剩余部分。这比静态IV安全得多,特别是当加密的数据可能经常相同时。

答案 1 :(得分:1)

看起来就像是一个简单的字符编码问题。您的C#代码正在获取字符串的ASCII表示并加密,但您的PHP代码正在解密它并期望它是UTF-8。

尝试更换Encoding.ASCII来自Encoding.UTF8的{​​{1}}来电,并确保您的crypt.Charsetcrypt.SetEncodedIV也是UTF8