2014-10-20 26 views
6

Obecnie pracuję nad implementacją AES w języku C#. Metoda szyfrowania ma dwa parametry: ciąg znaków i hasło. Biorę podany ciąg i konwertuję go na tablicę bajtów, więc mogę użyć go później do zapisu danych do strumienia z BinaryWriter.Jak poprawnie i spójnie uzyskać bajty z ciągu znaków dla szyfrowania AES?

Problem polega na tym, że gdy używam Convert.FromBase64String(string), otrzymuję FormatException: Invalid length. i gdy używam Encoding.UTF8.GetBytes(string), moja metoda odszyfrowywania wyrzuca i jest niepoprawny wyjątek PKCS7.Padding.

Próbuję rozwiązać ten problem przez ostatnie kilka dni. Czytałem w pobliżu nieskończonych pytań w stackoverflow.com i innych witrynach internetowych, ale nadal nie wiem, co jest najbardziej niezawodnym sposobem rozwiązania tego problemu.

Łańcuchy, które będą używane w tym programie, są ograniczone do zdań (np. "Coś do zaszyfrowania") i numerów (np. "12345").

Dziękuję z góry, oto kod mam w tym momencie: oczekuje się, aby otrzymać ciąg generowany przez Convert.ToBase64String(byte[]); przechodzącej w dowolny tekst nie będzie działać

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

twoje 'DecryptStringFromBytes' nie zwraca ciąg znaków, dlaczego? –

+1

Najważniejszym pytaniem jest i pozostanie: co znajduje się w łańcuchu wejściowym, który ma odszyfrować. Mam na myśli, że jeśli jest to heksadecymal, to żadna odpowiedź nie będzie poprawna. Proszę podać przykład! –

+0

Dziękujemy za dane wejściowe. Przykłady ciągów, które już dodano w pytaniu! – Mandos

Odpowiedz

6

trzeba konwertować między bajtów i ciągi przed i po szyfrowania/deszyfrowania. To nie jest ta sama operacja i nie powinieneś używać tej samej metody.

Podczas szyfrowania rozpoczynasz od dowolnego ciągu. Przekonwertuj to na bajt [] używając Encoding.UTF8.GetBytes(). Zaszyfruj to. Wynikowy bajt [] można teraz przekonwertować na ciąg znaków za pomocą Convert.ToBase64String().

Po odszyfrowaniu rozpoczynasz teraz od zakodowanego łańcucha Base64. Odkodować to do bajtu [] przy użyciu Convert.FromBase64String(). Odszyfruj go. Teraz masz kodowanie UTF-8 oryginalnego łańcucha, który możesz dekodować, używając Encoding.UTF8.GetString().

Pamiętaj:

  • Encoding.UTF8 działa konwertować dowolne ciągi do bajt tablice (ale może konwertować tylko bajt tablic, które zawierają rzeczywiste kodowanie utf8-back).
  • Konwertuj. [Do/Od] Narzędzie Base64String służy do konwersji arbitralnych tablic bajtowych na łańcuchy (ale może tylko przekonwertować łańcuchy, które zawierają rzeczywiste kodowanie Base64).
+1

"Do każdego źle zredagowanego, bardzo długiego pytania zamieszczonego w Stack Overflow zawsze jest bardzo prosta odpowiedź, która sprawia, że ​​czujesz się bardzo głupio. " Albert Einstein. Dziękuję za odpowiedź, działało pięknie. – Mandos

2

Convert.FromBase64String(string);.

Najprostszym rozwiązaniem jest zastąpienie BinaryWriter i BinaryReader za pomocą StreamWriter i StreamReader i nie przeprowadzaj żadnej konwersji.

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

Również czynność deszyfrowanie jest rzeczywiście próbuje zaszyfrować tekst 2nd czasu, trzeba przekazać tablicę bajtów do konstruktora msDecrypt i umieścić go w trybie deszyfrowania.

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

Możliwe, że z Twoim kodem są inne błędy, ale przynajmniej pozwoli Ci to na właściwy krok.

+0

Dziękuję za odpowiedź. Ale myślę, że jest problem z tym podejściem (popraw mnie, jeśli się mylę, ponieważ nie wiem dużo o strumieniach w C#) ... W procesie szyfrowania szyfrujesz IV wraz z wiadomością, więc w proces odszyfrowywania, gdy metoda Array.Copy nazywa się IV, nie będzie oryginalna, ale będzie to pierwsze 16 bajtów zaszyfrowanego tekstu. To będzie problem. Również podczas odszyfrowywania zaszyfrowanego tekstu twój kod odszyfruje IV wraz z wiadomością (łatwe do rozwiązania, ale wciąż nie wiem, czy istnieje lepszy sposób). – Mandos

+0

Czy istnieje sposób, w jaki mogę używać StreamReader/Writer i nadal dostarczać tę samą funkcjonalność, co mój oryginalny kod w odniesieniu do IV? – Mandos

+0

Tak, właśnie skonsolidowałem twój kod, czytnik strumienia i program piszący nie ma wpływu na część IV. Po prostu użyj Binarnego Writera dla swojej części IV, jeśli chcesz (po prostu myślę, że jest to raczej nieefektywny sposób, gdy tworzysz dodatkowy bajt [] w pamięci za naukami, które są niepotrzebne.). Po prostu usuń 'msEncrypt.Write (IV, 0, IV.Length);' a następnie wróć do swojego starego bloku, w którym skopiowałeś IV. –

2

Patrząc na swoich liniach

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

arbitralny zwykły tekst nie będzie podstawa 64 zakodowany ciąg. Nawet jeśli to ma być podstawa 64 zakodowany tekst, wiadomość o błędzie wskazuje, że długość nie jest podzielna przez 4

FormatException
Długość s, ignorując znaki white-space, nie jest zerowy lub wielokrotność liczby 4. -lub- Format s jest nieprawidłowy. s zawiera znak non-base-64, więcej niż dwa znaki wypełnienia lub> nie-biały znak spacji wśród znaków dopełniających.

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

Jeśli jest to podstawa 64 zakodowany ciąg, trzeba pad to accorgingly

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