2012-08-08 22 views
17

Estoy tratando de cifrar algunos datos (cookies) en C# y luego descifrarlos en PHP. He elegido usar el cifrado Rijndael. Casi lo tengo funcionando, ¡excepto que solo una parte del texto está descifrado! Empecé a trabajar a partir de este ejemplo: Decrypt PHP encrypted string in C#C# Cifrado para descifrado PHP

Aquí está el texto (JSON) que estoy cifrando (información sensible eliminado):

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

Así que iniciar sesión en el # app C que crea/codifica la cookie de almacenado Key y IV y luego redirige a la aplicación PHP que se supone debe descifrar/leer la cookie. Cuando descifrar la cookie, sale así:

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

ACTUALIZACIÓN: He llegado un poco más lejos y esto ahora es el resultado

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

Como se puede ver , comienza a descifrarlo, pero luego se arruina ...

Cuando Descifra la cuerda sale correcta (con relleno, que tengo una función para eliminar e relleno), pero si cambio de la cadena de prueba por un personaje consigo la basura una vez más:

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

Aquí está el código C# que utilizo para generar la clave aleatoria y IV:

ACTUALIZACIÓN: Sólo estoy usando la clave estática/IV, por ahora, aquí están:

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

A continuación, guardo la clave y IV en una base de datos que se recuperará más tarde para la codificación/decodificación.

Ésta es la clase de cifrado completo:

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

Aquí está el código PHP que estoy usando para descifrar los datos:

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

estoy un poco inseguro acerca de esta parte, porque todos el código de ejemplo que he visto simplemente pasa en la cadena codificada en base64 directamente al descifrador, pero en mi ejemplo, tengo que base64_decode antes de pasarlo, de lo contrario me sale el error de que la clave y IV no tienen la longitud correcta.

ACTUALIZACIÓN: Estoy usando claves ASCII en el formato necesario para PHP. Si genero claves de la clase RijndaelManaged, no funcionan en el lado de PHP, pero puedo usar claves que se sabe que funcionan en el lado de PHP y las uso en el lado de RijndaelManaged C#.

Háganme saber si omití la información pertinente. TIA!

+2

Creo recordar que usted puede conseguir esto si el IV que proporcione para descifrar está mal – pm100

+0

¿Funciona sin el IV? –

+0

No funciona si no incluyo el IV. – solidau

Respuesta

5

Dado que la cadena está parcialmente bien, pero hay un galimatías al final, sugeriría un problema de relleno dentro del cifrado que espera bloques exactos de 256 bytes. Sugiero configurar el relleno como PKCS7 (PaddingMode.PKCS7) en lugar de Ceros en el lado C# que PHP entenderá sin problemas (ya que es el modo predeterminado en ese analizador).

Editar: Vaya, no me di cuenta que tenía lo siguiente en su PHP:

$enc = $_COOKIE["MyCookie"]; 

Ésta es la advertencia. Es probable que PHP no obtenga los datos cifrados tal como están y está ejecutando desinfección de urldecode. Debe imprimir esta variable para ver que realmente coincide con lo que se envía desde el código C#.

Edit2:

convertir los espacios en blanco que faltan + caracteres de la cookie mediante la adición de esto:

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

He cambiado esto, pero todavía hay un problema ... ¡Solo puedo codificar/decodificar una cadena en particular! Tengo una cadena JSON que he estado usando para probar, y finalmente lo descifré. Entonces, seguí probando algunas otras cadenas, si cambio incluso 1 carácter en mi cadena de prueba, se rompe ... verifique la actualización de mi código en unos minutos ... – solidau

+0

Las cadenas son un poco diferentes, en el Falta el lado "+" de PHP ... – solidau

+1

Para las personas que leen esto más tarde, tuve que eliminar algunos comentarios entre ... básicamente, cuando la cadena se enviaba a PHP desde una cookie, PHP obtenía la cadena y la reemplazaba "+" personaje con espacio "" – solidau

18

Para la posteridad Estoy colocando la solución totalmente terminado aquí.

C# Clase cifrado

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 descifrado de fragmentos

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

Tengo que cambiar el código C# a: var encoding = new UTF8Encoding(); var clave = codificación. GetBytes (prm_key); var IV = encoding.GetBytes (prm_iv); No pude encontrar una forma de generar el apropiado "prm_iv" usando Convert.FromBase64String (prm_iv), luego probé Encoding.ASCII.GetBytes y pude generar la clave pero no pude obtener el resultado correcto de PHP. Así que me encontré con otra pregunta/respuesta en el mismo contexto en que probé los GetBytes de UTF8Encoding y finalmente funcionó. Gracias a todos. –

+0

funciona como charm, C# 4.0 y php 5.4x, gracias señor :) –

Cuestiones relacionadas