Как правильно и consistely получить байт из строки для шифрования AES?

в настоящее время я работаю над реализацией AES в C#. Метод шифрования имеет два параметра: строку и пароль. Я беру предоставленную строку и преобразую ее в массив байтов, поэтому я могу использовать ее позже для записи данных в поток с BinaryWriter.

проблема в том, что когда я использую Convert.FromBase64String(string) Я FormatException: Invalid length.и когда я использую Encoding.UTF8.GetBytes(string) мой метод дешифрования бросает и недопустимый PKCS7.Исключение заполнения.

Я пытался решить эту проблему для последние пару дней. Я прочитал почти бесконечные вопросы в stackoverflow.com и другие сайты, но я до сих пор не знаю, какой самый надежный способ решить эту проблему.

строки, которые будут использоваться в этой программе, ограничены предложениями (ex. - Что-нибудь зашифровать.") и цифры (например. "12345").

заранее спасибо, Вот код, который у меня в данный момент:

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

3 ответов


вам нужно преобразовать между байтами и строками до и после шифрования/дешифрования. Это не та же операция, и вы не должны использовать тот же метод.

при шифровании вы начинаете с произвольной строкой. Преобразуйте это в байт [], используя Encoding.UTF8.GetBytes(). Зашифровать его. Полученный байт[] теперь можно преобразовать в строку с помощью Convert.ToBase64String().

при расшифровке теперь вы начинаете с строки, закодированной в Base64. Декодируйте это в байт [], используя Convert.FromBase64String(). Расшифруй его. Теперь у вас есть кодировка UTF-8 вашей исходной строки, которую вы можете декодировать с помощью Encoding.UTF8.GetString().

помните:

  • кодировка.UTF8 работает для преобразования произвольных строк в байтовые массивы (но он может конвертировать только байтовые массивы, содержащие фактические кодировки UTF8).
  • преобразовать.[To/From]Base64String работает для преобразования произвольных байтовых массивов в строки (но он может конвертировать только строки, содержащие фактические кодировки Base64).

глядя на ваши строки

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

произвольный простой текст будет в кодировке base 64 строку. Даже если предполагается, что это кодированный текст base 64, ваше сообщение об ошибке указывает, что длина не делится на 4

FormatException
Длина s, игнорируя символы пробела, не равна нулю или кратна 4. -или- Недопустимый формат s. s содержит не базовый символ-64, более двух символов заполнения или a > non-white space-символ среди символов заполнения.

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

Если это базовая 64-кодированная строка, вам нужно заполнить ее accorgingly

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


получит строку, сгенерированную Convert.ToBase64String(byte[]); передача произвольной строки не будет работать.

самое простое решение-заменить BinaryWriter и BinaryReader С StreamWriter и a StreamReader и не делать никакого преобразования вообще.

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

кроме того, ваша функция дешифрования фактически пытается зашифровать текст во второй раз, вам нужно передать массив байтов в конструктор msDecrypt и поместите его в расшифровку режим.

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

могут быть и другие ошибки с вашим кодом, но, по крайней мере, это приведет вас на правильный путь.