我已经在这方面工作了一段时间,我对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");
答案 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.Charset
和crypt.SetEncodedIV
也是UTF8