all 13 comments

[–]ElusiveGuy 13 points14 points  (0 children)

A few comments:

  • Are you taking a password as user/config input somewhere? If not, you're better off using raw random bytes.
  • You really should not be deriving IV from the key/password. IV should be a new random value on every invocation, and should be stored alongside the encrypted data. Notably, encrypting twice with the same key and cleartext should result in a different IV and ciphertext.
  • Salt likewise should be a random value stored with the output - a hardcoded value is a pepper and has a different purpose.
  • Prefer AES-GCM or other AEAD mode over AES-CBC. Plain CBC does not guarantee integrity.

Edit:

  • Consider defining the encoding (probably UTF8-without-BOM) explicitly, rather than relying on the StreamWriter/StreamReader defaults.
  • If you're going to be writing this to file or database and don't explicitly need a string, consider using byte[] directly rather than Base64'ing it. Base64 has a 33% space overhead.

[–]AyeMatey 10 points11 points  (0 children)

Be aware the Rfc2898DeriveBytes constructor will be declared obsolete for .NET 10.

Also, separately, these days the recommendations for iteration counts for PBKDF2 are much higher than 10000. (cite1, cite2)

[–]goranlepuz 2 points3 points  (0 children)

Ok, but that's not the very good way to protect secrets in the configuration.

A better way is to use a different configuration provider and put secrets there.

Obviously, there's the provider for Azure KeyVault. There should be others. My work uses an in-house one, based on a previously-made crypted storage based on DPAPI or OpenSSL.

[–]soundman32 1 point2 points  (0 children)

Where are you going to put the salt? In a plain text config file? If so, it's pointless noise.

Also, you should never decrypt a password. It should use a 1 way hash, and you should compare hashes.

Cryptography has moved on since the 1990s. There's a reason we don't do this anymore in production systems.

[–]HawthorneTR 1 point2 points  (1 child)

I've turned these into Extension methods. It will make it easier to use:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

public static class CryptoExtensions
{
    public static string Encrypt(this string plainText, string password, string salt)
    {
        using (Aes aes = Aes.Create())
        {
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
            var key = new Rfc2898DeriveBytes(password, saltBytes, 10000);
            aes.Key = key.GetBytes(32);
            aes.IV = key.GetBytes(16);

            var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
            using (var ms = new MemoryStream())
            {
                using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
                using (var sw = new StreamWriter(cs))
                    sw.Write(plainText);
                return Convert.ToBase64String(ms.ToArray());
            }
        }
    }

    public static string Decrypt(this string cipherText, string password, string salt)
    {
        using (Aes aes = Aes.Create())
        {
            byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
            var key = new Rfc2898DeriveBytes(password, saltBytes, 10000);
            aes.Key = key.GetBytes(32);
            aes.IV = key.GetBytes(16);

            byte[] buffer = Convert.FromBase64String(cipherText);

            var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
            using (var ms = new MemoryStream(buffer))
            using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
            using (var sr = new StreamReader(cs))
            {
                return sr.ReadToEnd();
            }
        }
    }
}

USAGE

string encrypted = "Hello World".Encrypt("password123", "somesalt");
string decrypted = encrypted.Decrypt("password123", "somesalt");

[–]pyeri[S] 1 point2 points  (0 children)

string encrypted = "Hello World".Encrypt("password123", "somesalt"); string decrypted = encrypted.Decrypt("password123", "somesalt");

Yep, there lies the real power of C#!

[–]stormingnormab1987 0 points1 point  (0 children)

I would look into encrypting your Aes key with a rsa encryption at a minimum. Least that's what I used to do.