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

百般思念 提交于 2019-12-04 03:22:22

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).

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

Convert.FromBase64String(string); is expected to receive a string generated by Convert.ToBase64String(byte[]); passing in a arbitrary string will not work.

The easiest solution is replace the BinaryWriter and BinaryReader with a 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();
        }
    }
}

Also, your decryption function is actually trying to encrypt the text a 2nd time, you need to pass the byte array in to the constructor of msDecrypt 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 with your code, but at least this gets you on the right track.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!