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

I am currently working on implementing AES in C #. The encryption method has two parameters: a string and a password. I take the provided string and convert it to an array of bytes, so I can use it later to write data to the stream using BinaryWriter .

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

I have been trying to solve this problem the last couple of days. I read almost endless questions at stackoverflow.com and other sites, but I still don't know what is the most reliable way to solve this problem.

The lines that will be used in this program are limited to sentences (for example, "Something to encrypt") and numbers (for example, "12345").

Thanks in advance, here is the code that I have at the moment:

  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; } } } } } 
+6
source share
3 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 with an arbitrary string. Convert this to byte [] using Encoding.UTF8.GetBytes() . Encrypt it. The resulting byte [] can now be converted to a string using Convert.ToBase64String() .

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

Remember:

  • Encoding.UTF8 works to convert arbitrary strings to byte arrays (but it can only convert byte arrays containing actual UTF8 encodings).
  • Convert [To / From] Base64String works to convert arbitrary byte arrays to strings (but it can only convert strings containing actual Base64 encodings).
+6
source
Expected that

Convert.FromBase64String(string); will receive the line generated by Convert.ToBase64String(byte[]); passing in an arbitrary line will not work.

The simplest solution is to replace BinaryWriter and BinaryReader with StreamWriter and a StreamReader and not do any conversion at all.

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

In addition, your decryption function is actually trying to encrypt the text a second time, you need to pass an array of bytes to the msDecrypt constructor and put it in decryption mode.

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

There may be other errors in your code, but at least it will lead you to the right path.

+2
source

Looking at your lines

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

Custom plain text will not be base-64 encoded string. Even if it is assumed that base-64 encoded text, your error message indicates that the length is not divisible by 4

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

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

If it's a 64-bit base encoding, you need to skip it

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

+2
source

Source: https://habr.com/ru/post/976991/


All Articles