问题描述
我正在尝试在 C# 中加密一些(cookie)数据,然后在 PHP 中对其进行解密.我选择使用 Rijndael 加密.我几乎让它工作了,除了只有部分文本被解密!我从这个例子开始工作:在 C# 中解密 PHP 加密字符串
I'm trying to encrypt some (cookie) data in C# and then decrypt it in PHP. I have chosen to use Rijndael encryption. I've almost got it working, except only part of the text is decrypted! I started working from this example: Decrypt PHP encrypted string in C#
这是我正在加密的文本 (JSON)(已删除敏感信息):
Here's the text (JSON) that I am encrypting (sensitive information removed):
{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"}
所以我登录到 C# 应用程序,它从存储的密钥和 IV 创建/编码 cookie,然后重定向到应该解密/读取 cookie 的 PHP 应用程序.当我解密cookie时,它是这样的:<罢工>
So I login to the C# app which creates/encodes the cookie from stored Key and IV and then redirects to the PHP app which is supposed to decrypt/read the cookie. When I decrypt the cookie, it comes out like this:
{"DisplayName":"xxx","F?A ;??HP=D???????4??z????????k?#E???R?j?5??t. t?D??"
更新:我已经更进一步了,这是现在的结果
UPDATE: i've gotten a little bit further and this is now the result
string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"?)???-?J??k/VV-v? ?9?B`7^"
如您所见,它开始解密它,但随后变得一团糟......罢工>
As you can see, it starts decrypting it, but then gets messed up...
当解密字符串时,它是正确的(带填充,我有一个删除填充的功能),但是如果我将测试字符串更改一个字符,我又得到了垃圾:
When Decrypt the string it comes out correct (with padding, which I have a function to remove padding), but if I change the test string by one character I get garbage again:
B?nHL?Ek ????UΣlO????O??M??NO/?f.M???L??CC?Y>F??~?qd?+
这是我用来生成随机密钥和 IV 的 c# 代码:
更新:我现在只使用静态密钥/IV,它们是:
UPDATE: I'm just using static key/IV for now, here they are:
Key: lkirwf897+22#bbtrm8814z5qq=498j5 IV: 741952hheeyy66#cs!9hjv887mxx7@8y
<打击>
RijndaelManaged symmetricKey = new RijndaelManaged(); symmetricKey.BlockSize = 256; symmetricKey.KeySize = 256; symmetricKey.Padding = PaddingMode.Zeros; symmetricKey.Mode = CipherMode.CBC; string key = Convert.ToBase64String(symmetricKey.Key); string IV = Convert.ToBase64String(symmetricKey.IV);
然后我将密钥和 IV 保存到数据库中,以便稍后检索以进行编码/解码.
I then save the key and IV to a database to be retrieved later for encoding/decoding.
这是完整的加密类:
public static class Encryption { public static string Encrypt(string prm_text_to_encrypt, string prm_key, string prm_iv) { var sToEncrypt = prm_text_to_encrypt; var rj = new RijndaelManaged() { Padding = PaddingMode.PKCS7, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256, //FeedbackSize = 256 }; var key = Encoding.ASCII.GetBytes(prm_key); var IV = Encoding.ASCII.GetBytes(prm_iv); //var key = Convert.FromBase64String(prm_key); //var IV = Convert.FromBase64String(prm_iv); var encryptor = rj.CreateEncryptor(key, IV); var msEncrypt = new MemoryStream(); var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); var toEncrypt = Encoding.ASCII.GetBytes(sToEncrypt); csEncrypt.Write(toEncrypt, 0, toEncrypt.Length); csEncrypt.FlushFinalBlock(); var encrypted = msEncrypt.ToArray(); return (Convert.ToBase64String(encrypted)); } public static string Decrypt(string prm_text_to_decrypt, string prm_key, string prm_iv) { var sEncryptedString = prm_text_to_decrypt; var rj = new RijndaelManaged() { Padding = PaddingMode.PKCS7, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256, //FeedbackSize = 256 }; var key = Encoding.ASCII.GetBytes(prm_key); var IV = Encoding.ASCII.GetBytes(prm_iv); //var key = Convert.FromBase64String(prm_key); //var IV = Convert.FromBase64String(prm_iv); var decryptor = rj.CreateDecryptor(key, IV); var sEncrypted = Convert.FromBase64String(sEncryptedString); var fromEncrypt = new byte[sEncrypted.Length]; var msDecrypt = new MemoryStream(sEncrypted); var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); csDecrypt.Read(fromEncrypt, 0, fromEncrypt.Length); return (Encoding.ASCII.GetString(fromEncrypt)); } public static void GenerateKeyIV(out string key, out string IV) { var rj = new RijndaelManaged() { Padding = PaddingMode.PKCS7, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256, //FeedbackSize = 256 }; rj.GenerateKey(); rj.GenerateIV(); key = Convert.ToBase64String(rj.Key); IV = Convert.ToBase64String(rj.IV); } }
这是我用来解密数据的 PHP 代码:
Here's the PHP code I am using to decrypt the data:
function decryptRJ256($key,$iv,$string_to_decrypt) { $string_to_decrypt = base64_decode($string_to_decrypt); $rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $string_to_decrypt, MCRYPT_MODE_CBC, $iv); //$rtn = rtrim($rtn, "4"); $rtn = unpad($rtn); return($rtn); } function unpad($value) { $blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC); //apply pkcs7 padding removal $packing = ord($value[strlen($value) - 1]); if($packing && $packing < $blockSize){ for($P = strlen($value) - 1; $P >= strlen($value) - $packing; $P--){ if(ord($value{$P}) != $packing){ $packing = 0; }//end if }//end for }//end if return substr($value, 0, strlen($value) - $packing); } $ky = 'lkirwf897+22#bbtrm8814z5qq=498j5'; // 32 * 8 = 256 bit key $iv = '741952hheeyy66#cs!9hjv887mxx7@8y'; // 32 * 8 = 256 bit iv $enc = $_COOKIE["MyCookie"]; $dtext = decryptRJ256($ky, $iv, $enc); var_dump($dtext);
我对这部分有点不确定,因为我看到的所有示例代码都只是将 base64 编码的字符串直接传递给解密器,但在我的示例中,我必须在传递之前对其进行 base64_decode否则我会得到密钥和 IV 长度不正确的错误.
更新:我正在使用 PHP 所需格式的 ASCII 键.如果我从 RijndaelManaged 类生成密钥,它们在 PHP 端不起作用,但我可以使用已知在 PHP 端工作的密钥,并在 RijndaelManaged C# 端使用它们.
UPDATE: I'm using ASCII keys in the format needed by PHP. If I generate keys from the RijndaelManaged class they dont work on the PHP side, but I can use keys that are known to work on PHP side and use them in the RijndaelManaged C# side.
如果我遗漏了任何相关信息,请告诉我.TIA!
Please let me know if I left out any pertinent information. TIA!
推荐答案
由于字符串部分正常,但最后有乱码,这表明加密中存在填充问题,该加密需要精确的 256 字节块.我建议在 C# 端将填充设置为 PKCS7 (PaddingMode.PKCS7) 而不是 Zeros,PHP 可以毫无问题地理解(因为它是该解析器的默认模式).
Since the string is partially OK, but there is gibberish at the end it would suggest a padding problem within the encryption which expects exact blocks of 256 bytes. I suggest setting the padding as PKCS7 (PaddingMode.PKCS7) instead of Zeros on the C# side which PHP will understand without issues (as it's the default mode on that parser).
糟糕,我没有注意到您的 PHP 中有以下内容:
Oops, I did not notice that you had the following in your PHP:
$enc = $_COOKIE["MyCookie"];
这是警告.PHP 可能无法按原样获取加密数据,并且正在运行一些 urldecode 清理.您应该打印此变量以查看它是否真正匹配从 C# 代码发送的内容.
This is the caveat. PHP is likely not getting the encrypted data as-is and is running some urldecode sanitizing. You should print this variable to see that it really matches what is being sent from the C# code.
编辑 2:
通过添加以下内容将空格转换为 cookie 中缺失的 + 字符:
Convert the whitespaces to missing + characters from the cookie by adding this:
str_replace(' ', '+', $enc);