2011-08-16 26 views
14

Estoy tratando de leer un valor Base64-Encoded de una tabla de base de datos administrada en el lado de Linux. En esa tabla hay una columna llamada first_name. En el lado de Linux que puedo descifrar esto fácilmente usando el siguiente comando en PHP:¿Cómo puedo descifrar un valor cifrado MCRYPT_RIJNDAEL_256 en C#, cifrado por mcrypt en PHP?

$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, "patient_fn_salt", 
         base64_decode("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo="), 
         MCRYPT_MODE_ECB); 

Sin embargo, me tratan como todo lo que pueda para duplicar esta lógica en el lado C# y todo lo que consigo es un galimatías.

Mi código C# es más adelante, espero que tenga algunas sugerencias porque me acabaron las ideas :(

byte [] cipherText = 
     Convert.FromBase64String("H6XmkH+VWvdD88THCliKJjLisGZIBk3CTNvyQMLnhpo="); 
byte [] key = Encoding.UTF8.GetBytes("patient_fn_salt"); 
Array.Resize(ref key, 32); 
byte [] iv = new byte[32]; 

string fname = Utilities.Decrypt(cipherText, key, iv); 


public static string Decrypt(byte[] cipherText, byte[] Key, byte[] IV) 
    { 
    // Check arguments. 
    if (cipherText == null || cipherText.Length <= 0) 
    throw new ArgumentNullException("cipherText"); 
    if (Key == null || Key.Length <= 0) 
    throw new ArgumentNullException("Key"); 
    if (IV == null || IV.Length <= 0) 
    throw new ArgumentNullException("Key"); 

    // TDeclare the streams used 
    // to decrypt to an in memory 
    // array of bytes. 
    MemoryStream msDecrypt = null; 
    CryptoStream csDecrypt = null; 
    StreamReader srDecrypt = null; 

    // Declare the AesManaged object 
    // used to decrypt the data. 
    RijndaelManaged rj = new RijndaelManaged(); 

    // Declare the string used to hold 
    // the decrypted text. 
    string plaintext = null; 

    try 
    { 
    // Create an AesManaged object 
    // with the specified key and IV. 

    rj.Mode = CipherMode.ECB; 
    rj.BlockSize = 256; 
    rj.KeySize = 256; 
    rj.Padding = PaddingMode.Zeros; 

    rj.Key = Key; 
    rj.GenerateIV(); 
    //rj.IV = IV; 


    // Create a decrytor to perform the stream transform. 
    ICryptoTransform decryptor = rj.CreateDecryptor(rj.Key, rj.IV); 

    // Create the streams used for decryption. 
    msDecrypt = new MemoryStream(cipherText); 
    csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); 
    srDecrypt = new StreamReader(csDecrypt); 

    // Read the decrypted bytes from the decrypting stream 
    // and place them in a string. 
    plaintext = srDecrypt.ReadToEnd(); 
    } 
    finally 
    { 
    // Clean things up. 

    // Close the streams. 
    if (srDecrypt != null) 
    srDecrypt.Close(); 
    if (csDecrypt != null) 
    csDecrypt.Close(); 
    if (msDecrypt != null) 
    msDecrypt.Close(); 

    // Clear the AesManaged object. 
    if (rj != null) 
    rj.Clear(); 
    } 
    return plaintext; 
    } 
} 
+0

Tal vez esto puede ayudar. http://stackoverflow.com/questions/202011/encrypt-decrypt-string-in-net –

+1

Solo una nota: El parámetro 'IV' no se usa en su método ... y el modo ECB no tiene vectores de inicialización. (Pero no creo que esto se relacione con su problema.) –

Respuesta

5

Como dice Paulo, el modo BCE no hace uso de una vía intravenosa. Si C# insiste en una continuación, utilizar todos de cero bytes.

La tecla "patient_fn_salt" tiene 15 caracteres, 120 bits. Su función de descifrado está esperando 256 bits de clave. tienes que ser muy asegurarse de que los bits adicionales son idénticos en ambos sistemas y se están agregado en el mismo lugar en ambos sistemas. Incluso un error simple dará como resultado el descifrado de la basura. Lea el documento PHP umentación con mucho cuidado para determinar exactamente cómo se expande "patient_fn_salt" a una clave de 256 bits. En particular, compruebe si la clave real es SHA256("patient_fn_salt").

Como un lado, el modo ECB es inseguro. Utilice ya sea el modo CTR o el modo CBC de preferencia. El modo CTR no requiere relleno, por lo que probablemente signifique menos texto cifrado para almacenar.

ETA: en otra lectura veo que el lado C# está relleno con ceros. ¿Qué relleno está usando el lado de PHP? El relleno cero no es una buena idea, ya que no puede reconocer un descifrado defectuoso. El relleno PKCS7 tiene muchas más posibilidades de reconocer una salida defectuosa. Lo mejor es especificar explícitamente el relleno en ambos extremos en lugar de confiar en los valores predeterminados.

+0

La [documentación dice] (http://www.php.net/manual/en/function.mcrypt-decrypt.php): * Si es más pequeño que el tamaño de clave requerido, está rellenado con '\ 0'. * ... y Array.Resize parece que hace lo mismo. Pero tal vez el relleno explícito es más seguro. –

+0

Sería útil verificar directamente el contenido de las dos matrices de bytes si es posible. De lo contrario, vale la pena probar el relleno explícito. – rossum

3

La publicación es antigua, pero esto podría ayudar a alguien en el futuro. Esta función cifrar exactamente igual que con los parámetros MCRYPT_ENCRYPT MCRYPT_RIJNDAEL_256 y MCRYPT_MODE_ECB

static byte[] EncryptStringToBytes(string plainText, byte[] key) 
    { 
     if (plainText == null || plainText.Length <= 0) 
      throw new ArgumentNullException("plainText"); 
     if (key == null || key.Length <= 0) 
      throw new ArgumentNullException("key"); 

     byte[] encrypted; 
     using (var rijAlg = new RijndaelManaged()) 
     { 
      rijAlg.BlockSize = 256; 
      rijAlg.Key = key; 
      rijAlg.Mode = CipherMode.ECB; 
      rijAlg.Padding = PaddingMode.Zeros; 
      rijAlg.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 

      ICryptoTransform encryptor = rijAlg.CreateEncryptor(rijAlg.Key, rijAlg.IV); 
      using (var msEncrypt = new MemoryStream()) 
       using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
       { 
        using (var swEncrypt = new StreamWriter(csEncrypt)) 
         swEncrypt.Write(plainText); 
        encrypted = msEncrypt.ToArray(); 
       } 
     } 
     return encrypted; 
    } 

Y aquí es la función para descifrarlo

 static string DecryptStringFromBytes(byte[] cipherText, byte[] key) 
    { 
     if (cipherText == null || cipherText.Length <= 0) 
      throw new ArgumentNullException("cipherText"); 
     if (key == null || key.Length <= 0) 
      throw new ArgumentNullException("key"); 

     string plaintext; 
     using (var rijAlg = new RijndaelManaged()) 
     { 
      rijAlg.BlockSize = 256; 
      rijAlg.Key = key; 
      rijAlg.Mode = CipherMode.ECB; 
      rijAlg.Padding = PaddingMode.Zeros; 
      rijAlg.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 

      ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); 
      using (var msDecrypt = new MemoryStream(cipherText)) 
       using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
        using (var srDecrypt = new StreamReader(csDecrypt)) 
         plaintext = srDecrypt.ReadToEnd(); 
     } 
     return plaintext; 
    } 
+1

Espero que esto no sea útil para nadie, ya que si lo es, significa que están usando una criptografía bastante cuestionable. – CodesInChaos