Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this wrapper around AesManaged ok?

I need to encrypt/decrypt some strings. I've build my wrapper class according to the msdn documentation but with some changes.

Since I want to encrypt/decrypt data with a given string/passphrase, I don't use AesManaged for creating a key. (The user should be able to encrypt/decrypt with a key he enters, and therefore I cannot use the key from AesManaged and I cannot save the key).

I instead create the key by using Rfc2898DeriveBytes (PBKDF2) with a given salt. The given salt is used since I do not store the key and I think because of this, the salt must be always the same.

I then create an IV, encrypt the given string and concatenate the IV and the encrypted string. This will then eventually got saved in a file. This means the IV gets save together with the encrypted data.

Questions:

  1. Is it ok to store the IV together with the encrypted data?
  2. Is there another way to create the key without using the same salt everytime(Based on a given passphrase)?
  3. Is this encryption done using AES128 or AES256?
  4. Will the IV be always 16 bytes, or can this change?

    static void Main(string[] args)
    {
        const string stringToEncrypt = "String to be encrypted/decrypted. Encryption is done via AesManaged";
        const string password = "m1Sup3rS3cre!Password";

        string encrypted = EncryptString(stringToEncrypt, password);
        string roundtrip = DecryptStringFromBytes_Aes(encrypted, password);

        Console.WriteLine("Original:   {0}", stringToEncrypt);
        Console.WriteLine("Round Trip: {0}", roundtrip);

        Console.ReadLine();
    }

    static string EncryptString(string plainText, string password)
    {
        string encryptedString;

        using (AesManaged aesAlg = new AesManaged())
        {
            aesAlg.Key = PasswordAsByte(password);
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            using (MemoryStream msEncrypt = new MemoryStream())
            {
                using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                {
                    using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))
                    {
                        swEncrypt.Write(plainText);
                    }
                    var encrypted = msEncrypt.ToArray();

                    encryptedString = Encoding.Default.GetString(aesAlg.IV);
                    encryptedString += Encoding.Default.GetString(encrypted);
                }
            }
        }
        return encryptedString;
    }

    static string DecryptStringFromBytes_Aes(string cipherText, string password)
    {
        using (AesManaged aesAlg = new AesManaged())
        {
            aesAlg.Key = PasswordAsByte(password);

            aesAlg.IV = Encoding.Default.GetBytes(cipherText).Take(16).ToArray();

            ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            var encryptedByteArray = Encoding.Default.GetBytes(cipherText).Skip(16).ToArray();

            using (MemoryStream msDecrypt = new MemoryStream(encryptedByteArray))
            {
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                    {
                        return srDecrypt.ReadToEnd();
                    }
                }
            }
        }
    }

    private static byte[] PasswordAsByte(string password)
    {
        byte[] salt = Encoding.Default.GetBytes("foobar42");
        Rfc2898DeriveBytes passwordBytes = new Rfc2898DeriveBytes(password, salt);

        return passwordBytes.GetBytes(32);
    }
like image 631
Manuel Avatar asked Feb 10 '23 14:02

Manuel


2 Answers

No, this is not okay.

1) You're using Encoding.Default in various places. Don't do that - it means you're at the whim of the platform you're on. Always use an explicit encoding, ideally UTF-8 in most cases.

2) You're using Encoding.GetString / Encoding.GetBytes to convert arbitrary binary data to a string and back. That's almost bound to lose data. (It happened to succeed on my machine, but it really depends on the encoding - and it's fundamentally a bad idea.) Encoding is designed for data which is inherently text data, and you're just applying an encoding one way or the other. Your encrypted data is inherently binary data. Use Convert.ToBase64String and Convert.FromBase64String instead.

For your other questions:

  • Yes, it's okay to store the IV with the encrypted data, as far as I know.
  • You could use the same approach for the password: generate a different salt each time, and store that with the encrypted text. Not sure whether that's generally recommended or not, I'm afraid.
  • I believe you're controlling whether the key size is 128 or 256 bits, with your call to passwordBytes.GetBytes(32) - that's a 256-bit key, so it's AES256.
  • I believe the IV size for AES is always 16 bytes (128 bits)
like image 62
Jon Skeet Avatar answered Feb 13 '23 02:02

Jon Skeet


Normally salt is used together with cryptographic hashing of say passwords to protect against dictionary attacks. To get the same kind of protection for symmetric encryption with AES you should use a random initialization vector. So when you encrypt create a random IV and prepend it to the message (in cleartext). When you decrypt get the IV from the encrypted message and use it to decrypt the message. Then the ciphertext of the same message encrypted with the same key will be different.

  • So, yes, it is OK to store the IV together with the encrypted data.

  • You do not need a different salt every time because the purpose of the random IV is similar in how salt makes dictionary attacks on hashes harder.

  • AES can use key sizes of 128, 192 or 256 bits so to use AES 256 you need a 256 bit key (32 bytes) which is what you use.

  • AES uses a 128 bit block which requires a 128 bit IV (or 16 bytes).

like image 36
Martin Liversage Avatar answered Feb 13 '23 04:02

Martin Liversage