By NotDan


2008-10-14 17:07:32 8 Comments

How can I encrypt and decrypt a string in C#?

25 comments

@Gopal Reddy V 2015-02-19 11:22:03

Encryption

public string EncryptString(string inputString)
{
    MemoryStream memStream = null;
    try
    {
        byte[] key = { };
        byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 };
        string encryptKey = "aXb2uy4z"; // MUST be 8 characters
        key = Encoding.UTF8.GetBytes(encryptKey);
        byte[] byteInput = Encoding.UTF8.GetBytes(inputString);
        DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
        memStream = new MemoryStream();
        ICryptoTransform transform = provider.CreateEncryptor(key, IV);
        CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
        cryptoStream.Write(byteInput, 0, byteInput.Length);
        cryptoStream.FlushFinalBlock();
    }
    catch (Exception ex)
    {
        Response.Write(ex.Message);
    }
    return Convert.ToBase64String(memStream.ToArray());
}

Decryption:

public string DecryptString(string inputString)
{
    MemoryStream memStream = null;
    try
    {
        byte[] key = { };
        byte[] IV = { 12, 21, 43, 17, 57, 35, 67, 27 };
        string encryptKey = "aXb2uy4z"; // MUST be 8 characters
        key = Encoding.UTF8.GetBytes(encryptKey);
        byte[] byteInput = new byte[inputString.Length];
        byteInput = Convert.FromBase64String(inputString);
        DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
        memStream = new MemoryStream();
        ICryptoTransform transform = provider.CreateDecryptor(key, IV);
        CryptoStream cryptoStream = new CryptoStream(memStream, transform, CryptoStreamMode.Write);
        cryptoStream.Write(byteInput, 0, byteInput.Length);
        cryptoStream.FlushFinalBlock();
    }
    catch (Exception ex)
    {
        Response.Write(ex.Message);
    }

    Encoding encoding1 = Encoding.UTF8;
    return encoding1.GetString(memStream.ToArray());
}

@CodesInChaos 2015-02-19 21:29:42

-1 This is very weak. 1) DES is easy to brute force having a 56 bit key. 2) A key is binary, not UTF8. If the key consists of ASCII characters (likely in practice), this reduces the effective key size to 48 bits. 3) An IV should be different for each message 4) Lack of MAC leaves you open to active attacks, including padding oracles.

@Roland 2015-10-28 15:47:58

+1 OP had a very simple question, without requirement for maximum strenght, and this answer corresponds with that perfectly. At least I can use this because I also have a simple usage for encryption.

@Dieter Meemken 2015-12-14 09:12:22

A little bit of explenation would be nice...

@jbtule 2017-03-29 17:29:20

-1 @Roland as mentioned by CodesInChaos an IV needs to be different for each message, very simply, if it isn't, you are using the API incorrectly, hence this code should never be used. Period. Not to overshadow the 48bit key makes this decryptable to anyone without the key in merely a day, so this this is no longer encryption and thus does not answer the question.

@Roland 2017-03-29 20:31:59

@jbtule thanks, I will seriously consider the other answers when reviewing my encryption project.

@jbtule 2017-05-08 21:59:27

Security Warning: Do Not Use This Code See above Comment by @CodesInChaos

@Gil Cohen 2015-05-25 12:26:02

Disclaimer: This solution should only be used for data at rest that is not exposed to the public (for example - a configuration file or DB). Only in this scenario, the quick-and-dirty solution can be considered better than @jbtule's solution, due to lower maintanance.

Original post: I found jbtule's answer a bit complicated for a quick and dirty secured AES string encryption and Brett's answer had a bug with the Initialization Vector being a fixed value making it vulnerable to padding attacks, so I fixed Brett's code and added a random IV that is added to the chipered string, creating a different encrypted value each and every encryption of the same value:

Encryption:

public static string Encrypt(string clearText)
{            
    byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
    using (Aes encryptor = Aes.Create())
    {
        byte[] IV = new byte[15];
        rand.NextBytes(IV);
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(clearBytes, 0, clearBytes.Length);
                cs.Close();
            }
            clearText = Convert.ToBase64String(IV) + Convert.ToBase64String(ms.ToArray());
        }
    }
    return clearText;
}

Decryption:

public static string Decrypt(string cipherText)
{
    byte[] IV = Convert.FromBase64String(cipherText.Substring(0, 20));
    cipherText = cipherText.Substring(20).Replace(" ", "+");
    byte[] cipherBytes = Convert.FromBase64String(cipherText);
    using (Aes encryptor = Aes.Create())
    {
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, IV);
        encryptor.Key = pdb.GetBytes(32);
        encryptor.IV = pdb.GetBytes(16);
        using (MemoryStream ms = new MemoryStream())
        {
            using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(cipherBytes, 0, cipherBytes.Length);
                cs.Close();
            }
            cipherText = Encoding.Unicode.GetString(ms.ToArray());
        }
    }
    return cipherText;
}

Replace EncryptionKey with your key. In my implementation, the key is being saved in the configuration file (web.config\app.config) as you shouldn't save it hard coded. The configuration file should be also encrypted so the key won't be saved as clear text in it.

protected static string _Key = "";
protected static string EncryptionKey
{
    get
    {
        if (String.IsNullOrEmpty(_Key))
        {
            _Key = ConfigurationManager.AppSettings["AESKey"].ToString();
        }

        return _Key;
    }
}

@dub stylee 2016-03-18 16:57:21

While your Encrypt method generates a different value for every call even with the same plain text, the Substring(20) will be the same every time, right?

@Gil Cohen 2016-03-24 18:55:06

What do you mean "the same"? The decrypt function takes every value that was generated by the encryption function, breaks it apart to the initialization vector and the encrypted value, and decrypt it,

@dub stylee 2016-03-24 19:10:21

I didn't notice that the Encrypt generated a different IV each time. For some reason I was thinking the IV was the same every time, which would basically make it pointless.

@jbtule 2017-03-29 17:50:50

Security is complicated, and should not be quick and dirty. But ultimately -1 because you lose a lot of security with CBC and PKCS7 when you don't authenticate the cipher text.

@Gil Cohen 2017-03-30 12:15:51

I understand what you say @jbtule, but it's really all about risk management. If all you need to do is to store some confidential info locally and you want extra defense with AES, this solution might satisfy your needs.

@jbtule 2017-03-30 14:52:08

@GilCohen Well put a big disclaimer on this and say only use for data at rest, don't expose with a service and then you can claim risk management. however, your quick and dirty is just sloppy. For example why do you replace spaces with plus signs on just decrypt and not vice versa, is it because something else is modifying the ciphertext before you get? Like passing via a url query string, cookie or form variable, hmm, that sounds like a service, which is absolutely when you need to authenticate a cipher text.

@Gil Cohen 2017-08-06 12:28:15

@jbtule actually no, this is the encoding of Base64 function for some reason. This was indeed used for data at rest and I agree with your comment. I'll add it.

@oleksa 2018-10-11 07:31:01

Here is the sample how AES-GCM encryption/decryption can be done using Bouncy castle package.

I've found that sample when googled for possibility to decrypt data from GOlang crypto/aes api:

const (
    gcmBlockSize         = 16 // this is key size
    gcmTagSize           = 16 // this is mac
    gcmStandardNonceSize = 12 // this is nonce
)

func encrypt(data []byte, passphrase string) []byte {
    block, _ := aes.NewCipher([]byte(createHash(passphrase)))
    gcm, err := cipher.NewGCM(block)
    if err != nil {
        panic(err.Error())
    }
    nonce := make([]byte, gcm.NonceSize())
    if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
        panic(err.Error())
    }
    ciphertext := gcm.Seal(nonce, nonce, data, nil)
    return ciphertext
}

.Net sample works like a charm with key (256 bit), mac (128bit) and nonce (96 bit).

@Davit Tvildiani 2018-10-10 21:05:38

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

public class Program
{
    public static void Main()
    {
        var key = Encoding.UTF8.GetBytes("SUkbqO2ycDo7QwpR25kfgmC7f8CoyrZy");
        var data = Encoding.UTF8.GetBytes("testData");

        //Encrypt data
        var encrypted = CryptoHelper.EncryptData(data,key);

        //Decrypt data
        var decrypted = CryptoHelper.DecryptData(encrypted,key);

        //Display result
        Console.WriteLine(Encoding.UTF8.GetString(decrypted));
    }
}

public static class CryptoHelper
{
    public static byte[] EncryptData(byte[] data, byte[] key)
    {
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;
            using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
            {
                using (var msEncrypt = new MemoryStream())
                {
                    msEncrypt.Write(aesAlg.IV, 0, aesAlg.IV.Length);

                    using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        csEncrypt.Write(data, 0, data.Length);

                    return msEncrypt.ToArray();
                }
            }
        }

    }

    public static byte[] DecryptData(byte[] encrypted, byte[] key)
    {
        var iv = new byte[16];
        Buffer.BlockCopy(encrypted, 0, iv, 0, iv.Length);
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;
            using (var decryptor = aesAlg.CreateDecryptor(key, iv))
            {
                using (var msDecrypt = new MemoryStream(encrypted, iv.Length, encrypted.Length - iv.Length))
                {
                    using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (var resultStream = new MemoryStream())
                        {
                            csDecrypt.CopyTo(resultStream);
                            return resultStream.ToArray();
                        }
                    }
                }
            }
        }
    }
}

@Konamiman 2015-05-07 07:30:09

A good algorithm to securely hash data is BCrypt:

Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with increasing computation power.

There's a nice .NET implementation of BCrypt that is available also as a NuGet package.

@juFo 2015-09-28 14:17:41

yes, good nuget package!

@The1nk 2016-07-01 15:50:48

The question asks how to encrypt and decrypt a string. Unless I'm missing something huge -- how can you decrypt a string in BCrypt? BCrypt, despite its name, is a hashing function.

@Tamas Czinege 2008-10-14 17:27:30

Here is an example using RSA.

Important: There is a limit to the size of data you can encrypt with the RSA encryption, KeySize - MinimumPadding. e.g. 256 bytes (assuming 2048 bit key) - 42 bytes (min OEAP padding) = 214 bytes (max plaintext size)

Replace your_rsa_key with your RSA key.

var provider = new System.Security.Cryptography.RSACryptoServiceProvider();
provider.ImportParameters(your_rsa_key);

var encryptedBytes = provider.Encrypt(
    System.Text.Encoding.UTF8.GetBytes("Hello World!"), true);

string decryptedTest = System.Text.Encoding.UTF8.GetString(
    provider.Decrypt(encryptedBytes, true));

For more info, visit MSDN - RSACryptoServiceProvider

@Akash Kava 2009-11-05 17:36:51

Sorry to ask such a simple question but can anyone tell me where do I get RSA Key or how do i generate one?

@CodesInChaos 2011-07-17 13:35:56

Why RSA? RSA has its uses, but nothing indicated that this is one of them.

@Tamas Czinege 2011-07-26 23:35:04

@CodeInChaos: Because when I have provided this answer, almost 3 years ago, the question originally didn't indicate anything. That was added later. I have answered the original question. Look at the question history and see it for yourself.

@CodesInChaos 2011-07-27 09:08:30

Even in the original question there is no indication that RSA might be a good fit. Asymmetric encryption has its uses, but it's not the right choice as a default encryption. Your example code will fail for longer strings because the RSA class isn't designed for general purpose encryption. If you need the asymmetric features you should encrypt a symmetric key with RSA and encrypt the actual data with that symmetric key. So I still believe your answer is bad advice.

@Otto Kanellis 2014-02-16 08:46:23

I'm impressed, 70 votes for a wrong answer!!!, as CodesInChaos said for this type of encryption you need a Symmetric key, not an Assymetric.

@Tomer W 2015-02-22 12:32:43

It's not a wrong answer, just over-complicating with a huge overhead... use AES / any other Symmetric methods for better results.

@Brett 2010-05-07 19:41:33

EDIT 2013-Oct: Although I've edited this answer over time to address shortcomings, please see jbtule's answer for a more robust, informed solution.

https://stackoverflow.com/a/10366194/188474

Original Answer:

Here's a working example derived from the "RijndaelManaged Class" documentation and the MCTS Training Kit.

EDIT 2012-April: This answer was edited to pre-pend the IV per jbtule's suggestion and as illustrated here:

http://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.95%29.aspx

Good luck!

public class Crypto
{

    //While an app specific salt is not the best practice for
    //password based encryption, it's probably safe enough as long as
    //it is truly uncommon. Also too much work to alter this answer otherwise.
    private static byte[] _salt = __To_Do__("Add a app specific salt here");

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match.
    /// </summary>
    /// <param name="plainText">The text to encrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for encryption.</param>
    public static string EncryptStringAES(string plainText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        string outStr = null;                       // Encrypted string to return
        RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create a RijndaelManaged object
            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            // Create a decryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // prepend the IV
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.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);
                    }
                }
                outStr = Convert.ToBase64String(msEncrypt.ToArray());
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    /// <param name="cipherText">The text to decrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for decryption.</param>
    public static string DecryptStringAES(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create the streams used for decryption.                
            byte[] bytes = Convert.FromBase64String(cipherText);
            using (MemoryStream msDecrypt = new MemoryStream(bytes))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }
}

@user593912 2011-01-29 16:19:05

To Bret - hi thx for your example. Maybye one think - I had issue with key leng - I did modification with MD5, so if somebody will use your example in the feature pls use this for key normalization(or you can use other hash algoritm: HashAlgorithm hash = new MD5CryptoServiceProvider(); UnicodeEncoding UE = new UnicodeEncoding(); byte[] key = hash.ComputeHash(UE.GetBytes(encrypt_password)); ps:sorry for my english :) slinti

@nakhli 2011-12-18 20:17:14

what is the length of the result byte array (as a function of the plainText input length)? Thank you!

@Brett 2011-12-19 01:43:00

Block size is 16 bytes. The output byte length will be the same size as the input, rounded up to fill the block. e.g., an input string of 120 bytes will result in an output string of 8 blocks, or 128 bytes. See: en.wikipedia.org/wiki/Block_size_%28cryptography%29

@John Bubriski 2012-01-27 21:36:06

It looks like AES is the newer form of Rijndeal, and according to the MSDN AES should be used instead of Rijndeal: So use AesManaged instead? More info here

@John Bubriski 2012-01-30 22:02:56

I guess AES is not available in 2.0.

@jbtule 2012-04-24 22:54:55

The above code is not secure, it breaks the most basic rule of semantic security with aes, you should NEVER use the same IV more than once with the same key. This always gives an identical IV every time you use the same key.

@Brett 2012-04-27 02:03:59

@jbtule - Thanks. Please see my edits above. Deriving the key from the password would satisfy your concerns, I believe. The MCTS text says this about the IV: "Like the Key property, both the encryptor and decryptor must specify the same value. To avoid the overhead of transferring the IV securely between the encryptor and decryptor, you might choose to define the IV in your application statically or to derive this from the Key property." The code above does the latter -- derives the IV from the key.

@jbtule 2012-04-27 02:40:02

This is current MCTS text?! Avoiding just a single block of overhead by basically disabling a security feature of AES doesn't seem like something Microsoft such be training people to do. Microsofts docs for aes are correct at least.

@jbtule 2012-04-27 13:48:41

This is a good answer of how to use an IV, that MCTS text is irresponsible and nonstandard.

@Brett 2012-04-27 15:13:47

Thanks. I'll edit the answer to pre-pend the IV.

@wotney 2012-11-09 10:30:28

I am encrypting a string for use as a url parameter. The strings that this returns contains slashes and other symbols that mess up my URL, is there anything clever I can do to stop that from happening ?

@Brett 2012-11-09 17:22:28

@wotney: the returned string is base64, which may contain ?'s and /'s. I suggest that you UrlEncode the string: msdn.microsoft.com/en-us/library/…

@wotney 2012-11-10 10:57:43

Yup, simple solution. I was just wondering whether there was a setting in the RijndaelManaged class that could be invoked instead of using UrlEncode. Thanks though Brett !

@Afflatus 2013-04-08 03:03:54

@Brett using this method for the data that are going to be serialized and deserialized will cause an error since it can produce hexadecimal characters. how can I encode it to acceptable character set for XML?

@Brett 2013-04-08 17:40:11

@Elham It's difficult to respond. I suspect you are trying to write the xml directly and then de-serialize that xml back into an object. Perhaps you can refactor how you're writing your xml? Are you using the XmlSerializer (best approach)? Or perhaps you can simply wrap the base64 output with CDATA?. Surely the XmlSerializer will serialize and de-serialize string data that includes ampersands, etc?

@Afflatus 2013-04-09 02:28:16

@Brett I do employ XmlSerializer for the task. I had a varbinary element which was causing trouble I changed the type to varchar and the problem got solved. thanks anyway!

@CodesInChaos 2013-08-14 15:48:18

Using a salt in the key-derivation process wouldn't hurt. A constant isn't a good salt, just like a constant isn't a good IV.

@CodesInChaos 2013-08-14 15:50:08

Concerning the AES vs. Rijndael confusion: AES is a subset of Rijndael. If you use Rijndael with 128 bit blocks and either 128, 192 or 256 bit keys you're using AES.

@RemarkLima 2013-10-15 14:08:28

Just tried this and it's working great - However, in ASP.NET when the webserver restarts, the string aren't decrypted correctly, just decrypted yet look like a different encrypted string... Any ideas as to why?

@Brett 2013-10-15 20:06:30

@RemarkLima I'm afraid I don't have any suggestions. The intent of this algorithm was to avoid such problems.

@RemarkLima 2013-10-15 20:08:40

@Brett Sorry, found my mistake - Encrypting twice before saving to the database slaps forehead

@Rod 2014-10-06 18:45:13

So the _salt is the key you want to keep safe correct?

@Brett 2014-10-06 20:16:20

The salt adds a degree of obfuscation to prevent cracking. Recommend that you read jbtules examples below where the salt is generated.

@Ian Warburton 2015-12-18 14:26:16

RijndaelManaged has a method GenerateIV()

@Brett 2015-12-18 14:40:40

@IanWarburton We're simply referencing the IV property off of RijndaelManaged class. The documentation states that the IV is set to a new random value whenever you instantiate the class. See the remarks section of the documentation: bit.ly/1ObZRzg

@Foxman 2016-03-26 22:38:24

Isn't it a vulnerability that you transport the IV unencrypted? Doesn't this lose its point if an attacker knows the IV?

@Brett 2016-03-27 01:07:03

The IV and the key are both required to decrypt. A random IV provides "semantic security" -- prevents anyone watching traffic from discerning patterns. For example, if everyone's password were set to "change me" random IVs would nevertheless result in random cipher text, even if the key were the same.

@Farukh 2016-07-25 23:00:39

RijndaelManaged is not FIPS 140-2 compliant and might throw exception on DOD systems where FIPS is enabled.

@J.K. Harthoorn 2017-11-12 10:22:49

Could someone please explain me how to use this with an interger? I don't know how to use this and what to set as salt value. How do I save the int encrypted?

@Rahul Modi 2017-02-04 18:18:56

With the reference of Encrypt and Decrypt a String in c#, I found one of good solution :

static readonly string PasswordHash = "[email protected]@Sw0rd";
static readonly string SaltKey = "[email protected]&KEY";
static readonly string VIKey = "@1B2c3D4e5F6g7H8";

For Encrypt

public static string Encrypt(string plainText)
{
    byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

    byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
    var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
    var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));

    byte[] cipherTextBytes;

    using (var memoryStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
        {
            cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
            cryptoStream.FlushFinalBlock();
            cipherTextBytes = memoryStream.ToArray();
            cryptoStream.Close();
        }
        memoryStream.Close();
    }
    return Convert.ToBase64String(cipherTextBytes);
}

For Decrypt

public static string Decrypt(string encryptedText)
{
    byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
    byte[] keyBytes = new Rfc2898DeriveBytes(PasswordHash, Encoding.ASCII.GetBytes(SaltKey)).GetBytes(256 / 8);
    var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };

    var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
    var memoryStream = new MemoryStream(cipherTextBytes);
    var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
    byte[] plainTextBytes = new byte[cipherTextBytes.Length];

    int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
    memoryStream.Close();
    cryptoStream.Close();
    return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray());
}

@jbtule 2017-03-29 17:43:32

Hardcoded Salt and IV, and using an ASCII representation for them, this is all kinds of wrong.

@jbtule 2017-05-08 22:00:21

Security Warning: Do Not Use This Code See my comment above.

@Rahul Modi 2017-05-09 04:35:51

You know what Nothing is wrong/Unsafe/illogical in our logic industry.Though you might be good at some aspect, its dont mean that you are making wrong review to other post. In additionally, I have seen you some of post you are many of time posting negative blog in place of positive blog.However, your solution is good but never think its best. Life run on code.

@jbtule 2017-05-09 04:59:20

I apologize that I didn't spell this out. IV is not a key, and keeping it secret offers zero added security, and making it predictable loses a quite a bit of security. Hardcoding the IV is completely unreasonable/illogical/wrong to anyone that actually knows how to use AES-CBC encryption. Encoding.ASCII.GetBytes for data that is intended to add entropy to something that human chosen, is going to be way less then entropy than expected and is a very novice mistake. These are all things that are easily correctable, but you aren't, so my bold warning stays, due to security implications.

@Nikhil Vartak 2017-05-25 07:26:20

Rahul, chill down! Sit back, relax and think why all 3 comments from @jbtule got up-votes. He is talking something sensible in order to put you on right path. Nothing to feel offended. You are new to SO. You'll realize how it works eventually.

@James McLachlan 2017-01-04 23:15:09

An alternative to BouncyCastle for AES-GCM encryption is libsodium-net. It wraps the libsodium C library. One nice advantage is that it uses the AES-NI extension in CPUs for very fast encryption. The down side is that it won't work at all if the CPU doesn't have the extension. There's no software fall back.

@Ashkan Sirous 2016-10-26 09:19:28

Encryption is a very common matter in programming. I think it is better to install a package to do the task for you. Maybe a simple open source Nuget project like Simple Aes Encryption

The key is in the config file and therefore it is easy to change in the production environment, and I don't see any drawbacks

<MessageEncryption>
  <EncryptionKey KeySize="256" Key="3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8="/>
</MessageEncryption>

@jbtule 2017-03-29 17:13:46

Big drawback is that it's not authenticated encryption.

@josedbaez 2016-08-19 08:30:14

To support mattmanser answer. Here's an example using MachineKey class to encrypt/decrypt URL safe values.

Something to bear in mind, as mentioned before, this will use Machine config settings (https://msdn.microsoft.com/en-us/library/ff649308.aspx). You can set encryption and decryption key/algorithm manually (you might need this specially if your site is running on multiple servers) in web.config file. You can generate keys from IIS (see here: https://blogs.msdn.microsoft.com/vijaysk/2009/05/13/iis-7-tip-10-you-can-generate-machine-keys-from-the-iis-manager/) or can use an online machine key generator like: http://www.developerfusion.com/tools/generatemachinekey/

    private static readonly UTF8Encoding Encoder = new UTF8Encoding();

    public static string Encrypt(string unencrypted)
    {
        if (string.IsNullOrEmpty(unencrypted)) 
            return string.Empty;

        try
        {
            var encryptedBytes = MachineKey.Protect(Encoder.GetBytes(unencrypted));

            if (encryptedBytes != null && encryptedBytes.Length > 0)
                return HttpServerUtility.UrlTokenEncode(encryptedBytes);    
        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }

    public static string Decrypt(string encrypted)
    {
        if (string.IsNullOrEmpty(encrypted)) 
            return string.Empty;

        try
        {
            var bytes = HttpServerUtility.UrlTokenDecode(encrypted);
            if (bytes != null && bytes.Length > 0)
            {
                var decryptedBytes = MachineKey.Unprotect(bytes);
                if(decryptedBytes != null && decryptedBytes.Length > 0)
                    return Encoder.GetString(decryptedBytes);
            }

        }
        catch (Exception)
        {
            return string.Empty;
        }

        return string.Empty;
    }

@Skull 2016-05-25 06:45:26

AES Algorithm:

public static class CryptographyProvider
    {
        public static string EncryptString(string plainText, out string Key)
        {
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");

            using (Aes _aesAlg = Aes.Create())
            {
                Key = Convert.ToBase64String(_aesAlg.Key);
                ICryptoTransform _encryptor = _aesAlg.CreateEncryptor(_aesAlg.Key, _aesAlg.IV);

                using (MemoryStream _memoryStream = new MemoryStream())
                {
                    _memoryStream.Write(_aesAlg.IV, 0, 16);
                    using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, _encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter _streamWriter = new StreamWriter(_cryptoStream))
                        {
                            _streamWriter.Write(plainText);
                        }
                        return Convert.ToBase64String(_memoryStream.ToArray());
                    }
                }
            }
        }
        public static string DecryptString(string cipherText, string Key)
        {

            if (string.IsNullOrEmpty(cipherText))
                throw new ArgumentNullException("cipherText");
            if (string.IsNullOrEmpty(Key))
                throw new ArgumentNullException("Key");

            string plaintext = null;

            byte[] _initialVector = new byte[16];
            byte[] _Key = Convert.FromBase64String(Key);
            byte[] _cipherTextBytesArray = Convert.FromBase64String(cipherText);
            byte[] _originalString = new byte[_cipherTextBytesArray.Length - 16];

            Array.Copy(_cipherTextBytesArray, 0, _initialVector, 0, _initialVector.Length);
            Array.Copy(_cipherTextBytesArray, 16, _originalString, 0, _cipherTextBytesArray.Length - 16);

            using (Aes _aesAlg = Aes.Create())
            {
                _aesAlg.Key = _Key;
                _aesAlg.IV = _initialVector;
                ICryptoTransform decryptor = _aesAlg.CreateDecryptor(_aesAlg.Key, _aesAlg.IV);

                using (MemoryStream _memoryStream = new MemoryStream(_originalString))
                {
                    using (CryptoStream _cryptoStream = new CryptoStream(_memoryStream, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader _streamReader = new StreamReader(_cryptoStream))
                        {
                            plaintext = _streamReader.ReadToEnd();
                        }
                    }
                }
            }
            return plaintext;
        }
    }

@Artjom B. 2016-06-10 08:23:57

1) The IV is passed in as a parameter which means that the developer has to do the IV management and they will get it wrong. Instead, the IV should be randomly generated and stored alongside of the ciphertext. 2) Since the IV and key will change between multiple executions of the Encryption method and does not persist, there is no need to have this method at all other than for demonstration purposes. 3) There is no authentication of the ciphertext, so attackers may manipulate it without you detecting it (see: padding oracle attack).

@Skull 2016-06-10 09:56:50

hai @ArtjomB. developer need not to worry about iv management because it ll be get generated and added along with the encrypted string.

@Artjom B. 2016-06-10 10:03:52

I have to disagree. The IV is stored in the _iv class variable and not written into the ciphertext. So, how do you think the receiver will know the key and IV? They would have to be distributed in some other way. Since the IV is not supposed to be secret, it should be randomly generated for each encryption and distributed along with the ciphertext.

@Skull 2016-06-10 10:11:57

@Skull 2016-06-15 12:37:32

1)In the above link, you can get the way of implementing aes without ,need not to worry about iv management,because iv is also get encrypted along with string. 2)since function you refers contains private access modifier you are not be able to call it outside. Inorder to encrypt we can be able to use only Cryptographyclass.Encrytion("SAMPLEstring") function

@nerdybeardo 2012-11-22 11:22:32

BouncyCastle is a great Crypto library for .NET, it's available as a Nuget package for install into your projects. I like it a lot more than what's currently available in the System.Security.Cryptography library. It gives you a lot more options in terms of available algorithms, and provides more modes for those algorithms.

This is an example of an implementation of TwoFish, which was written by Bruce Schneier (hero to all us paranoid people out there). It's a symmetric algorithm like the Rijndael (aka AES). It was one of the three finalists for the AES standard and sibling to another famous algorithm written by Bruce Schneier called BlowFish.

First thing with bouncycastle is to create an encryptor class, this will make it easier to implement other block ciphers within the library. The following encryptor class takes in a generic argument T where T implements IBlockCipher and has a default constructor.

UPDATE: Due to popular demand I have decided to implement generating a random IV as well as include an HMAC into this class. Although from a style perspective this goes against the SOLID principle of single responsibility, because of the nature of what this class does I reniged. This class will now take two generic parameters, one for the cipher and one for the digest. It automatically generates the IV using RNGCryptoServiceProvider to provide good RNG entropy, and allows you to use whatever digest algorithm you want from BouncyCastle to generate the MAC.

using System;
using System.Security.Cryptography;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Crypto.Parameters;

public sealed class Encryptor<TBlockCipher, TDigest>
    where TBlockCipher : IBlockCipher, new()
    where TDigest : IDigest, new()
{
    private Encoding encoding;

    private IBlockCipher blockCipher;

    private BufferedBlockCipher cipher;

    private HMac mac;

    private byte[] key;

    public Encryptor(Encoding encoding, byte[] key, byte[] macKey)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, new Pkcs7Padding());
    }

    public Encryptor(Encoding encoding, byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.encoding = encoding;
        this.key = key;
        this.Init(key, macKey, padding);
    }

    private void Init(byte[] key, byte[] macKey, IBlockCipherPadding padding)
    {
        this.blockCipher = new CbcBlockCipher(new TBlockCipher());
        this.cipher = new PaddedBufferedBlockCipher(this.blockCipher, padding);
        this.mac = new HMac(new TDigest());
        this.mac.Init(new KeyParameter(macKey));
    }

    public string Encrypt(string plain)
    {
        return Convert.ToBase64String(EncryptBytes(plain));
    }

    public byte[] EncryptBytes(string plain)
    {
        byte[] input = this.encoding.GetBytes(plain);

        var iv = this.GenerateIV();

        var cipher = this.BouncyCastleCrypto(true, input, new ParametersWithIV(new KeyParameter(key), iv));
        byte[] message = CombineArrays(iv, cipher);

        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        byte[] digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(digest, 0);

        var result = CombineArrays(digest, message);
        return result;
    }

    public byte[] DecryptBytes(byte[] bytes)
    {
        // split the digest into component parts
        var digest = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        var message = new byte[bytes.Length - digest.Length];
        var iv = new byte[this.blockCipher.GetBlockSize()];
        var cipher = new byte[message.Length - iv.Length];

        Buffer.BlockCopy(bytes, 0, digest, 0, digest.Length);
        Buffer.BlockCopy(bytes, digest.Length, message, 0, message.Length);
        if (!IsValidHMac(digest, message))
        {
            throw new CryptoException();
        }

        Buffer.BlockCopy(message, 0, iv, 0, iv.Length);
        Buffer.BlockCopy(message, iv.Length, cipher, 0, cipher.Length);

        byte[] result = this.BouncyCastleCrypto(false, cipher, new ParametersWithIV(new KeyParameter(key), iv));
        return result;
    }

    public string Decrypt(byte[] bytes)
    {
        return this.encoding.GetString(DecryptBytes(bytes));
    }

    public string Decrypt(string cipher)
    {
        return this.Decrypt(Convert.FromBase64String(cipher));
    }

    private bool IsValidHMac(byte[] digest, byte[] message)
    {
        this.mac.Reset();
        this.mac.BlockUpdate(message, 0, message.Length);
        byte[] computed = new byte[this.mac.GetUnderlyingDigest().GetDigestSize()];
        this.mac.DoFinal(computed, 0);

        return AreEqual(digest,computed);
    }

    private static bool AreEqual(byte [] digest, byte[] computed)
    {
        if(digest.Length != computed.Length)
        {
            return false;
        }

        int result = 0;
        for (int i = 0; i < digest.Length; i++)
        {
            // compute equality of all bytes before returning.
            //   helps prevent timing attacks: 
            //   https://codahale.com/a-lesson-in-timing-attacks/
            result |= digest[i] ^ computed[i];
        }

        return result == 0;
    }

    private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, ICipherParameters parameters)
    {
        try
        {
            cipher.Init(forEncrypt, parameters);

            return this.cipher.DoFinal(input);
        }
        catch (CryptoException)
        {
            throw;
        }
    }

    private byte[] GenerateIV()
    {
        using (var provider = new RNGCryptoServiceProvider())
        {
            // 1st block
            byte[] result = new byte[this.blockCipher.GetBlockSize()];
            provider.GetBytes(result);

            return result;
        }
    }

    private static byte[] CombineArrays(byte[] source1, byte[] source2)
    {
        byte[] result = new byte[source1.Length + source2.Length];
        Buffer.BlockCopy(source1, 0, result, 0, source1.Length);
        Buffer.BlockCopy(source2, 0, result, source1.Length, source2.Length);

        return result;
    }
}

Next just call the encrypt and decrypt methods on the new class, here's the example using twofish:

var encrypt = new Encryptor<TwofishEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);

string cipher = encrypt.Encrypt("TEST");   
string plainText = encrypt.Decrypt(cipher);

It's just as easy to substitute another block cipher like TripleDES:

var des = new Encryptor<DesEdeEngine, Sha1Digest>(Encoding.UTF8, key, hmacKey);

string cipher = des.Encrypt("TEST");
string plainText = des.Decrypt(cipher);

Finally if you want to use AES with SHA256 HMAC you can do the following:

var aes = new Encryptor<AesEngine, Sha256Digest>(Encoding.UTF8, key, hmacKey);

cipher = aes.Encrypt("TEST");
plainText = aes.Decrypt(cipher);

The hardest part about encryption actually deals with the keys and not the algorithms. You'll have to think about where you store your keys, and if you have to, how you exchange them. These algorithms have all withstood the test of time, and are extremely hard to break. Someone who wants to steal information from you isn't going to spend eternity doing cryptanalysis on your messages, they're going to try to figure out what or where your key is. So #1 choose your keys wisely, #2 store them in a safe place, if you use a web.config and IIS then you can encrypt parts of the the web.config, and finally if you have to exchange keys make sure that your protocol for exchanging the key is secure.

Update 2 Changed compare method to mitigate against timing attacks. See more info here http://codahale.com/a-lesson-in-timing-attacks/ . Also updated to default to PKCS7 padding and added new constructor to allow end user the ability to choose which padding they would like to use. Thanks @CodesInChaos for the suggestions.

@CodesInChaos 2013-08-14 16:47:07

1) That class is pretty annoying to use since you put the burden of IV management on the user who will almost certainly get it wrong. 2) Lack of MAC makes this vulnerable to padding oracles.

@CodesInChaos 2014-03-10 17:08:29

1) Your padding looks broken to me. You add a zero padding and don't remove it. Zero padding is a bad idea since it can't be removed reliably. Use PKCS#7 padding instead. I'd expect the bouncycastle encryption/decryption function to already support this. 2) You should use a constant time comparison to validate the MAC, not SequenceEqual. This avoids a timing side-channel that leaks how long a prefix of the presented MAC and the actual MAC match.

@nerdybeardo 2014-03-11 10:10:08

@CodesInChaos I agree, thanks for checking this I've made an edit to resolve these couple issues. – nerdybeardo

@Terkhos 2015-07-24 15:35:50

great answer, just one question....what would be the key and hmacKey, i'm new to crypto..thanks !

@nerdybeardo 2015-07-27 01:07:44

@Terkhos You should use a secure random number generator for generating keys like RNGCryptoServiceProvider, you should never use a passphrase or something predictable. You should also use the maximum length that the algorithm will provide for example AES 256 uses a key size that's 256 bits in length so 32 random bytes would be best, HMAC key sizes are typically are based on the size of the algorithm for example SHA2 (256) a 256 bit key generated by a secure random number generator would suffice. Change keys often! The more often the better!

@ps2goat 2016-05-21 13:35:05

does the HMAC class come from System.Security.Cryptography or Org.BouncyCastle.Crypto.Macs?

@maksymiuk 2017-01-14 03:48:23

I have a question about this one, must it really use two keys? I understand they do different things, but can both keys perhaps be combined into one or is that bad practice? How come i cannot find anywhere else on the internet where an encryption takes two keys?

@jbtule 2012-04-28 18:07:48

Modern Examples of Symmetric Authenticated Encryption of a string.

The general best practice for symmetric encryption is to use Authenticated Encryption with Associated Data (AEAD), however this isn't a part of the standard .net crypto libraries. So the first example uses AES256 and then HMAC256, a two step Encrypt then MAC, which requires more overhead and more keys.

The second example uses the simpler practice of AES256-GCM using the open source Bouncy Castle (via nuget).

Both examples have a main function that takes secret message string, key(s) and an optional non-secret payload and return and authenticated encrypted string optionally prepended with the non-secret data. Ideally you would use these with 256bit key(s) randomly generated see NewKey().

Both examples also have a helper methods that use a string password to generate the keys. These helper methods are provided as a convenience to match up with other examples, however they are far less secure because the strength of the password is going to be far weaker than a 256 bit key.

Update: Added byte[] overloads, and only the Gist has the full formatting with 4 spaces indent and api docs due to StackOverflow answer limits.


.NET Built-in Encrypt(AES)-Then-MAC(HMAC) [Gist]

/*
 * This work (Modern Encryption of a String C#, by James Tuley), 
 * identified by James Tuley, is free of known copyright restrictions.
 * https://gist.github.com/4336842
 * http://creativecommons.org/publicdomain/mark/1.0/ 
 */

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

namespace Encryption
{
  public static class AESThenHMAC
  {
    private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();

    //Preconfigured Encryption Parameters
    public static readonly int BlockBitSize = 128;
    public static readonly int KeyBitSize = 256;

    //Preconfigured Password Key Derivation Parameters
    public static readonly int SaltBitSize = 64;
    public static readonly int Iterations = 10000;
    public static readonly int MinPasswordLength = 12;

    /// <summary>
    /// Helper that generates a random key on each call.
    /// </summary>
    /// <returns></returns>
    public static byte[] NewKey()
    {
      var key = new byte[KeyBitSize / 8];
      Random.GetBytes(key);
      return key;
    }

    /// <summary>
    /// Simple Encryption (AES) then Authentication (HMAC) for a UTF8 Message.
    /// </summary>
    /// <param name="secretMessage">The secret message.</param>
    /// <param name="cryptKey">The crypt key.</param>
    /// <param name="authKey">The auth key.</param>
    /// <param name="nonSecretPayload">(Optional) Non-Secret Payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
    /// <remarks>
    /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize +  HMac-Tag(32)) * 1.33 Base64
    /// </remarks>
    public static string SimpleEncrypt(string secretMessage, byte[] cryptKey, byte[] authKey,
                       byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncrypt(plainText, cryptKey, authKey, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }

    /// <summary>
    /// Simple Authentication (HMAC) then Decryption (AES) for a secrets UTF8 Message.
    /// </summary>
    /// <param name="encryptedMessage">The encrypted message.</param>
    /// <param name="cryptKey">The crypt key.</param>
    /// <param name="authKey">The auth key.</param>
    /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    public static string SimpleDecrypt(string encryptedMessage, byte[] cryptKey, byte[] authKey,
                       int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecrypt(cipherText, cryptKey, authKey, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    /// <summary>
    /// Simple Encryption (AES) then Authentication (HMAC) of a UTF8 message
    /// using Keys derived from a Password (PBKDF2).
    /// </summary>
    /// <param name="secretMessage">The secret message.</param>
    /// <param name="password">The password.</param>
    /// <param name="nonSecretPayload">The non secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">password</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// Adds additional non secret payload for key generation parameters.
    /// </remarks>
    public static string SimpleEncryptWithPassword(string secretMessage, string password,
                             byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }

    /// <summary>
    /// Simple Authentication (HMAC) and then Descryption (AES) of a UTF8 Message
    /// using keys derived from a password (PBKDF2). 
    /// </summary>
    /// <param name="encryptedMessage">The encrypted message.</param>
    /// <param name="password">The password.</param>
    /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// </remarks>
    public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
                             int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] cryptKey, byte[] authKey, byte[] nonSecretPayload = null)
    {
      //User Error Checks
      if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "cryptKey");

      if (authKey == null || authKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "authKey");

      if (secretMessage == null || secretMessage.Length < 1)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      //non-secret payload optional
      nonSecretPayload = nonSecretPayload ?? new byte[] { };

      byte[] cipherText;
      byte[] iv;

      using (var aes = new AesManaged
      {
        KeySize = KeyBitSize,
        BlockSize = BlockBitSize,
        Mode = CipherMode.CBC,
        Padding = PaddingMode.PKCS7
      })
      {

        //Use random IV
        aes.GenerateIV();
        iv = aes.IV;

        using (var encrypter = aes.CreateEncryptor(cryptKey, iv))
        using (var cipherStream = new MemoryStream())
        {
          using (var cryptoStream = new CryptoStream(cipherStream, encrypter, CryptoStreamMode.Write))
          using (var binaryWriter = new BinaryWriter(cryptoStream))
          {
            //Encrypt Data
            binaryWriter.Write(secretMessage);
          }

          cipherText = cipherStream.ToArray();
        }

      }

      //Assemble encrypted message and add authentication
      using (var hmac = new HMACSHA256(authKey))
      using (var encryptedStream = new MemoryStream())
      {
        using (var binaryWriter = new BinaryWriter(encryptedStream))
        {
          //Prepend non-secret payload if any
          binaryWriter.Write(nonSecretPayload);
          //Prepend IV
          binaryWriter.Write(iv);
          //Write Ciphertext
          binaryWriter.Write(cipherText);
          binaryWriter.Flush();

          //Authenticate all data
          var tag = hmac.ComputeHash(encryptedStream.ToArray());
          //Postpend tag
          binaryWriter.Write(tag);
        }
        return encryptedStream.ToArray();
      }

    }

    public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] cryptKey, byte[] authKey, int nonSecretPayloadLength = 0)
    {

      //Basic Usage Error Checks
      if (cryptKey == null || cryptKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("CryptKey needs to be {0} bit!", KeyBitSize), "cryptKey");

      if (authKey == null || authKey.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("AuthKey needs to be {0} bit!", KeyBitSize), "authKey");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      using (var hmac = new HMACSHA256(authKey))
      {
        var sentTag = new byte[hmac.HashSize / 8];
        //Calculate Tag
        var calcTag = hmac.ComputeHash(encryptedMessage, 0, encryptedMessage.Length - sentTag.Length);
        var ivLength = (BlockBitSize / 8);

        //if message length is to small just return null
        if (encryptedMessage.Length < sentTag.Length + nonSecretPayloadLength + ivLength)
          return null;

        //Grab Sent Tag
        Array.Copy(encryptedMessage, encryptedMessage.Length - sentTag.Length, sentTag, 0, sentTag.Length);

        //Compare Tag with constant time comparison
        var compare = 0;
        for (var i = 0; i < sentTag.Length; i++)
          compare |= sentTag[i] ^ calcTag[i]; 

        //if message doesn't authenticate return null
        if (compare != 0)
          return null;

        using (var aes = new AesManaged
        {
          KeySize = KeyBitSize,
          BlockSize = BlockBitSize,
          Mode = CipherMode.CBC,
          Padding = PaddingMode.PKCS7
        })
        {

          //Grab IV from message
          var iv = new byte[ivLength];
          Array.Copy(encryptedMessage, nonSecretPayloadLength, iv, 0, iv.Length);

          using (var decrypter = aes.CreateDecryptor(cryptKey, iv))
          using (var plainTextStream = new MemoryStream())
          {
            using (var decrypterStream = new CryptoStream(plainTextStream, decrypter, CryptoStreamMode.Write))
            using (var binaryWriter = new BinaryWriter(decrypterStream))
            {
              //Decrypt Cipher Text from Message
              binaryWriter.Write(
                encryptedMessage,
                nonSecretPayloadLength + iv.Length,
                encryptedMessage.Length - nonSecretPayloadLength - iv.Length - sentTag.Length
              );
            }
            //Return Plain Text
            return plainTextStream.ToArray();
          }
        }
      }
    }

    public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
    {
      nonSecretPayload = nonSecretPayload ?? new byte[] {};

      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (secretMessage == null || secretMessage.Length ==0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var payload = new byte[((SaltBitSize / 8) * 2) + nonSecretPayload.Length];

      Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
      int payloadIndex = nonSecretPayload.Length;

      byte[] cryptKey;
      byte[] authKey;
      //Use Random Salt to prevent pre-generated weak password attacks.
      using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
      {
        var salt = generator.Salt;

        //Generate Keys
        cryptKey = generator.GetBytes(KeyBitSize / 8);

        //Create Non Secret Payload
        Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
        payloadIndex += salt.Length;
      }

      //Deriving separate key, might be less efficient than using HKDF, 
      //but now compatible with RNEncryptor which had a very similar wireformat and requires less code than HKDF.
      using (var generator = new Rfc2898DeriveBytes(password, SaltBitSize / 8, Iterations))
      {
        var salt = generator.Salt;

        //Generate Keys
        authKey = generator.GetBytes(KeyBitSize / 8);

        //Create Rest of Non Secret Payload
        Array.Copy(salt, 0, payload, payloadIndex, salt.Length);
      }

      return SimpleEncrypt(secretMessage, cryptKey, authKey, payload);
    }

    public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cryptSalt = new byte[SaltBitSize / 8];
      var authSalt = new byte[SaltBitSize / 8];

      //Grab Salt from Non-Secret Payload
      Array.Copy(encryptedMessage, nonSecretPayloadLength, cryptSalt, 0, cryptSalt.Length);
      Array.Copy(encryptedMessage, nonSecretPayloadLength + cryptSalt.Length, authSalt, 0, authSalt.Length);

      byte[] cryptKey;
      byte[] authKey;

      //Generate crypt key
      using (var generator = new Rfc2898DeriveBytes(password, cryptSalt, Iterations))
      {
        cryptKey = generator.GetBytes(KeyBitSize / 8);
      }
      //Generate auth key
      using (var generator = new Rfc2898DeriveBytes(password, authSalt, Iterations))
      {
        authKey = generator.GetBytes(KeyBitSize / 8);
      }

      return SimpleDecrypt(encryptedMessage, cryptKey, authKey, cryptSalt.Length + authSalt.Length + nonSecretPayloadLength);
    }
  }
}

Bouncy Castle AES-GCM [Gist]

/*
 * This work (Modern Encryption of a String C#, by James Tuley), 
 * identified by James Tuley, is free of known copyright restrictions.
 * https://gist.github.com/4336842
 * http://creativecommons.org/publicdomain/mark/1.0/ 
 */

using System;
using System.IO;
using System.Text;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
namespace Encryption
{

  public static class AESGCM
  {
    private static readonly SecureRandom Random = new SecureRandom();

    //Preconfigured Encryption Parameters
    public static readonly int NonceBitSize = 128;
    public static readonly int MacBitSize = 128;
    public static readonly int KeyBitSize = 256;

    //Preconfigured Password Key Derivation Parameters
    public static readonly int SaltBitSize = 128;
    public static readonly int Iterations = 10000;
    public static readonly int MinPasswordLength = 12;


    /// <summary>
    /// Helper that generates a random new key on each call.
    /// </summary>
    /// <returns></returns>
    public static byte[] NewKey()
    {
      var key = new byte[KeyBitSize / 8];
      Random.NextBytes(key);
      return key;
    }

    /// <summary>
    /// Simple Encryption And Authentication (AES-GCM) of a UTF8 string.
    /// </summary>
    /// <param name="secretMessage">The secret message.</param>
    /// <param name="key">The key.</param>
    /// <param name="nonSecretPayload">Optional non-secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">Secret Message Required!;secretMessage</exception>
    /// <remarks>
    /// Adds overhead of (Optional-Payload + BlockSize(16) + Message +  HMac-Tag(16)) * 1.33 Base64
    /// </remarks>
    public static string SimpleEncrypt(string secretMessage, byte[] key, byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncrypt(plainText, key, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }


    /// <summary>
    /// Simple Decryption & Authentication (AES-GCM) of a UTF8 Message
    /// </summary>
    /// <param name="encryptedMessage">The encrypted message.</param>
    /// <param name="key">The key.</param>
    /// <param name="nonSecretPayloadLength">Length of the optional non-secret payload.</param>
    /// <returns>Decrypted Message</returns>
    public static string SimpleDecrypt(string encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrEmpty(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecrypt(cipherText, key, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    /// <summary>
    /// Simple Encryption And Authentication (AES-GCM) of a UTF8 String
    /// using key derived from a password (PBKDF2).
    /// </summary>
    /// <param name="secretMessage">The secret message.</param>
    /// <param name="password">The password.</param>
    /// <param name="nonSecretPayload">The non secret payload.</param>
    /// <returns>
    /// Encrypted Message
    /// </returns>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// Adds additional non secret payload for key generation parameters.
    /// </remarks>
    public static string SimpleEncryptWithPassword(string secretMessage, string password,
                             byte[] nonSecretPayload = null)
    {
      if (string.IsNullOrEmpty(secretMessage))
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var plainText = Encoding.UTF8.GetBytes(secretMessage);
      var cipherText = SimpleEncryptWithPassword(plainText, password, nonSecretPayload);
      return Convert.ToBase64String(cipherText);
    }


    /// <summary>
    /// Simple Decryption and Authentication (AES-GCM) of a UTF8 message
    /// using a key derived from a password (PBKDF2)
    /// </summary>
    /// <param name="encryptedMessage">The encrypted message.</param>
    /// <param name="password">The password.</param>
    /// <param name="nonSecretPayloadLength">Length of the non secret payload.</param>
    /// <returns>
    /// Decrypted Message
    /// </returns>
    /// <exception cref="System.ArgumentException">Encrypted Message Required!;encryptedMessage</exception>
    /// <remarks>
    /// Significantly less secure than using random binary keys.
    /// </remarks>
    public static string SimpleDecryptWithPassword(string encryptedMessage, string password,
                             int nonSecretPayloadLength = 0)
    {
      if (string.IsNullOrWhiteSpace(encryptedMessage))
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var cipherText = Convert.FromBase64String(encryptedMessage);
      var plainText = SimpleDecryptWithPassword(cipherText, password, nonSecretPayloadLength);
      return plainText == null ? null : Encoding.UTF8.GetString(plainText);
    }

    public static byte[] SimpleEncrypt(byte[] secretMessage, byte[] key, byte[] nonSecretPayload = null)
    {
      //User Error Checks
      if (key == null || key.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");

      if (secretMessage == null || secretMessage.Length == 0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      //Non-secret Payload Optional
      nonSecretPayload = nonSecretPayload ?? new byte[] { };

      //Using random nonce large enough not to repeat
      var nonce = new byte[NonceBitSize / 8];
      Random.NextBytes(nonce, 0, nonce.Length);

      var cipher = new GcmBlockCipher(new AesFastEngine());
      var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
      cipher.Init(true, parameters);

      //Generate Cipher Text With Auth Tag
      var cipherText = new byte[cipher.GetOutputSize(secretMessage.Length)];
      var len = cipher.ProcessBytes(secretMessage, 0, secretMessage.Length, cipherText, 0);
      cipher.DoFinal(cipherText, len);

      //Assemble Message
      using (var combinedStream = new MemoryStream())
      {
        using (var binaryWriter = new BinaryWriter(combinedStream))
        {
          //Prepend Authenticated Payload
          binaryWriter.Write(nonSecretPayload);
          //Prepend Nonce
          binaryWriter.Write(nonce);
          //Write Cipher Text
          binaryWriter.Write(cipherText);
        }
        return combinedStream.ToArray();
      }
    }

    public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (key == null || key.Length != KeyBitSize / 8)
        throw new ArgumentException(String.Format("Key needs to be {0} bit!", KeyBitSize), "key");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      using (var cipherStream = new MemoryStream(encryptedMessage))
      using (var cipherReader = new BinaryReader(cipherStream))
      {
        //Grab Payload
        var nonSecretPayload = cipherReader.ReadBytes(nonSecretPayloadLength);

        //Grab Nonce
        var nonce = cipherReader.ReadBytes(NonceBitSize / 8);

        var cipher = new GcmBlockCipher(new AesFastEngine());
        var parameters = new AeadParameters(new KeyParameter(key), MacBitSize, nonce, nonSecretPayload);
        cipher.Init(false, parameters);

        //Decrypt Cipher Text
        var cipherText = cipherReader.ReadBytes(encryptedMessage.Length - nonSecretPayloadLength - nonce.Length);
        var plainText = new byte[cipher.GetOutputSize(cipherText.Length)];  

        try
        {
          var len = cipher.ProcessBytes(cipherText, 0, cipherText.Length, plainText, 0);
          cipher.DoFinal(plainText, len);

        }
        catch (InvalidCipherTextException)
        {
          //Return null if it doesn't authenticate
          return null;
        }

        return plainText;
      }

    }

    public static byte[] SimpleEncryptWithPassword(byte[] secretMessage, string password, byte[] nonSecretPayload = null)
    {
      nonSecretPayload = nonSecretPayload ?? new byte[] {};

      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (secretMessage == null || secretMessage.Length == 0)
        throw new ArgumentException("Secret Message Required!", "secretMessage");

      var generator = new Pkcs5S2ParametersGenerator();

      //Use Random Salt to minimize pre-generated weak password attacks.
      var salt = new byte[SaltBitSize / 8];
      Random.NextBytes(salt);

      generator.Init(
        PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
        salt,
        Iterations);

      //Generate Key
      var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

      //Create Full Non Secret Payload
      var payload = new byte[salt.Length + nonSecretPayload.Length];
      Array.Copy(nonSecretPayload, payload, nonSecretPayload.Length);
      Array.Copy(salt,0, payload,nonSecretPayload.Length, salt.Length);

      return SimpleEncrypt(secretMessage, key.GetKey(), payload);
    }

    public static byte[] SimpleDecryptWithPassword(byte[] encryptedMessage, string password, int nonSecretPayloadLength = 0)
    {
      //User Error Checks
      if (string.IsNullOrWhiteSpace(password) || password.Length < MinPasswordLength)
        throw new ArgumentException(String.Format("Must have a password of at least {0} characters!", MinPasswordLength), "password");

      if (encryptedMessage == null || encryptedMessage.Length == 0)
        throw new ArgumentException("Encrypted Message Required!", "encryptedMessage");

      var generator = new Pkcs5S2ParametersGenerator();

      //Grab Salt from Payload
      var salt = new byte[SaltBitSize / 8];
      Array.Copy(encryptedMessage, nonSecretPayloadLength, salt, 0, salt.Length);

      generator.Init(
        PbeParametersGenerator.Pkcs5PasswordToBytes(password.ToCharArray()),
        salt,
        Iterations);

      //Generate Key
      var key = (KeyParameter)generator.GenerateDerivedMacParameters(KeyBitSize);

      return SimpleDecrypt(encryptedMessage, key.GetKey(), salt.Length + nonSecretPayloadLength);
    }
  }
}

@jbtule 2012-12-03 14:27:08

Have these samples posted on code review too.

@Caster Troy 2012-12-18 23:13:29

For the first code example, and the decryption method... There are two things I do not understand very well and hope you can clarify. In what circumstances would the first if statement return? And the for loop that checks that each element in bot arrays match: I've never seen your logic before "auth = auth && sentTag[i] == calcTag[i];" is there anything special about it or could I yield false as soon as I detect one byte does not correspond? Sorry for being vague, there's not a lot of room to comment and format code here!

@jbtule 2012-12-19 13:41:38

That's a good question, these are using Authenticated Encryption examples, in addition to encrypting they have a MAC to validate that the ciphertext hasn't been modifed by someone else, this is primarily to thwart chosen-ciphertext attacks. So when decrypting it calculates the MAC to check against the appended one to authenticate it, if it authenticates it decrypts and if it doesn't it returns null.

@jbtule 2012-12-19 13:42:24

The array check on the MAC does every index, because a timing attack can be used to calculate a new MAC on a fake ciphertext if it returns the first byte that doesn't match.

@Caster Troy 2012-12-19 19:59:06

Superb, thanks! Would you recommend Practical Cryptography by Niels Ferguson? Everybody seems to recommend it for a primer on cryptography. It's all very well understanding how to implement the bare bones encryption but it's extra security measures like you describe that interest me the most. One last question if you don't mind; You use AesManaged which I understand to be not certified. Should I / is it okay for me to use AesCryptoServiceProvider? I feel more comfortable using that class at the moment.

@jbtule 2012-12-19 20:08:59

That is a good book and relatively recent. What I would recommend even more is the free online course Cryptography I by Dan Boneh. Really good videos, really good quizes, and really good machine problems too that provide a good practical base for the usage of cryptography. You should use what you are most comfortable with in regards to the AesCryptoServiceProvider.

@Jan 2014-10-15 09:53:39

In AESThenHMAC.SimpleEncryptWithPassword() what is the non-secret payload for?

@jbtule 2014-10-15 13:47:43

It's anything that you want to send with your data (not encrypted) that you want to verify that it hasn't been altered from something like a man in the middle or fuzzing from an attacker. It's non-encrypted signed data.

@Kyle 2014-10-30 18:25:51

SimpleEncrypt example for BouncyCastle is giving me a compilation error on Random.NextBytes(nonce, 0, nonce.Length); saying it only takes one argument.

@jbtule 2014-10-30 20:13:33

Well it's not true, and doesn't give me any compiler warnings. github.com/bcgit/bc-csharp/blob/master/crypto/src/security/… Did you make any edits or exclude anything when copy and pasting?

@Benji XVI 2014-11-19 19:25:13

Thanks for this superb answer. I have a general question about this encryption scenario -- appending the HMAC enables a holder of the auth key to verify that the first section (non-secret portion + AES IV + encrypted message) was signed by another party who holds the same auth key. In this case though, if you have two parties communicating (and ignoring the plain text portion for a second) isn't the mere fact of their ability to decrypt the AES encrypted section an equally strong guarantee? What more does the HMAC buy you (or is it solely to authenticate the non-encrypted part)?

@jbtule 2014-12-01 23:32:13

@BenjiXVI no it is not a strong guarentee, see above comment about authenticated encryption and chosen-ciphertext attacks.

@Parth Shah 2014-12-30 04:03:00

I apologize for the stupidity of the following questions. 1) How or what is the recommended best practice for storing the crypt and auth keys for AESThenHMAC solution? 2) Is it a bad idea to use the same crypt and auth keys over the lifetime of the application? I am considering using this code for encrypting sensitive data prior to storing it in my database.

@jbtule 2014-12-30 14:02:28

@ParthShah It's not a stupid question, and it's actually an often overlooked one. Storing keys is HARD-very. There isn't an ideal way without specialized hardware, so just do the best you can keeping it as separate from your ciphertext as possible (in this case don't store the keys (or keys to decrypt the keys) in the same database as the ciphertext at the very least). Rotating keys over the lifetime of the app involves more than the above code, but is available for dotnet in library form: github.com/jbtule/keyczar-dotnet

@olingern 2015-06-03 19:20:22

@jbtule : The AESThenHMAC.cs Gist's SimpleDecrypt & SimpleDecryptWithPassword attempt to return "plaintext" all lower case instead of camel case as declared.

@Sudhanshu Mishra 2015-07-14 06:08:43

@jbtule I'm trying to adapt the code in public static byte[] SimpleDecrypt(byte[] encryptedMessage, byte[] key, int nonSecretPayloadLength = 0) to my use-case where there is no noonce and no authentication. With the changes (i.e. NonceBitSize = 0; MacBitSize = 0)) I get an AccessViolationException on the line cipher.ProcessBytes inside the try-catch... could you kindly suggest where am I going wrong?

@jbtule 2015-07-14 13:15:44

@dotnetguy this is a practical example of how to do symmetric encryption appropriately, what you describe is not appropriate.

@Sudhanshu Mishra 2015-07-15 01:01:50

@jbtule I may not have worded my question correctly 'cause I'm a newbie in this area, but this is what I was looking for: stackoverflow.com/a/31419559/190476

@Rocklan 2015-09-28 06:42:39

A nicely explained usage section would be extremely helpful.

@TrueWill 2015-11-11 15:45:06

RandomNumberGenerator is not thread-safe; consider RNGCryptoServiceProvider instead. And second the need for a usage section!

@jbtule 2015-11-11 16:04:20

@TrueWill RandomNumberGenerator is an abstract class, System.Security.Cryptography,RandomNumberGenerate.Create() actually returns RNGCryptoServiceProvider -stackoverflow.com/a/12329913/637783. I think about a usage example.

@TrueWill 2015-11-13 19:21:22

Note that, with Iterations = 10000 and a string password, this may be unacceptably slow for certain scenarios.

@jbtule 2015-11-13 19:29:11

@TrueWill It's true, it's 10000 because it's supposed to be slow. Better to not use password generated key's when performance is important. And password derived keys are not ideal for most scenarios, i added them as an option in this example because most SO encrypt examples use a password :'( so I wanted to provide the best practice securitywise to deal with them.

@Ian Warburton 2015-12-18 15:16:55

Is there a version of this that works with files instead of strings?

@jbtule 2015-12-18 15:31:35

@IanWarburton nah this is example code, while you could convert a file into a byte array and then use this as is, it's not ideal, you'd have to modify a little to efficiently encrypt a file. At the point you are trying to encrypt files use github.com/jbtule/keyczar-dotnet as it handles other concerns like keyrotation etc.

@jbtule 2015-12-18 17:16:26

@IanWarburton Data that you want to keep with your cipher text that you need authenticated because it's important to know if an attacker modified. Anything you need to look at before decryption, In the password examples you can see I put the salt in the nonSecretPayload.

@Ian Warburton 2015-12-21 17:10:43

@jbtule Why do you use one key for encryption and another for hashing? Why not use one key for both?

@jbtule 2015-12-21 17:49:21

Not strictly designed to be used that way, I don't know of any attacks, but it's better to go with the best practices, if you can't manage another 16 bytes of key data use AES-GCM.

@dub stylee 2016-03-18 17:49:43

If I am storing the encrypted text in a database, will I also need to store the key in the database to know what the key is to decrypt the encrypted data?

@jbtule 2016-03-18 19:12:09

Key handling is a bit out of scope and a more difficult problem. Generally the best thing to do is store the key as far away from the data as possible (so not in the database). Going with a library that has the infrastructure to handle keys including rotating keys would be a better fit for you @dubstylee such as keyczar jay.tuley.name/keyczar-dotnet

@dub stylee 2016-03-18 19:15:32

Keyczar looks like a great solution. One question: how about decrypting the same data on multiple devices? Is it possible to share the keyset among different devices? By share the keyset, I mean keep them synchronized, not just import/export.

@jbtule 2016-03-18 19:21:36

Yes, a keyset is just data, but very sensitve data, so you need to figure how to distributed it securely (public/private key cryptography). That is more of an architecture question and out side the scope of this set of comments.

@Rebecca 2016-03-23 13:24:03

This might be a stupid question, but the salt, is that stored with the encrypted ciphertext? I thought I would need to store them separately in my database, but if I'm reading the code properly, I don't need to do this using this code sample? Is that right?

@jbtule 2016-03-23 13:32:08

You are correct, if you are using a string password, the salt is randomly generated and added to the authenticated payload of the ciphertext. Design wise it ideal, because it keeps people from incorrectly using the function by reusing salts per application etc.

@Rebecca 2016-04-11 08:36:12

@jbtule you might be interested in the question I posted concerning your code: stackoverflow.com/questions/36503620/… (with bounty).

@jbtule 2016-04-11 15:44:01

@Junto not my code, you used nerdybeardo code, but I posted my thoughts anyway.

@rdans 2016-05-20 11:10:03

Which of the two solutions would you recommend? Since you refer to your second solution as a simpler practice, do we assume that means it's technically inferior but faster then the first solution?

@jbtule 2016-05-20 12:31:11

@rdans it's not technically inferior, quite the opposite, it's easier to use by having authentication and encryption in one step. The second solution's down side is that you need an extra dependency, which is the only reason I provided the first solution.

@STORM 2016-06-05 15:18:10

I got it working. What is the difference between "SimdpleDecrypt" and "SimpleDecryptWithPassword"?

@jbtule 2016-06-05 19:33:21

@STORM using an actually randomized key is way more secure than a password based key.

@kristian mo 2016-08-30 13:50:15

Is there a particular reason the salt byte size is derived by dividing by 8? var salt = new byte[SaltBitSize / 8];

@jbtule 2016-08-30 13:52:04

@kristianmo How many bits in a byte?

@Mark Sowul 2016-12-16 16:49:08

This is great, and your answering of questions is too. There's a ton of subtlety and difficulty in proper encryption, and it's little wonder that it's so often screwed up.

@Spoc 2017-07-03 07:28:36

@jbtule Thank you for these Examples, so is it secure to reuse the same cryptKey and authKey for multiple (many?) Messages? Is it because of the random IV? I'm asking, because the Generation of these Keys is (supposed to be) expensive, so at least cacheing them for the runtime of the current Process would be a huge boost.

@jbtule 2018-02-23 15:11:46

@Spoc yes you can reuse the keys, but best practice is to rotate to different keys over a long time. GCM in particular is catastrophic if the IV is ever reused with the same key, so make sure you rotate your key before you've generated 1.8446744e+19 ciphers.

@Parsa 2018-09-02 17:38:09

In .NET Built-in Encrypt(AES)-Then-MAC(HMAC) , why all methods are RECURSIVE ??? and where is their recursive-Exit-Condition ?!??! I Cant understand it...

@jbtule 2018-09-02 17:40:23

@Parsa Not recursive. There are overloads with different parameters for convenience.

@Vijay Kumbhoje 2014-12-17 06:46:17

Here is simple Snippet originally by ASP Snippets

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


 private string Encrypt(string clearText)
    {
        string EncryptionKey = "yourkey";
        byte[] clearBytes = Encoding.Unicode.GetBytes(clearText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(clearBytes, 0, clearBytes.Length);
                    cs.Close();
                }
                clearText = Convert.ToBase64String(ms.ToArray());
            }
        }
        return clearText;
    }

 private string Decrypt(string cipherText)
    {
        string EncryptionKey = "yourkey";
        cipherText = cipherText.Replace(" ", "+");
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        using (Aes encryptor = Aes.Create())
        {
            Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(EncryptionKey, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
            encryptor.Key = pdb.GetBytes(32);
            encryptor.IV = pdb.GetBytes(16);
            using (MemoryStream ms = new MemoryStream())
            {
                using (CryptoStream cs = new CryptoStream(ms, encryptor.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    cs.Write(cipherBytes, 0, cipherBytes.Length);
                    cs.Close();
                }
                cipherText = Encoding.Unicode.GetString(ms.ToArray());
            }
        }
        return cipherText;
    }

@Artjom B. 2014-12-17 09:18:00

You don't check integrity/authentication. You should add a MAC.

@Vijay Kumbhoje 2015-01-06 05:01:18

What you exactly mean actually the above example is to Encrypt/Decrypt the string variable.

@Artjom B. 2015-01-06 09:33:10

The ciphertext should be authenticated (e.g. with HMAC) to protect against padding oracle attacks. When I look through this code again, it seems that you're using ECB mode which should never be used because it is not semantically secure. Also, when you derive the key and IV from a master key and salt, the salt is static. This leads to a static IV which breaks the whole concept of the IV and makes your scheme semantically insecure again.

@Vijay Kumbhoje 2015-01-09 02:35:11

Thanks Brother, it would be great help if you can pass the corrected code here.

@Manu Nair 2014-12-01 07:16:25

You have to use the namespace using System.Security.Cryptography; and useHashing is a bool type either true or false. String variable "key" should be same for Encryption and for Decryption

//Encryption
public string EncryptText(string toEncrypt, bool useHashing)
    {
        try
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

            string key = "String Key Value"; //Based on this key stirng is encrypting
            //System.Windows.Forms.MessageBox.Show(key);
            //If hashing use get hashcode regards to your key
            if (useHashing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                //Always release the resources and flush data
                //of the Cryptographic service provide. Best Practice

                hashmd5.Clear();
            }
            else
                keyArray = UTF8Encoding.UTF8.GetBytes(key);

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            //set the secret key for the tripleDES algorithm
            tdes.Key = keyArray;
            //mode of operation. there are other 4 modes. We choose ECB(Electronic code Book)
            tdes.Mode = CipherMode.ECB;
            //padding mode(if any extra byte added)
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateEncryptor();
            //transform the specified region of bytes array to resultArray
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,          toEncryptArray.Length);
            //Release resources held by TripleDes Encryptor
            tdes.Clear();
            //Return the encrypted data into unreadable string format
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        catch (Exception e)
        {
            throw e;
        }
    }

    //Decryption
    public string DecryptText(string cipherString, bool useHashing)
    {

        try
        {
            byte[] keyArray;
            //get the byte code of the string

            byte[] toEncryptArray = Convert.FromBase64String(cipherString);

            string key = "String Key Value"; //Based on this key string is decrypted

            if (useHashing)
            {
                //if hashing was used get the hash code with regards to your key
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                //release any resource held by the MD5CryptoServiceProvider

                hashmd5.Clear();
            }
            else
            {
                //if hashing was not implemented get the byte code of the key
                keyArray = UTF8Encoding.UTF8.GetBytes(key);
            }

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            //set the secret key for the tripleDES algorithm
            tdes.Key = keyArray;
            //mode of operation. there are other 4 modes.
            //We choose ECB(Electronic code Book)

            tdes.Mode = CipherMode.ECB;
            //padding mode(if any extra byte added)
            tdes.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = tdes.CreateDecryptor();
            byte[] resultArray = cTransform.TransformFinalBlock
                    (toEncryptArray, 0, toEncryptArray.Length);
            //Release resources held by TripleDes Encryptor
            tdes.Clear();
            //return the Clear decrypted TEXT
            return UTF8Encoding.UTF8.GetString(resultArray);
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

@CodesInChaos 2015-02-19 21:34:05

-1 1) ECB mode is very weak 2) Lack of MAC leaves you open to active attacks, such as padding oracles. 3) Why would you still use 3DES nowadays? It's not broken, but AES is clearly a better choice.

@angularsen 2014-10-22 23:03:36

Copied in my answer here from a similar question: Simple two-way encryption for C#.

Based on multiple answers and comments.

  • Random initialization vector prepended to crypto text (@jbtule)
  • Use TransformFinalBlock() instead of MemoryStream (@RenniePet)
  • No pre-filled keys to avoid anyone copy & pasting a disaster
  • Proper dispose and using patterns

Code:

/// <summary>
/// Simple encryption/decryption using a random initialization vector
/// and prepending it to the crypto text.
/// </summary>
/// <remarks>Based on multiple answers in https://stackoverflow.com/questions/165808/simple-two-way-encryption-for-c-sharp </remarks>
public class SimpleAes : IDisposable
{
    /// <summary>
    ///     Initialization vector length in bytes.
    /// </summary>
    private const int IvBytes = 16;

    /// <summary>
    ///     Must be exactly 16, 24 or 32 characters long.
    /// </summary>
    private static readonly byte[] Key = Convert.FromBase64String("FILL ME WITH 16, 24 OR 32 CHARS");

    private readonly UTF8Encoding _encoder;
    private readonly ICryptoTransform _encryptor;
    private readonly RijndaelManaged _rijndael;

    public SimpleAes()
    {
        _rijndael = new RijndaelManaged {Key = Key};
        _rijndael.GenerateIV();
        _encryptor = _rijndael.CreateEncryptor();
        _encoder = new UTF8Encoding();
    }

    public string Decrypt(string encrypted)
    {
        return _encoder.GetString(Decrypt(Convert.FromBase64String(encrypted)));
    }

    public void Dispose()
    {
        _rijndael.Dispose();
        _encryptor.Dispose();
    }

    public string Encrypt(string unencrypted)
    {
        return Convert.ToBase64String(Encrypt(_encoder.GetBytes(unencrypted)));
    }

    private byte[] Decrypt(byte[] buffer)
    {
        // IV is prepended to cryptotext
        byte[] iv = buffer.Take(IvBytes).ToArray();
        using (ICryptoTransform decryptor = _rijndael.CreateDecryptor(_rijndael.Key, iv))
        {
            return decryptor.TransformFinalBlock(buffer, IvBytes, buffer.Length - IvBytes);
        }
    }

    private byte[] Encrypt(byte[] buffer)
    {
        // Prepend cryptotext with IV
        byte[] inputBuffer = _rijndael.IV.Concat(buffer).ToArray();
        return _encryptor.TransformFinalBlock(inputBuffer, IvBytes, buffer.Length);
    }
}

@CodesInChaos 2015-02-19 21:31:57

You should add a MAC to prevent active attacks, such as padding oracles.

@angularsen 2015-02-20 07:36:44

You are probably right, I'm in no way proficient on this area. When I originally visited this topic I just wanted something simple that works and fairly secure. I'd definitely use a tried and true library for very sensitive data.

@drzaus 2014-10-08 21:49:12

If you got here looking for PGP encryption, in the following comment on an example of how to use PGP encryption via BouncyCastle, the PGPEncryptDecrypt class seems to work basically out of the box:

http://blogs.microsoft.co.il/kim/2009/01/23/pgp-zip-encrypted-files-with-c/#comment-611002

Too long to paste here, slightly modified: https://gist.github.com/zaus/c0ea1fd8dad5d9590af1

@Jim Flood 2014-06-25 06:23:12

Here is a simple example of encrypting strings in C# using AES CBC mode with random IV and HMAC and password-derived keys, to show the basic moving parts:

private byte[] EncryptBytes(byte[] key, byte[] plaintext)
{
    using (var cipher = new RijndaelManaged { Key = key })
    {
        using (var encryptor = cipher.CreateEncryptor())
        {
            var ciphertext = encryptor.TransformFinalBlock(plaintext, 0, plaintext.Length);

            // IV is prepended to ciphertext
            return cipher.IV.Concat(ciphertext).ToArray();
        }
    }
}

private byte[] DecryptBytes(byte[] key, byte[] packed)
{
    using (var cipher = new RijndaelManaged { Key = key })
    {
        int ivSize = cipher.BlockSize / 8;

        cipher.IV = packed.Take(ivSize).ToArray();

        using (var encryptor = cipher.CreateDecryptor())
        {
            return encryptor.TransformFinalBlock(packed, ivSize, packed.Length - ivSize);
        }
    }
}

private byte[] AddMac(byte[] key, byte[] data)
{
    using (var hmac = new HMACSHA256(key))
    {
        var macBytes = hmac.ComputeHash(data);

        // HMAC is appended to data
        return data.Concat(macBytes).ToArray();
    }
}

private bool BadMac(byte[] found, byte[] computed)
{
    int mismatch = 0;

    // Aim for consistent timing regardless of inputs
    for (int i = 0; i < found.Length; i++)
    {
        mismatch += found[i] == computed[i] ? 0 : 1;
    }

    return mismatch != 0;
}

private byte[] RemoveMac(byte[] key, byte[] data)
{
    using (var hmac = new HMACSHA256(key))
    {
        int macSize = hmac.HashSize / 8;

        var packed = data.Take(data.Length - macSize).ToArray();

        var foundMac = data.Skip(packed.Length).ToArray();

        var computedMac = hmac.ComputeHash(packed);

        if (this.BadMac(foundMac, computedMac))
        {
            throw new Exception("Bad MAC");
        }

        return packed;
    }            
}

private List<byte[]> DeriveTwoKeys(string password)
{
    var salt = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    var kdf = new Rfc2898DeriveBytes(password, salt, 10000);

    var bytes = kdf.GetBytes(32); // Two keys 128 bits each

    return new List<byte[]> { bytes.Take(16).ToArray(), bytes.Skip(16).ToArray() };
}

public byte[] EncryptString(string password, String message)
{
    var keys = this.DeriveTwoKeys(password);

    var plaintext = Encoding.UTF8.GetBytes(message);

    var packed = this.EncryptBytes(keys[0], plaintext);

    return this.AddMac(keys[1], packed);
}

public String DecryptString(string password, byte[] secret)
{
    var keys = this.DeriveTwoKeys(password);

    var packed = this.RemoveMac(keys[1], secret);

    var plaintext = this.DecryptBytes(keys[0], packed);

    return Encoding.UTF8.GetString(plaintext);
}

public void Example()
{
    var password = "correcthorsebatterystaple";

    var secret = this.EncryptString(password, "Hello World");

    Console.WriteLine("secret: " + BitConverter.ToString(secret));

    var recovered = this.DecryptString(password, secret);

    Console.WriteLine(recovered);
}

@CodesInChaos 2014-08-11 10:59:12

A couple of issues: 1) You're not using a salt in the key derivation, enabling multi target attacks. 2) Your MAC comparison function is potentially vulnerable to side-channel/timing attacks since you branch on secret data. Use something like mismatch += found[i]^computed[i] instead. 3) You're using more than 20 bytes of PBKDF2-HMAC-SHA-1 which slows your KDF down by a factor of 2 without slowing down an attacker

@Jim Flood 2014-08-11 20:47:50

@CodesInChaos: 1) This was meant as a simple example to get people started -- I omit random salt only for clarity. But, good point. 2) Good, subtle point. 3) What do you suggest to derive two 16 byte keys in twenty bytes?

@CodesInChaos 2014-08-12 08:00:50

The simplest way is hashing the output of the slow hash with SHA-2. Fancier ways are HKDF or simply applying PBKDF2 again, but this time with iterations set to 1.

@Jim Flood 2014-08-14 16:27:51

@CodesInChaos I would not use SHA-2. The job of a hash function is not the same as the job of a key derivation function. A hash only needs to be unpredictable, and to change when the input changes. A key needs to be indistinguishable from random. I would still draw 32 bytes from the KDF. In this case, you are optimizing too early and adding risk.

@user3556387 2014-04-23 13:08:10

for simplicity i made for myself this function that i use for non crypto purposes : replace "yourpassphrase" with your password ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

 namespace My
{
    public class strCrypto
    {
        // This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
    // This size of the IV (in bytes) must = (keysize / 8).  Default keysize is 256, so the IV must be
    // 32 bytes long.  Using a 16 character string here gives us 32 bytes when converted to a byte array.
    private const string initVector = "r5dm5fgm24mfhfku";
    private const string passPhrase = "yourpassphrase"; // email password encryption password

    // This constant is used to determine the keysize of the encryption algorithm.
    private const int keysize = 256;

    public static string encryptString(string plainText)
    {
        //if the plaintext  is empty or null string just return an empty string
        if (plainText == "" || plainText == null )
        {
            return "";
        }

        byte[] initVectorBytes = Encoding.UTF8.GetBytes(initVector);
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
        byte[] keyBytes = password.GetBytes(keysize / 8);
        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;
        ICryptoTransform encryptor = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream();
        CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);
        cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
        cryptoStream.FlushFinalBlock();
        byte[] cipherTextBytes = memoryStream.ToArray();
        memoryStream.Close();
        cryptoStream.Close();
        return Convert.ToBase64String(cipherTextBytes);
    }

    public static string decryptString(string cipherText)
    {
        //if the ciphertext is empty or null string just return an empty string
        if (cipherText == "" || cipherText == null )
        {
            return "";
        }

        byte[] initVectorBytes = Encoding.ASCII.GetBytes(initVector);
        byte[] cipherTextBytes = Convert.FromBase64String(cipherText);
        PasswordDeriveBytes password = new PasswordDeriveBytes(passPhrase, null);
        byte[] keyBytes = password.GetBytes(keysize / 8);
        RijndaelManaged symmetricKey = new RijndaelManaged();
        symmetricKey.Mode = CipherMode.CBC;
        ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes);
        MemoryStream memoryStream = new MemoryStream(cipherTextBytes);
        CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
        byte[] plainTextBytes = new byte[cipherTextBytes.Length];
        int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
        memoryStream.Close();
        cryptoStream.Close();
        return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
    }


}

}

@CodesInChaos 2014-04-23 16:09:27

1) No salt in key derivation 2) Constant IV, which misses the whole point of an IV. It should be different for each encryption. 3) No authentication => padding oracles are a threat 4) encryptor.TransformFinalBlock is simpler than using those memory and crypto streams.

@KarthikManoharan 2014-04-08 10:33:50

            using System;
            using System.Collections.Generic;
            using System.Text;
            using System.Text.RegularExpressions;  // This is for password validation
            using System.Security.Cryptography;
            using System.Configuration;  // This is where the hash functions reside

            namespace BullyTracker.Common
            {
                public class HashEncryption
                {
                    //public string GenerateHashvalue(string thisPassword)
                    //{
                    //    MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
                    //    byte[] tmpSource;
                    //    byte[] tmpHash;

                    //    tmpSource = ASCIIEncoding.ASCII.GetBytes(thisPassword); // Turn password into byte array
                    //    tmpHash = md5.ComputeHash(tmpSource);

                    //    StringBuilder sOutput = new StringBuilder(tmpHash.Length);
                    //    for (int i = 0; i < tmpHash.Length; i++)
                    //    {
                    //        sOutput.Append(tmpHash[i].ToString("X2"));  // X2 formats to hexadecimal
                    //    }
                    //    return sOutput.ToString();
                    //}
                    //public Boolean VerifyHashPassword(string thisPassword, string thisHash)
                    //{
                    //    Boolean IsValid = false;
                    //    string tmpHash = GenerateHashvalue(thisPassword); // Call the routine on user input
                    //    if (tmpHash == thisHash) IsValid = true;  // Compare to previously generated hash
                    //    return IsValid;
                    //}
                    public string GenerateHashvalue(string toEncrypt, bool useHashing)
                    {
                        byte[] keyArray;
                        byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                        System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
                        // Get the key from config file
                        string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));
                        //System.Windows.Forms.MessageBox.Show(key);
                        if (useHashing)
                        {
                            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                            hashmd5.Clear();
                        }
                        else
                            keyArray = UTF8Encoding.UTF8.GetBytes(key);

                        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                        tdes.Key = keyArray;
                        tdes.Mode = CipherMode.ECB;
                        tdes.Padding = PaddingMode.PKCS7;

                        ICryptoTransform cTransform = tdes.CreateEncryptor();
                        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
                        tdes.Clear();
                        return Convert.ToBase64String(resultArray, 0, resultArray.Length);
                    }
                    /// <summary>
                    /// DeCrypt a string using dual encryption method. Return a DeCrypted clear string
                    /// </summary>
                    /// <param name="cipherString">encrypted string</param>
                    /// <param name="useHashing">Did you use hashing to encrypt this data? pass true is yes</param>
                    /// <returns></returns>
                    public string Decrypt(string cipherString, bool useHashing)
                    {
                        byte[] keyArray;
                        byte[] toEncryptArray = Convert.FromBase64String(cipherString);

                        System.Configuration.AppSettingsReader settingsReader = new AppSettingsReader();
                        //Get your key from config file to open the lock!
                        string key = (string)settingsReader.GetValue("SecurityKey", typeof(String));

                        if (useHashing)
                        {
                            MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                            keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                            hashmd5.Clear();
                        }
                        else
                            keyArray = UTF8Encoding.UTF8.GetBytes(key);

                        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
                        tdes.Key = keyArray;
                        tdes.Mode = CipherMode.ECB;
                        tdes.Padding = PaddingMode.PKCS7;

                        ICryptoTransform cTransform = tdes.CreateDecryptor();
                        byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);

                        tdes.Clear();
                        return UTF8Encoding.UTF8.GetString(resultArray);
                    }


                }

            }

@CodesInChaos 2014-04-10 10:28:55

Really low quality. 1) ECB mode (which also implies no IV) 2) 3DES 3) Confuses keys and passwords. 4) Bad naming 5) No MAC

@Catto 2013-10-01 20:12:16

This is the class that was placed here by Brett. However I made a slight edit since I was receiving the error 'Invalid length for a Base-64 char array' when using it for URL strings to encrypt and decrypt.

public class CryptoURL
{
    private static byte[] _salt = Encoding.ASCII.GetBytes("Catto_Salt_Enter_Any_Value99");

    /// <summary>
    /// Encrypt the given string using AES.  The string can be decrypted using 
    /// DecryptStringAES().  The sharedSecret parameters must match. 
    /// The SharedSecret for the Password Reset that is used is in the next line
    ///  string sharedSecret = "OneUpSharedSecret9";
    /// </summary>
    /// <param name="plainText">The text to encrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for encryption.</param>
    public static string EncryptString(string plainText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException("plainText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        string outStr = null;                       // Encrypted string to return
        RijndaelManaged aesAlg = null;              // RijndaelManaged object used to encrypt the data.

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create a RijndaelManaged object
            aesAlg = new RijndaelManaged();
            aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);

            // Create a decryptor to perform the stream transform.
            ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

            // Create the streams used for encryption.
            using (MemoryStream msEncrypt = new MemoryStream())
            {
                // prepend the IV
                msEncrypt.Write(BitConverter.GetBytes(aesAlg.IV.Length), 0, sizeof(int));
                msEncrypt.Write(aesAlg.IV, 0, aesAlg.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);
                    }
                }

                outStr = HttpServerUtility.UrlTokenEncode(msEncrypt.ToArray());
                //outStr = Convert.ToBase64String(msEncrypt.ToArray());
                // you may need to add a reference. right click reference in solution explorer => "add Reference" => .NET tab => select "System.Web"
            }
        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        // Return the encrypted bytes from the memory stream.
        return outStr;
    }

    /// <summary>
    /// Decrypt the given string.  Assumes the string was encrypted using 
    /// EncryptStringAES(), using an identical sharedSecret.
    /// </summary>
    /// <param name="cipherText">The text to decrypt.</param>
    /// <param name="sharedSecret">A password used to generate a key for decryption.</param>
    public static string DecryptString(string cipherText, string sharedSecret)
    {
        if (string.IsNullOrEmpty(cipherText))
            throw new ArgumentNullException("cipherText");
        if (string.IsNullOrEmpty(sharedSecret))
            throw new ArgumentNullException("sharedSecret");

        // Declare the RijndaelManaged object
        // used to decrypt the data.
        RijndaelManaged aesAlg = null;

        // Declare the string used to hold
        // the decrypted text.
        string plaintext = null;

        byte[] inputByteArray;

        try
        {
            // generate the key from the shared secret and the salt
            Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(sharedSecret, _salt);

            // Create the streams used for decryption.                
            //byte[] bytes = Convert.FromBase64String(cipherText);
            inputByteArray = HttpServerUtility.UrlTokenDecode(cipherText);

            using (MemoryStream msDecrypt = new MemoryStream(inputByteArray))
            {
                // Create a RijndaelManaged object
                // with the specified key and IV.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
                // Get the initialization vector from the encrypted stream
                aesAlg.IV = ReadByteArray(msDecrypt);
                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
                using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                {
                    using (StreamReader srDecrypt = new StreamReader(csDecrypt))

                        // Read the decrypted bytes from the decrypting stream
                        // and place them in a string.
                        plaintext = srDecrypt.ReadToEnd();
                }
            }
        }
        catch (System.Exception ex)
        {
            return "ERROR";
            //throw ex;

        }
        finally
        {
            // Clear the RijndaelManaged object.
            if (aesAlg != null)
                aesAlg.Clear();
        }

        return plaintext;
    }

    static string ConvertStringArrayToString(string[] array)
    {
        //
        // Concatenate all the elements into a StringBuilder.
        //
        StringBuilder builder = new StringBuilder();
        foreach (string value in array)
        {
            builder.Append(value);
            builder.Append('.');
        }
        return builder.ToString();
    }

    private static byte[] ReadByteArray(Stream s)
    {
        byte[] rawLength = new byte[sizeof(int)];
        if (s.Read(rawLength, 0, rawLength.Length) != rawLength.Length)
        {
            throw new SystemException("Stream did not contain properly formatted byte array");
        }

        byte[] buffer = new byte[BitConverter.ToInt32(rawLength, 0)];
        if (s.Read(buffer, 0, buffer.Length) != buffer.Length)
        {
            throw new SystemException("Did not read byte array properly");
        }

        return buffer;
    }

}

@abenci 2016-09-14 12:49:13

What is the ConvertStringArrayToString() method used for?

@mattmanser 2013-03-14 11:14:24

If you are using ASP.Net you can now use built in functionality in .Net 4.0 onwards.

System.Web.Security.MachineKey

.Net 4.5 has MachineKey.Protect() and MachineKey.Unprotect().

.Net 4.0 has MachineKey.Encode() and MachineKey.Decode(). You should just set the MachineKeyProtection to 'All'.

Outside of ASP.Net this class seems to generate a new key with every app restart so doesn't work. With a quick peek in ILSpy it looks to me like it generates its own defaults if the appropriate app.settings are missing. So you may actually be able to set it up outside ASP.Net.

I haven't been able to find a non-ASP.Net equivalent outside the System.Web namespace.

@Dirk Boer 2014-03-27 12:04:39

hmm can anyone tell me why does this answer has so few votes? It looks like a very convenient way for ASP.NET applications

@mattmanser 2014-03-27 12:38:04

@DirkBoer The functionality was added a couple of years after the question was asked, I added my answer to this question to let people know there are easier ways today. This also only works with ASP.Net without some app.config-fu which is fairly dangerous if you don't know what you are doing.

@Adriaan Davel 2016-02-24 15:33:43

Pardon my ignorance but from the web page I can't figure out my answer. If I encrypt a string on one machine, write it to a database, and read it with another machine will I be able to decrypt it as long as the purposes parameters are the same value? Maybe I'm just confused by the class name "MachineKey"

@Adriaan Davel 2016-02-24 15:57:30

Another question, can I use this in a WPF app? It has no web references, is it ok to add the references to System.Web?

@mattmanser 2016-02-25 09:53:38

@AdriaanDavel Yes, but be aware that I think it generates a new machine key each app run, so depending on what you are using it for I think you need to save your own machine key somewhere.

@Mark Sowul 2016-12-19 19:16:10

@AdriaanDavel Per the linked docs, "The MachineKey APIs should only be used in an ASP.NET app. Behavior of the MachineKey APIs outside the context of an ASP.NET application is undefined" -- use it only if you enjoy the game of Russian Roulette

@MAXE 2012-08-25 13:22:43

I want to give you my contribute, with my code for AES Rfc2898DeriveBytes (here the documentation) algorhytm, written in C# (.NET framework 4) and fully working also for limited platforms, as .NET Compact Framework for Windows Phone 7.0+ (not all platforms support every criptographic method of the .NET framework!).

I hope this can help anyone!

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

public static class Crypto
{
    private static readonly byte[] IVa = new byte[] { 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x11, 0x12, 0x13, 0x14, 0x0e, 0x16, 0x17 };


    public static string Encrypt(this string text, string salt)
    {
        try
        {
            using (Aes aes = new AesManaged())
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                aes.IV = aes.Key;
                using (MemoryStream encryptionStream = new MemoryStream())
                {
                    using (CryptoStream encrypt = new CryptoStream(encryptionStream, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        byte[] cleanText = Encoding.UTF8.GetBytes(text);
                        encrypt.Write(cleanText, 0, cleanText.Length);
                        encrypt.FlushFinalBlock();
                    }

                    byte[] encryptedData = encryptionStream.ToArray();
                    string encryptedText = Convert.ToBase64String(encryptedData);


                    return encryptedText;
                }
            }
        }
        catch
        {
            return String.Empty;
        }
    }

    public static string Decrypt(this string text, string salt)
    {
        try
        {
            using (Aes aes = new AesManaged())
            {
                Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(Encoding.UTF8.GetString(IVa, 0, IVa.Length), Encoding.UTF8.GetBytes(salt));
                aes.Key = deriveBytes.GetBytes(128 / 8);
                aes.IV = aes.Key;

                using (MemoryStream decryptionStream = new MemoryStream())
                {
                    using (CryptoStream decrypt = new CryptoStream(decryptionStream, aes.CreateDecryptor(), CryptoStreamMode.Write))
                    {
                        byte[] encryptedData = Convert.FromBase64String(text);


                        decrypt.Write(encryptedData, 0, encryptedData.Length);
                        decrypt.Flush();
                    }

                    byte[] decryptedData = decryptionStream.ToArray();
                    string decryptedText = Encoding.UTF8.GetString(decryptedData, 0, decryptedData.Length);


                    return decryptedText;
                }
            }
        }
        catch
        {
            return String.Empty;
        }
        }
    }
}

@CodesInChaos 2013-08-14 16:44:08

1) Why are you using a variable called IVa which is no IV, but a password? 2) Why are you setting IV=Key? You need a new random IV for each encryption. 3) Lack of MAC enables padding oracle attacks

@BPL 2011-04-01 06:37:58

using System;
using System.Data;
using System.Configuration;
using System.Text;
using System.Security.Cryptography;

namespace Encription
{
    class CryptorEngine
    {
        public static string Encrypt(string ToEncrypt, bool useHasing)
        {
            byte[] keyArray;
            byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(ToEncrypt);
            //System.Configuration.AppSettingsReader settingsReader = new     AppSettingsReader();
           string Key = "Bhagwati";
            if (useHasing)
            {
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(Key));
                hashmd5.Clear();  
            }
            else
            {
                keyArray = UTF8Encoding.UTF8.GetBytes(Key);
            }
            TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();
            tDes.Key = keyArray;
            tDes.Mode = CipherMode.ECB;
            tDes.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tDes.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0,     toEncryptArray.Length);
            tDes.Clear();
            return Convert.ToBase64String(resultArray, 0, resultArray.Length);
        }
        public static string Decrypt(string cypherString, bool useHasing)
        {
            byte[] keyArray;
            byte[] toDecryptArray = Convert.FromBase64String(cypherString);
            //byte[] toEncryptArray = Convert.FromBase64String(cypherString);
            //System.Configuration.AppSettingsReader settingReader = new     AppSettingsReader();
            string key = "Bhagwati";
            if (useHasing)
            {
                MD5CryptoServiceProvider hashmd = new MD5CryptoServiceProvider();
                keyArray = hashmd.ComputeHash(UTF8Encoding.UTF8.GetBytes(key));
                hashmd.Clear();
            }
            else
            {
                keyArray = UTF8Encoding.UTF8.GetBytes(key);
            }
            TripleDESCryptoServiceProvider tDes = new TripleDESCryptoServiceProvider();
            tDes.Key = keyArray;
            tDes.Mode = CipherMode.ECB;
            tDes.Padding = PaddingMode.PKCS7;
            ICryptoTransform cTransform = tDes.CreateDecryptor();
            try
            {
                byte[] resultArray = cTransform.TransformFinalBlock(toDecryptArray, 0,         toDecryptArray.Length);

                tDes.Clear();
                return UTF8Encoding.UTF8.GetString(resultArray,0,resultArray.Length);
            }
            catch (Exception ex)
            {
                throw ex;
             }
        }
    }
}

@John Bubriski 2012-01-27 20:14:23

Isn't the ECB cipher mode a big no-no?

@Rich 2012-12-05 10:34:02

Yes, ECB is the least secure option. See MS's comments: "Important: This mode is not recommended because it opens the door for multiple security exploits." msdn.microsoft.com/en-us/library/…

Related Questions

Sponsored Content

26 Answered Questions

[SOLVED] How do I enumerate an enum in C#?

8 Answered Questions

[SOLVED] How to loop through all enum values in C#?

24 Answered Questions

[SOLVED] Cast int to enum in C#

  • 2008-08-27 03:58:21
  • lomaxx
  • 1140732 View
  • 2796 Score
  • 24 Answer
  • Tags:   c# enums casting

23 Answered Questions

[SOLVED] How do you give a C# Auto-Property a default value?

42 Answered Questions

[SOLVED] How to create Excel (.XLS and .XLSX) file in C# without installing Ms Office?

  • 2008-09-29 22:30:28
  • mistrmark
  • 981447 View
  • 1697 Score
  • 42 Answer
  • Tags:   c# .net excel file-io

38 Answered Questions

57 Answered Questions

[SOLVED] What is the difference between String and string in C#?

9 Answered Questions

[SOLVED] What are the correct version numbers for C#?

63 Answered Questions

[SOLVED] How do I calculate someone's age in C#?

  • 2008-07-31 23:40:59
  • Jeff Atwood
  • 524475 View
  • 1743 Score
  • 63 Answer
  • Tags:   c# .net datetime

9 Answered Questions

Sponsored Content