sol*_*dau 18 php c# encryption cookies encryption-symmetric
我正在尝试在C#中加密一些(cookie)数据,然后在PHP中解密它.我选择使用Rijndael加密.我几乎让它工作,除了只有部分文本被解密!我从这个例子开始工作:用C#解密PHP加密字符串
这是我正在加密的文本(JSON)(删除了敏感信息):
{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"}
Run Code Online (Sandbox Code Playgroud)
所以我登录到C#app,它从存储的Key和IV创建/编码cookie,然后重定向到应该解密/读取cookie的PHP应用程序.当我解密cookie时,它出现如下:
{"DisplayName":"xxx","F?A ;??HP=D???????4??z????????k?#E???R?j?5?\?t. t?D??"
Run Code Online (Sandbox Code Playgroud)
更新:我已经进一步了,这就是结果
string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"?)???-?J??k/VV-v? ?9?B`7^"
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它开始解密它,但后来搞砸了......
当解密字符串时它出来是正确的(使用填充,我有一个删除填充的函数),但如果我将测试字符串改为一个字符,我会再次得到垃圾:
B?nHL?Ek ?¿??U?lO????O??M??NO/?f.M???L??CC?Y>F??~?qd?+
Run Code Online (Sandbox Code Playgroud)
这是我用来生成随机密钥和IV的c#代码:
更新:我现在只使用静态键/ IV,它们是:
Key: lkirwf897+22#bbtrm8814z5qq=498j5
IV: 741952hheeyy66#cs!9hjv887mxx7@8y
Run Code Online (Sandbox Code Playgroud)
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);
Run Code Online (Sandbox Code Playgroud)
然后,我将密钥和IV保存到数据库中,以便稍后检索以进行编码/解码.
这是完整的加密类:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
这是我用来解密数据的PHP代码:
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, "\0\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);
Run Code Online (Sandbox Code Playgroud)
我对这一部分有点不确定,因为我看到的所有示例代码都只是将base64编码的字符串直接传递给解密器,但在我的示例中,我必须在传递它之前对其进行base64_decode,否则我会收到错误键和IV的长度不正确.
更新:我正在使用PHP所需格式的ASCII密钥.如果我从RijndaelManaged类生成键,他们不在PHP方面工作,但我可以使用已知在PHP端工作的键并在RijndaelManaged C#端使用它们.
如果我遗漏任何相关信息,请告诉我.TIA!
sol*_*dau 19
对于后人,我在这里放置完整的解决方案.
C#加密类
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,
};
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,
};
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,
};
rj.GenerateKey();
rj.GenerateIV();
key = Convert.ToBase64String(rj.Key);
IV = Convert.ToBase64String(rj.IV);
}
}
Run Code Online (Sandbox Code Playgroud)
PHP解密片段
<?php
function decryptRJ256($key,$iv,$encrypted)
{
//PHP strips "+" and replaces with " ", but we need "+" so add it back in...
$encrypted = str_replace(' ', '+', $encrypted);
//get all the bits
$key = base64_decode($key);
$iv = base64_decode($iv);
$encrypted = base64_decode($encrypted);
$rtn = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
$rtn = unpad($rtn);
return($rtn);
}
//removes PKCS7 padding
function unpad($value)
{
$blockSize = mcrypt_get_block_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$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;
}
}
}
return substr($value, 0, strlen($value) - $packing);
}
?>
<pre>
<?php
$enc = $_COOKIE["MyCookie"];
$ky = ""; //INSERT THE KEY GENERATED BY THE C# CLASS HERE
$iv = ""; //INSERT THE IV GENERATED BY THE C# CLASS HERE
$json_user = json_decode(decryptRJ256($ky, $iv, $enc), true);
var_dump($json_user);
?>
Run Code Online (Sandbox Code Playgroud)
由于字符串部分正常,但最后会有乱码,这表明加密中的填充问题需要256字节的精确块.我建议在C#端将填充设置为PKCS7(PaddingMode.PKCS7)而不是Zeros,PHP将理解没有问题(因为它是该解析器的默认模式).
编辑:哎呀,我没注意到你的PHP中有以下内容:
$enc = $_COOKIE["MyCookie"];
Run Code Online (Sandbox Code Playgroud)
这是警告.PHP可能无法按原样获取加密数据,并且正在运行一些urldecode清理.您应该打印此变量以查看它与C#代码发送的内容真正匹配.
EDIT2:
通过添加以下内容将空格转换为cookie中的缺失+字符:
str_replace(' ', '+', $enc);
Run Code Online (Sandbox Code Playgroud)