2012-08-08 143 views
17

我想在C#中加密一些(cookie)數據,然後用PHP解密它。我選擇了使用Rijndael加密。我幾乎可以工作,除了部分文本被解密!我開始從這個例子的工作:Decrypt PHP encrypted string in C#C#加密到PHP解密

這裏的文本(JSON),我加密(敏感信息已刪除):

{"DisplayName":"xxx", "Username": "yyy", "EmailAddress":"zzz"} 

所以我登錄到它創建/ C#的應用程序從存儲編碼的cookie Key和IV,然後重定向到應該解密/讀取cookie的PHP應用程序。當我解密的cookie,它出來是這樣的:

{"DisplayName":"xxx","F�A ;��HP=D�������4��z����ť���k�#E���R�j�5�\�t. t�D��" 

UPDATE:我已經得到一點點進一步,這是現在的結果

string(96) "{"DisplayName":"xxx","Username":"yyy","EmailAddress"�)ق��-�J��k/VV-v� �9�B`7^" 

正如你所看到的,它開始解密它,但然後得到搞砸了...

當解密字符串它出來正確(與填充,我有一個函數要刪除Ë填充),但如果我一個字符改變測試字符串,我得到一次垃圾:

B�nHL�Ek �¿?�UΣlO����OЏ�M��NO/�f.M���Lƾ�CC�Y>F��~�qd�+ 

這裏的C#代碼,我用它來生成隨機密鑰和IV:

UPDATE:我只是用靜態密鑰/ IV現在,在這裏,他們是:

Key: lkirwf897+22#bbtrm8814z5qq=498j5 
IV: 741952hheeyy66#[email protected] 

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保存到數據庫中,以便以後進行編碼/解碼。

這是完全加密類:

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代碼:

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#[email protected]'; // 32 * 8 = 256 bit iv 

$enc = $_COOKIE["MyCookie"]; 

$dtext = decryptRJ256($ky, $iv, $enc); 
var_dump($dtext); 

我對這個部分有點不確定,因爲所有的我見過的示例代碼只是將base64編碼的字符串直接傳遞給解密器,但在我的示例中,我必須在通過base64_decode之前對其進行解碼,否則會出現密鑰和IV長度不正確的錯誤。

UPDATE:我使用PHP所需格式的ASCII密鑰。如果我從RijndaelManaged類生成密鑰,他們不會在PHP端工作,但我可以使用已知在PHP端工作的密鑰並在RijndaelManaged C#端使用它們。

如果我遺漏了任何相關信息,請讓我知道。 TIA!

+2

我似乎記得,你可以得到這個,如果你提供解密IV是錯誤 – pm100 2012-08-08 22:06:03

+0

是否不工作IV? – 2012-08-08 22:07:45

+0

如果我不包含IV,它不起作用。 – solidau 2012-08-08 22:18:17

回答

5

由於字符串被部分行,但有亂碼在端部將其預計的256個字節確切塊加密內提供一個填充的問題。我建議設置填充爲PKCS7(PaddingMode.PKCS7),而不是 C#的一側PHP就會明白沒有問題(因爲它是默認模式上解析器)。

編輯:哎呀,我沒有注意到,你有你的PHP以下內容:

$enc = $_COOKIE["MyCookie"]; 

這是需要注意的。 PHP很可能不會按原樣獲取加密數據,並且正在運行一些urldecode消毒。你應該打印這個變量,看看它是否真正匹配從C#代碼發送的內容。

EDIT2:

加入這個轉換的空格丟失+字符從cookie:

str_replace(' ', '+', $enc); 
+0

我改變了這一點,但仍然有一個問題......我只能編碼/解碼一個特定的字符串!我有一個JSON字符串,我一直用於測試,我終於得到了解密。因此,我繼續測試其他一些字符串,如果我在測試字符串中更改了1個字符,它就會中斷...請在幾分鐘內檢查我的代碼更新... – solidau 2012-08-10 20:11:25

+0

字符串有點不同,在PHP方面的「+」缺少... – solidau 2012-08-10 20:51:00

+1

對於稍後閱讀這些內容的人,我不得不刪除一些在兩者之間的評論....基本上,當字符串從cookie發送到PHP時,PHP會得到字符串並替換「+」帶空格的字符「」 – solidau 2012-08-10 21:00:34

18

爲後人我在這裏將完全完整的解決方案。

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); 
    } 
} 

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); 

?> 
+0

我必須將c#代碼更改爲: var encoding = new UTF8Encoding(); var key = encoding.GetBytes(prm_key); var IV = encoding.GetBytes(prm_iv); 我無法找到一種方法來使用Convert.FromBase64String(prm_iv)生成適當的「prm_iv」,然後我嘗試了Encoding.ASCII.GetBytes,我可以生成密鑰,但無法從PHP獲得正確的結果。所以我偶然發現了另一個問題/ ansewer在同樣的環境下,我嘗試了UTF8Encoding的GetBytes並最終工作。謝謝大家。 – 2015-11-21 19:33:24

+0

作品像魅力,C#4.0和PHP 5.4x,謝謝先生:) – 2016-03-22 06:57:31