2012-08-08 27 views
17

Sto tentando di crittografare alcuni dati (cookie) in C# e quindi decrittografarli in PHP. Ho scelto di usare la crittografia Rijndael. Ho quasi funzionato, tranne che solo una parte del testo è stata decifrata! Ho iniziato a lavorare da questo esempio: Decrypt PHP encrypted string in C#C# Crittografia in PHP Decryption

Ecco il testo (JSON) che io sono la crittografia (informazioni sensibili rimossa):

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

Così faccio il login al C# app che crea/codifica il cookie di memorizzata Chiave e IV e poi reindirizza all'app PHP che dovrebbe decodificare/leggere il cookie. Quando ho decifrare il biscotto, esce in questo modo:

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

UPDATE: ho ottenuto un po 'più avanti e questo è ora il risultato

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

Come si può vedere , inizia a decrittografarlo, ma poi viene incasinato ...

Quando decrittografa la stringa risulta corretta (con padding, che ho una funzione di remov e padding), ma se cambio la stringa di prova di un carattere ricevo ancora spazzatura:

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

Ecco il codice C# che uso per generare il caso chiave e IV:

UPDATE: sto solo usando la chiave statica/IV per ora, qui sono:

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

Ho quindi salvato la chiave e IV in un database da recuperare in seguito per la codifica/decodifica.

Questa è la classe crittografia completa:

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

Ecco il codice PHP che sto usando per decodificare i dati:

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

Io sono un po 'incerto su questa parte, perché tutti il codice di esempio che ho visto passa semplicemente nella stringa codificata in base 64 direttamente al decryptor, ma nel mio esempio, devo basarlo 64_decode prima di passarlo altrimenti ricevo l'errore che la chiave e IV non sono la lunghezza corretta.

UPDATE: Sto utilizzando le chiavi ASCII nel formato richiesto da PHP. Se genero chiavi dalla classe RijndaelManaged, non funzionano sul lato PHP, ma posso usare le chiavi che sono conosciute per funzionare su PHP e usarle nel lato RijndaelManaged C#.

Per favore fatemi sapere se ho omesso qualsiasi informazione pertinente. TIA!

+2

Mi sembra di ricordare che si può ottenere questo se il IV fornite per decifrare è sbagliato – pm100

+0

Funziona senza la IV? –

+0

Non funziona se non includo la flebo. – solidau

risposta

5

Poiché la stringa è parzialmente OK, ma alla fine è priva di significato, suggerirebbe un problema di riempimento all'interno della crittografia che prevede blocchi esatti di 256 byte. Suggerisco di impostare il padding come PKCS7 (PaddingMode.PKCS7) anziché Zeros sul lato C# che PHP comprenderà senza problemi (poiché è la modalità predefinita su quel parser).

Edit: Ops, non ho notato che lei ha avuto il seguente in PHP:

$enc = $_COOKIE["MyCookie"]; 

Questo è l'avvertimento. Probabilmente PHP non sta ottenendo i dati crittografati così come sono e sta eseguendo un po 'di sanitizzazione di urldecode. Dovresti stampare questa variabile per verificare che corrisponda realmente a ciò che viene inviato dal codice C#.

Edit2:

convertire i spazi vuoti a caratteri mancanti + dal cookie aggiungendo questo:

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

Ho cambiato questo, ma c'è ancora un problema ... Posso solo codificare/decodificare una stringa particolare! Ho una stringa JSON che sto usando per i test e finalmente ho deciso di decifrare. Quindi ho continuato a testare qualche altra stringa, se cambio anche 1 carattere nella mia stringa di test, si rompe ... per favore controlla l'aggiornamento del mio codice in pochi minuti .... – solidau

+0

Le stringhe sono un po 'diverse, sul Manca il lato PHP "+" ... – solidau

+1

Per le persone che leggono più tardi, ho dovuto cancellare alcuni commenti tra ... in pratica, quando la stringa è stata inviata a PHP da un cookie, PHP avrebbe ottenuto la stringa e sostituito "+" carattere con spazio "" – solidau

18

Ai posteri sto mettendo la soluzione completamente compilato qui.

C# classe Encryption

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 decrittografia Snippet

<?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

Devo cambiare il codice C# in: var encoding = new UTF8Encoding(); var key = encoding.GetBytes (prm_key); var IV = encoding.GetBytes (prm_iv); Non ho trovato un modo per generare l'appropriato "prm_iv" usando Convert.FromBase64String (prm_iv), quindi ho provato Encoding.ASCII.GetBytes e ho potuto generare la chiave ma non ho potuto ottenere il risultato corretto da PHP. Quindi mi sono imbattuto in un'altra domanda/ansewer nello stesso contesto in cui ho provato GetBytes da UTF8Encoding e infine ho funzionato. Grazie a tutti. –

+0

funziona come il fascino, C# 4.0 e php 5.4x, grazie signore :) –

Problemi correlati