2014-10-20 11 views
6

Attualmente sto lavorando all'implementazione di AES in C#. Il metodo di crittografia ha due parametri: una stringa e una password. Sto prendendo la stringa fornita e convertendola in una matrice di byte, quindi posso usarla più tardi per scrivere i dati su uno stream con BinaryWriter.Come ottenere correttamente e in modo coerente i byte da una stringa per la crittografia AES?

Il problema è che quando uso Convert.FromBase64String(string) ottengo FormatException: Invalid length. e quando uso il metodo di decodifica Encoding.UTF8.GetBytes(string) e l'eccezione PKCS7.Padding non valida.

Ho cercato di risolvere questo problema negli ultimi due giorni. Ho letto quasi infinite domande su stackoverflow.com e altri siti Web, ma non so ancora quale sia il modo più affidabile per risolvere questo problema.

Le stringhe che verranno utilizzate in questo programma sono limitate alle frasi (ad esempio "Qualcosa da crittografare".) E ai numeri (ad esempio "12345").

Grazie in anticipo, ecco il codice che ho a questo punto nel tempo:

public class AESProvider { 

    public byte[] EncryptStringToBytes_Aes(string plainText, string Key) 
    { 
     // Check arguments. 
     if (plainText == null || plainText.Length <= 0) 
      throw new ArgumentNullException("plainText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     byte[] plainTextInBytes = Convert.FromBase64String(plainText); 
     byte[] encrypted; 

     //Create an Aes object 
     //with the specified key and IV. 

     using (Aes aesAlg = Aes.Create()) 
     { 
      aesAlg.GenerateIV(); 
      byte[] IV = aesAlg.IV; 
      //The Salt will be the first 8 bytes of the IV. 
      byte[] theSalt = new byte[8]; 
      Array.Copy(IV,theSalt,8); 
      //A key for AES is generated by expanding the password using the following method. 
      Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
      byte[] aesKey = keyGen.GetBytes(16); 
      aesAlg.Key = aesKey; 

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

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

         //Write all data to the stream. 
         swEncrypt.Write(plainTextInBytes); 
        } 
        encrypted = msEncrypt.ToArray(); 
       } 
      } 
      // Prepend the IV to the ciphertext so it can be used in the decryption process. 
      using (MemoryStream ivPlusCipher = new MemoryStream()) 
      { 
       using (BinaryWriter tBinaryWriter = new BinaryWriter(ivPlusCipher)) 
       { 
        tBinaryWriter.Write(IV); 
        tBinaryWriter.Write(encrypted); 
        tBinaryWriter.Flush(); 
       } 
       return ivPlusCipher.ToArray(); 
      } 
     } 
    } 

    public byte[] DecryptStringFromBytes_Aes(byte[] cipherText, string Key) 
    { 
     // Check arguments. 
     if (cipherText == null || cipherText.Length <= 0) 
      throw new ArgumentNullException("cipherText"); 
     if (Key == null || Key.Length <= 0) 
      throw new ArgumentNullException("Key"); 
     // Declare the string used to hold 
     // the decrypted text. 
     byte[] decrypted; 

     // Create an Aes object 
     // with the specified key and IV. 

     // Create the streams used for decryption. 

     using (Aes aesAlg = Aes.Create()) 
     { 
      aesAlg.Mode = CipherMode.CBC; 
      aesAlg.Padding = PaddingMode.PKCS7; 
      //Grab IV from ciphertext 
      byte[] IV = new byte[16]; 
      Array.Copy(cipherText,0,IV,0,16); 
      //Use the IV for the Salt 
      byte[] theSalt = new byte[8]; 
      Array.Copy(IV,theSalt,8); 
      Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
      byte[] aesKey = keyGen.GetBytes(16); 
      aesAlg.Key = aesKey; 

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

      using (MemoryStream msDecrypt = new MemoryStream()) 
      { 
       using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write)) 
       { 
        using (BinaryWriter srDecrypt = new BinaryWriter(csDecrypt)) 
        { 
         //Decrypt the ciphertext 
         srDecrypt.Write(cipherText, IV.Length, (cipherText.Length - IV.Length)); 
        } 
        decrypted = msDecrypt.ToArray(); 
        return decrypted; 
       } 
      } 
     } 
    } 
} 
+0

il tuo 'DecryptStringFromBytes' non restituisce una stringa, perché? –

+1

La domanda più importante è e rimarrà: cosa viene inserito nella stringa di input che si suppone decrittografare. Voglio dire, se sono esadecimali, nessuna risposta sarà corretta. Si prega di fornire un esempio! –

+0

Grazie per l'input. Esempi di stringhe in cui sono già stati aggiunti nella domanda! – Mandos

risposta

6

È necessario per la conversione tra byte e stringhe prima e dopo la cifratura/decifratura. Questa non è la stessa operazione e non si dovrebbe usare lo stesso metodo.

Durante la crittografia si inizia con una stringa arbitraria. Converti questo in un byte [] usando Encoding.UTF8.GetBytes(). Criptalo. Il byte risultante [] può ora essere convertito in una stringa utilizzando Convert.ToBase64String().

Quando decodifichi ora inizi con una stringa codificata Base64. Decodificarlo su un byte [] utilizzando Convert.FromBase64String(). Decifralo. Ora hai la codifica UTF-8 della tua stringa originale, che puoi decodificare usando Encoding.UTF8.GetString().

Ricorda:

  • Encoding.UTF8 lavora per convertire le stringhe arbitrarie di byte-array (ma può convertire solo byte-array che contengono effettivi utf8-codifiche schiena).
  • Converti. [To/From] Base64String funziona per convertire arbitrari array di byte in stringhe (ma può solo convertire stringhe che contengono codifiche Base64 effettive).
+1

"A ogni domanda molto lunga, molto lunga, pubblicata in Stack Overflow, c'è sempre un risposta molto semplice che ti fa sentire molto stupido. " Albert Einstein. Grazie per la risposta, ha funzionato magnificamente. – Mandos

2

Convert.FromBase64String(string); si aspetta di ricevere una stringa generata dal Convert.ToBase64String(byte[]); passando una stringa arbitraria non funzionerà.

La soluzione più semplice è sostituire BinaryWriter e BinaryReader con un StreamWriter e un StreamReader e non eseguire alcuna conversione.

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


    //Create an Aes object 
    //with the specified key and IV. 

    using (Aes aesAlg = Aes.Create()) 
    { 
     aesAlg.GenerateIV(); 
     byte[] IV = aesAlg.IV; 
     //The Salt will be the first 8 bytes of the IV. 
     byte[] theSalt = new byte[8]; 
     Array.Copy(IV,theSalt,8); 
     //A key for AES is generated by expanding the password using the following method. 
     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
     byte[] aesKey = keyGen.GetBytes(16); 
     aesAlg.Key = aesKey; 

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

     // Create the streams used for encryption. 
     using (MemoryStream msEncrypt = new MemoryStream()) 
     { 
      //You can write the IV here and not need to do it later. 
      msEncrypt.Write(IV, 0, IV.Length); 

      using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) 
      { 
       using (StreamWriter swEncrypt = new StreamWriter (csEncrypt)) 
       {  
        //Write all data to the stream. 
        swEncrypt.Write(plainText); 
       } 
      } 

      //Move this outside of the using statement for CryptoStream so it is flushed and dipsoed. 
      return msEncrypt.ToArray(); 
     } 
    } 
} 

Inoltre, la funzione di decifratura è in realtà cercando di crittografare il testo una seconda volta, è necessario passare l'array di byte in al costruttore di msDecrypt e metterlo in modalità decrittazione.

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

    // Create an Aes object 
    // with the specified key and IV. 

    // Create the streams used for decryption. 

    using (Aes aesAlg = Aes.Create()) 
    { 
     aesAlg.Mode = CipherMode.CBC; 
     aesAlg.Padding = PaddingMode.PKCS7; 
     //Grab IV from ciphertext 
     byte[] IV = new byte[16]; 
     Array.Copy(cipherText,0,IV,0,16); 
     //Use the IV for the Salt 
     byte[] theSalt = new byte[8]; 
     Array.Copy(IV,theSalt,8); 
     Rfc2898DeriveBytes keyGen = new Rfc2898DeriveBytes(Key,theSalt); 
     byte[] aesKey = keyGen.GetBytes(16); 
     aesAlg.Key = aesKey; 

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

     //You can chain using statements like this to make the code easier to read. 
     using (MemoryStream msDecrypt = new MemoryStream(cipherText)) 
     using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) //Notice this is Read mode not Write mode. 
     using (StreamReader srDecrypt = new StreamReader(csDecrypt)) 
     { 
      //Decrypt the ciphertext 
      return srDecrypt.ReadToEnd(); 
     } 
    } 
} 

Ci possono essere altri errori con il codice, ma almeno questo ti porta sulla giusta strada.

+0

Grazie per la risposta. Ma penso che ci sia un problema con questo approccio (correggimi se sbaglio dal momento che non so molto sugli stream in C#) ... Nel processo di crittografia cripti la IV insieme al messaggio, quindi nel processo di decrittografia quando il metodo Array.Copy viene chiamato IV non sarà quello originale, ma saranno i primi 16 byte del testo cifrato. Quindi questo sarà un problema. Inoltre, quando decrittografando il testo cifrato, il tuo codice decodificherà la IV insieme al messaggio (facile da risolvere ma ancora non so se c'è un modo migliore). – Mandos

+0

C'è un modo in cui posso utilizzare StreamReader/Writer e ancora fornire la stessa funzionalità che il mio codice originale aveva per quanto riguarda la IV? – Mandos

+0

Sì, ho appena consolidato il codice, il lettore e lo scrittore di Stream non ha alcun effetto sulla parte IV. Usa un Binary Writer per la tua porzione IV, se vuoi (penso solo che sia un modo piuttosto inefficiente di farlo mentre stai creando extra byte [] in memoria dietro le scienze che non sono necessarie.). Basta cancellare 'msEncrypt.Write (IV, 0, IV.Length);' e poi rimetti nel tuo vecchio blocco in cui hai copiato l'IV. –

2

Guardando le tue linee

public byte[] EncryptStringToBytes_Aes(string plainText, string Key) 
byte[] plainTextInBytes = Convert.FromBase64String(plainText); 

arbitraria testo normale non sarà una stringa codificata di base 64. Anche se si suppone che sia di base 64 del testo codificato, il messaggio di errore indica che la lunghezza non è divisibile per 4

FormatException
La lunghezza di s, ignorando spazi vuoti, non è zero o un multiplo di 4. -or- Il formato di s non è valido. s contiene un carattere non di base 64, più di due caratteri di riempimento o un> carattere di spazio non bianco tra i caratteri di riempimento.

http://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs.110).aspx

Se è una stringa codificata base 64, è necessario pad è accorgingly

http://en.wikipedia.org/wiki/Base64

Problemi correlati