2011-03-27 22 views
17

Estoy buscando crear una clase que use las librerías .NET que sean compatibles con OpenSSL. Soy consciente de que hay un contenedor OpenSSL.Net, pero preferiría evitar hacer referencia al código de terceros \ no administrado. No estoy buscando una discusión sobre si esta es la opción correcta, pero hay razones para ello.Encriptación OpenSSL usando clases .NET

Actualmente tengo lo siguiente, que creo que debería ser compatible con OpenSSL: efectivamente hace lo que creo que hace OpenSSL a partir de la documentación de OpenSSL. Sin embargo, incluso cuando sólo usar esta clase para hacer tanto el cifrado y descifrado, estoy recibiendo el siguiente error:

[CryptographicException] Padding is invalid and cannot be removed. 

He caminado a través del código y verificado que la sal tecla \ \ IV son todos iguales durante el proceso de cifrado y descifrado

Consulte a continuación la clase de muestra y las llamadas para cifrar el descifrado. Cualquier idea o puntero sería bienvenida.

public class Protection 
    { 
     public string OpenSSLEncrypt(string plainText, string passphrase) 
     { 
      // generate salt 
      byte[] key, iv; 
      byte[] salt = new byte[8]; 
      RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
      rng.GetNonZeroBytes(salt); 
      DeriveKeyAndIV(passphrase, salt, out key, out iv); 
      // encrypt bytes 
      byte[] encryptedBytes = EncryptStringToBytesAes(plainText, key, iv); 
      // add salt as first 8 bytes 
      byte[] encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length]; 
      Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 0, salt.Length); 
      Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length, encryptedBytes.Length); 
      // base64 encode 
      return Convert.ToBase64String(encryptedBytesWithSalt); 
     } 

     public string OpenSSLDecrypt(string encrypted, string passphrase) 
     { 
      // base 64 decode 
      byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted); 
      // extract salt (first 8 bytes of encrypted) 
      byte[] salt = new byte[8]; 
      byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length]; 
      Buffer.BlockCopy(encryptedBytesWithSalt, 0, salt, 0, salt.Length); 
      Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length, encryptedBytes, 0, encryptedBytes.Length); 
      // get key and iv 
      byte[] key, iv; 
      DeriveKeyAndIV(passphrase, salt, out key, out iv); 
      return DecryptStringFromBytesAes(encryptedBytes, key, iv); 
     } 

     private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv) 
     { 
      // generate key and iv 
      List<byte> concatenatedHashes = new List<byte>(48); 

      byte[] password = Encoding.UTF8.GetBytes(passphrase); 
      byte[] currentHash = new byte[0]; 
      MD5 md5 = MD5.Create(); 
      bool enoughBytesForKey = false; 
      // See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM 
      while (!enoughBytesForKey) 
      { 
       int preHashLength = currentHash.Length + password.Length + salt.Length; 
       byte[] preHash = new byte[preHashLength]; 

       Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); 
       Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length); 
       Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length); 

       currentHash = md5.ComputeHash(preHash); 
       concatenatedHashes.AddRange(currentHash); 

       if (concatenatedHashes.Count >= 48) 
        enoughBytesForKey = true; 
      } 

      key = new byte[32]; 
      iv = new byte[16]; 
      concatenatedHashes.CopyTo(0, key, 0, 32); 
      concatenatedHashes.CopyTo(32, iv, 0, 16); 

      md5.Clear(); 
      md5 = null; 
     } 

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

      // Declare the stream used to encrypt to an in memory 
      // array of bytes. 
      MemoryStream msEncrypt; 

      // Declare the RijndaelManaged object 
      // used to encrypt the data. 
      RijndaelManaged aesAlg = null; 

      try 
      { 
       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged { Key = key, IV = iv, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256 }; 


       // Create an encryptor to perform the stream transform. 
       ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 

       // Create the streams used for encryption. 
       msEncrypt = new MemoryStream(); 
       using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
       { 
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
        { 

         //Write all data to the stream. 
         swEncrypt.Write(plainText); 
         swEncrypt.Flush(); 
         swEncrypt.Close(); 
        } 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      // Return the encrypted bytes from the memory stream. 
      return msEncrypt.ToArray(); 
     } 

     static string DecryptStringFromBytesAes(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("iv"); 

      // Declare the RijndaelManaged object 
      // used to decrypt the data. 
      RijndaelManaged aesAlg = null; 

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

      try 
      { 
       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged { Key = key, IV = iv, Mode = CipherMode.CBC, KeySize = 256, BlockSize = 256}; 

       // Create a decrytor to perform the stream transform. 
       ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); 
       // Create the streams used for decryption. 
       using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
       { 
        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
        { 
         using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
         { 
          // Read the decrypted bytes from the decrypting stream 
          // and place them in a string. 
          plaintext = srDecrypt.ReadToEnd(); 
          srDecrypt.Close(); 
         } 
        } 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      return plaintext; 
     } 
    } 

entonces yo llamo a esto para probarlo:

Protection protection = new Protection(); 
const string passphrase = "<passphrase>"; 
string encrypted = protection.OpenSSLEncrypt(jobid, passphrase); 
string decrypted = protection.OpenSSLDecrypt(encrypted, passphrase); 
+0

Si está tratando de lograr AES, el tamaño del bloque es incorrecto, es de 128 bits para AES ('BlockSize = 256'). No veo ninguna mención de padding PKCS # 7 y creo que es un valor predeterminado de OpenSSL para AES. También la práctica habitual es crear un iv aleatorio en el cifrado y anteponerlo a los datos cifrados. En la desencriptación, extraiga el iv y úselo para el descifrado. – zaph

+0

Necesito implementar lo mismo pero con 3DES. ¿Cualquier sugerencia? Estoy tratando de implementar eso? – Azimuth

Respuesta

26

Finalmente descubierto éste hacia fuera. En caso de que alguien necesite integrar openssl y .NET sin usar los contenedores de openssl, compartiré los resultados aquí.

1) El problema principal con mi código original (como en la pregunta) es que debe inicializar BlockSize y KeySize en su instancia administrada por Rijndael ANTES de configurar la clave o IV.

2) También tuve BlockSize establece en 256, cuando sólo debería ser 128

3) El resto de mi problema llegó al hecho de que pone openssl y espera "Salted__" en la parte frontal de la sal antes de añadir la cadena encriptada y luego base64 codificándola. (Lo vi inicialmente en la documentación de openssl con respecto al cifrado de archivos, pero no pensé que lo hiciera al hacerlo directamente a través de la línea de comandos: ¡al parecer estaba equivocado! ¡Observe también la mayúscula del S en Salado!)

con que todos en mente, aquí es mi código "fija":

public class Protection 
    { 
     public string OpenSSLEncrypt(string plainText, string passphrase) 
     { 
      // generate salt 
      byte[] key, iv; 
      byte[] salt = new byte[8]; 
      RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
      rng.GetNonZeroBytes(salt); 
      DeriveKeyAndIV(passphrase, salt, out key, out iv); 
      // encrypt bytes 
      byte[] encryptedBytes = EncryptStringToBytesAes(plainText, key, iv); 
      // add salt as first 8 bytes 
      byte[] encryptedBytesWithSalt = new byte[salt.Length + encryptedBytes.Length + 8]; 
      Buffer.BlockCopy(Encoding.ASCII.GetBytes("Salted__"), 0, encryptedBytesWithSalt, 0, 8); 
      Buffer.BlockCopy(salt, 0, encryptedBytesWithSalt, 8, salt.Length); 
      Buffer.BlockCopy(encryptedBytes, 0, encryptedBytesWithSalt, salt.Length + 8, encryptedBytes.Length); 
      // base64 encode 
      return Convert.ToBase64String(encryptedBytesWithSalt); 
     } 

     public string OpenSSLDecrypt(string encrypted, string passphrase) 
     { 
      // base 64 decode 
      byte[] encryptedBytesWithSalt = Convert.FromBase64String(encrypted); 
      // extract salt (first 8 bytes of encrypted) 
      byte[] salt = new byte[8]; 
      byte[] encryptedBytes = new byte[encryptedBytesWithSalt.Length - salt.Length - 8]; 
      Buffer.BlockCopy(encryptedBytesWithSalt, 8, salt, 0, salt.Length); 
      Buffer.BlockCopy(encryptedBytesWithSalt, salt.Length + 8, encryptedBytes, 0, encryptedBytes.Length); 
      // get key and iv 
      byte[] key, iv; 
      DeriveKeyAndIV(passphrase, salt, out key, out iv); 
      return DecryptStringFromBytesAes(encryptedBytes, key, iv); 
     } 

     private static void DeriveKeyAndIV(string passphrase, byte[] salt, out byte[] key, out byte[] iv) 
     { 
      // generate key and iv 
      List<byte> concatenatedHashes = new List<byte>(48); 

      byte[] password = Encoding.UTF8.GetBytes(passphrase); 
      byte[] currentHash = new byte[0]; 
      MD5 md5 = MD5.Create(); 
      bool enoughBytesForKey = false; 
      // See http://www.openssl.org/docs/crypto/EVP_BytesToKey.html#KEY_DERIVATION_ALGORITHM 
      while (!enoughBytesForKey) 
      { 
       int preHashLength = currentHash.Length + password.Length + salt.Length; 
       byte[] preHash = new byte[preHashLength]; 

       Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length); 
       Buffer.BlockCopy(password, 0, preHash, currentHash.Length, password.Length); 
       Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + password.Length, salt.Length); 

       currentHash = md5.ComputeHash(preHash); 
       concatenatedHashes.AddRange(currentHash); 

       if (concatenatedHashes.Count >= 48) 
        enoughBytesForKey = true; 
      } 

      key = new byte[32]; 
      iv = new byte[16]; 
      concatenatedHashes.CopyTo(0, key, 0, 32); 
      concatenatedHashes.CopyTo(32, iv, 0, 16); 

      md5.Clear(); 
      md5 = null; 
     } 

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

      // Declare the stream used to encrypt to an in memory 
      // array of bytes. 
      MemoryStream msEncrypt; 

      // Declare the RijndaelManaged object 
      // used to encrypt the data. 
      RijndaelManaged aesAlg = null; 

      try 
      { 
       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged { Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv }; 

       // Create an encryptor to perform the stream transform. 
       ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV); 

       // Create the streams used for encryption. 
       msEncrypt = new MemoryStream(); 
       using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
       { 
        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) 
        { 

         //Write all data to the stream. 
         swEncrypt.Write(plainText); 
         swEncrypt.Flush(); 
         swEncrypt.Close(); 
        } 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      // Return the encrypted bytes from the memory stream. 
      return msEncrypt.ToArray(); 
     } 

     static string DecryptStringFromBytesAes(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("iv"); 

      // Declare the RijndaelManaged object 
      // used to decrypt the data. 
      RijndaelManaged aesAlg = null; 

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

      try 
      { 
       // Create a RijndaelManaged object 
       // with the specified key and IV. 
       aesAlg = new RijndaelManaged {Mode = CipherMode.CBC, KeySize = 256, BlockSize = 128, Key = key, IV = iv}; 

       // Create a decrytor to perform the stream transform. 
       ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV); 
       // Create the streams used for decryption. 
       using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
       { 
        using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) 
        { 
         using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
         { 
          // Read the decrypted bytes from the decrypting stream 
          // and place them in a string. 
          plaintext = srDecrypt.ReadToEnd(); 
          srDecrypt.Close(); 
         } 
        } 
       } 
      } 
      finally 
      { 
       // Clear the RijndaelManaged object. 
       if (aesAlg != null) 
        aesAlg.Clear(); 
      } 

      return plaintext; 
     } 
    } 
+0

Hola, ¿tienes los argumentos de línea de comando de openssl equivalentes para hacer las tareas opuestas? Por ejemplo, encripto un mensaje con su código, ¿cuál es el comando que debo usar para descifrarlo de openssl? – hardywang

+1

El comando equivalente para descifrar con openssl es 'openssl enc -d -a-256-cbc -a -in archivo_cifrado.txt> descrypted_file.txt' –

+0

¡Ese es un vínculo inactivo! Aquí está el correcto: https://www.openssl.org/docs/manmaster/man3/EVP_BytesToKey.html – balage

2

temo que hay problemas con este último código, así como los resultados OpenSSLDecrypt en un error:

Padding is invalid and cannot be removed.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for >more information about the error and where it originated in the code.

Exception Details: System.Security.Cryptography.CryptographicException: Padding is invalid and cannot be removed.

It occurs at the closen paren of this code:

using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))" in 'static string DecryptStringFromBytesAes(byte[] cipherText, byte[] key, byte[] iv)

no tenía idea de que sería tan difícil encriptar una burbuja de texto desde una computadora , luego enviarlo para su almacenamiento y descifrado a otro.

Cuestiones relacionadas