Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly and consistely get bytes from a string for AES encryption?

I am currently working on AES implementation in C#. The encryption method has two parameters: a string and a password. I am taking the supplied string and converting it to an array of bytes, so I can use it later for writing data to a stream with BinaryWriter.

The problem is that when I use Convert.FromBase64String(string) I get FormatException: Invalid length.and when I use Encoding.UTF8.GetBytes(string) my decryption method throws and invalid PKCS7.Padding exception.

I have been trying to solve this problem for the last couple of days. I have read near infinite questions in stackoverflow.com and other websites, but I still don't know what is the most reliable way to solve this problem.

Strings that will be used in this program are limited to sentences (ex. "Something to encrypt.") and numbers (ex. "12345").

Thank you in advance, here is the code I have at this point in time:

    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;
                }
            }   
        }
    }
}
like image 511
Geronimo Rodriguez Avatar asked Oct 20 '14 17:10

Geronimo Rodriguez


People also ask

How many bytes can AES encrypt?

AES has always 128-bit block size with 128,192 and 256-bit keyspaces. Therefore, you can encrypt 16-byte at a time if you are using ECB and CBC modes. By using CTR mode you can encrypt 1-bit to 128-bit.

How many bytes of data does AES 128 encrypt?

AES uses a 128-bit block size, in which data is divided into a four-by-four array containing 16 bytes. Since there are eight bits per byte, the total in each block is 128 bits. The size of the encrypted data remains the same: 128 bits of plaintext yields 128 bits of ciphertext.

How does AES encryption work step by step?

For encryption, each round consists of the following four steps: 1) Substitute bytes, 2) Shift rows, 3) Mix columns, and 4) Add round key. The last step consists of XORing the output of the previous three steps with four words from the key schedule.


2 Answers

You need to convert between bytes and strings before and after encryption/decryption. This is not the same operation, and you should not use the same method.

When encrypting you start out with an arbitrary string. Convert this to a byte[] using Encoding.UTF8.GetBytes(). Encrypt it. The resulting byte[] can now be converted to a string using Convert.ToBase64String().

When decrypting you now start out with a Base64 encoded string. Decode this to a byte[] using Convert.FromBase64String(). Decrypt it. You now have the UTF-8 encoding of your original string, which you can decode using Encoding.UTF8.GetString().

Remember:

  • Encoding.UTF8 works to convert arbitrary strings to byte-arrays (but it can only convert byte-arrays that contain actual UTF8-encodings back).
  • Convert.[To/From]Base64String works to convert arbitrary byte-arrays to strings (but it can only convert strings that contain actual Base64-encodings back).
like image 132
Rasmus Faber Avatar answered Oct 19 '22 12:10

Rasmus Faber


Looking at your lines

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

Arbitrary plain text will not be a base 64 encoded string. Even if it is supposed to be base 64 encoded text, your error message indicates that the length is not divisible by 4

FormatException
The length of s, ignoring white-space characters, is not zero or a multiple of 4. -or- The format of s is invalid. s contains a non-base-64 character, more than two padding characters, or a > non-white space-character among the padding characters.

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

If it is a base 64 encoded string, you need to pad it accorgingly

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

like image 37
Eric J. Avatar answered Oct 19 '22 12:10

Eric J.