Click here to Skip to main content
15,881,791 members
Articles / Security / Encryption

C# AES 256 bits Encryption Library with Salt

Rate me:
Please Sign up or sign in to vote.
4.79/5 (93 votes)
9 May 2014Public Domain2 min read 545K   23.3K   143   80
A C# universal AES Encryption Library.

Core Encryption Code

C#
using System.Security.Cryptography;
using System.IO;

Encryption

C#
public byte[] AES_Encrypt(byte[] bytesToBeEncrypted, byte[] passwordBytes)
{
    byte[] encryptedBytes = null;

    // Set your salt here, change it to meet your flavor:
    // The salt bytes must be at least 8 bytes.
    byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    using (MemoryStream ms = new MemoryStream())
    {
        using (RijndaelManaged AES = new RijndaelManaged())
        {
            AES.KeySize = 256;
            AES.BlockSize = 128;

            var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);

            AES.Mode = CipherMode.CBC;

            using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length);
                cs.Close();
            }
            encryptedBytes = ms.ToArray();
        }
    }

    return encryptedBytes;
}

Decryption

C#
public byte[] AES_Decrypt(byte[] bytesToBeDecrypted, byte[] passwordBytes)
{
    byte[] decryptedBytes = null;

    // Set your salt here, change it to meet your flavor:
    // The salt bytes must be at least 8 bytes.
    byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 };

    using (MemoryStream ms = new MemoryStream())
    {
        using (RijndaelManaged AES = new RijndaelManaged())
        {
            AES.KeySize = 256;
            AES.BlockSize = 128;

            var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000);
            AES.Key = key.GetBytes(AES.KeySize / 8);
            AES.IV = key.GetBytes(AES.BlockSize / 8);

            AES.Mode = CipherMode.CBC;

            using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
            {
                cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length);
                cs.Close();
            }
            decryptedBytes = ms.ToArray();
        }
    }

    return decryptedBytes;
}

Example of Encrypting String

Encrypt String

C#
public string EncryptText(string input, string password)
{
    // Get the bytes of the string
    byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(input);
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);

    // Hash the password with SHA256
    passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    byte[] bytesEncrypted = AES_Encrypt(bytesToBeEncrypted, passwordBytes);

    string result = Convert.ToBase64String(bytesEncrypted);

    return result;
}

Decrypt String

C#
public string DecryptText(string input, string password)
{
    // Get the bytes of the string
    byte[] bytesToBeDecrypted = Convert.FromBase64String(input);
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
    passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    byte[] bytesDecrypted = AES_Decrypt(bytesToBeDecrypted, passwordBytes);

    string result = Encoding.UTF8.GetString(bytesDecrypted);

    return result;
}

You'll notice that the encrypted string is stored in base64 encoded mode. For those who is not familiar with base64 encoding, you may want to read Base What? A Practical Introduction to Base Encoding[^]

Getting Randomized Encryption Result with Salt

If we encrypt the same context (i.e. string of "Hello World") for 10 times, the encrypted results will be the same. What if we want the results different from each time it is encrypted?

What I do is appending a random salt bytes in front of the original bytes before encryption, and remove it after decryption.

Example of Appending Randomized Salt Before Encrypting a String

C#
public static string EncryptString(string text, string password)
{
    byte[] baPwd = Encoding.UTF8.GetBytes(password);

    // Hash the password with SHA256
    byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);

    byte[] baText = Encoding.UTF8.GetBytes(text);

    byte[] baSalt = GetRandomBytes();
    byte[] baEncrypted = new byte[baSalt.Length + baText.Length];

    // Combine Salt + Text
    for (int i = 0; i < baSalt.Length; i++)
        baEncrypted[i] = baSalt[i];
    for (int i = 0; i < baText.Length; i++)
        baEncrypted[i + baSalt.Length] = baText[i];

    baEncrypted = AES_Encrypt(baEncrypted, baPwdHash);

    string result = Convert.ToBase64String(baEncrypted);
    return result;
}

Example of Removing the Salt after Decryption

C#
public static string DecryptString(string text, string password)
{
    byte[] baPwd = Encoding.UTF8.GetBytes(password);

    // Hash the password with SHA256
    byte[] baPwdHash = SHA256Managed.Create().ComputeHash(baPwd);

    byte[] baText = Convert.FromBase64String(text);

    byte[] baDecrypted = AES_Decrypt(baText, baPwdHash);

    // Remove salt
    int saltLength = GetSaltLength();
    byte[] baResult = new byte[baDecrypted.Length - saltLength];
    for (int i = 0; i < baResult.Length; i++)
        baResult[i] = baDecrypted[i + saltLength];

    string result = Encoding.UTF8.GetString(baResult);
    return result;
}

Code for getting random bytes

C#
public static byte[] GetRandomBytes()
{
    int saltLength = GetSaltLength();
    byte[] ba = new byte[saltLength];
    RNGCryptoServiceProvider.Create().GetBytes(ba);
    return ba;
}

public static int GetSaltLength()
{
    return 8;
}

Another way of getting random bytes is by using System.Random. However, System.Random is strongly not recommended to be used in cryptography. This is because System.Random is not a true random. The changes of the value are following a specific sequence and pattern and it is predictable. RNGCrypto is truly randomize and the generated values does not follow a pattern and it is unpredictable.

About Storing Password in System.String

If you are working this on a desktop pc application, you are recommended not to store the password in plain string. Strings cannot be manually disposed. It will be disposed by Garbage Collector, however, it is hard to tell how long will the string continue to stay in memory before it is completely dispose. At the time it remains in memory, it is easily retrievable. One of the suggested method is by capturing the password key strokes and store it into byte array (or list of bytes) and wipe off the byte array immediately after it is used (for example, fill the byte array with 0 (zero), etc).

Another option is by using System.Security.SecureString. System.Security.SecureString can be destroyed manually at any time. The value stored within System.Security.SecureString is encrypted.

Example of Using SecureString

C#
using System.Security;
using System.Runtime.InteropServices; 

Storing text into SecureString:

C#
SecureString secureString = new SecureString();
secureString.AppendChar('h');
secureString.AppendChar('e');
secureString.AppendChar('l');
secureString.AppendChar('l');
secureString.AppendChar('o'); 

Retrieving data from SecureString

byte[] secureStringBytes = null;
// Convert System.SecureString to Pointer
IntPtr unmanagedBytes = Marshal.SecureStringToGlobalAllocAnsi(secureString);
try
{
    unsafe
    {
        byte* byteArray = (byte*)unmanagedBytes.ToPointer();
        // Find the end of the string
        byte* pEnd = byteArray;
        while (*pEnd++ != 0) { }
        // Length is effectively the difference here (note we're 1 past end) 
        int length = (int)((pEnd - byteArray) - 1);
        secureStringBytes = new byte[length];
        for (int i = 0; i < length; ++i)
        {
            // Work with data in byte array as necessary, via pointers, here
            secureStringBytes[i] = *(byteArray + i);
        }
    }
}
finally
{
    // This will completely remove the data from memory
    Marshal.ZeroFreeGlobalAllocAnsi(unmanagedBytes);
} 
return secureStringBytes; 

Retriving data from SecureString involves executing unsafe code (for handling unmanaged bytes). Therefore you have to allow your project to be built with unsafe code allowed. This option is available at your project's Properties settings.

Image 1

Or else you will receive error message:

Unsafe code may only appear if compiling with /unsafe 

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer
Other Other
Programming is an art.

Comments and Discussions

 
QuestionHello Adrian Cimpeanu - will you accept PRs Pin
SERGUEIK21-Jun-23 10:08
SERGUEIK21-Jun-23 10:08 
GeneralMy vote of 5 Pin
devtronics31-May-21 13:34
devtronics31-May-21 13:34 
QuestionProgramming is an art. Pin
Stefan G - 974211825-Dec-19 10:29
Stefan G - 974211825-Dec-19 10:29 
SuggestionSuggestion of code with best cryptographic practices Pin
Alessandro Cagliostro5-Sep-19 14:02
Alessandro Cagliostro5-Sep-19 14:02 
Here is a repository with good code for reference and usage, with the best cryptographic practices -> GitHub - alecgn/crypthash-net: CryptHash.NET is a .NET multi-target library to encrypt/decrypt/hash strings and files, with an optional .NET Core multiplatform console utility.[^]
GeneralMy vote of 5 Pin
Sajeeb Chandan29-Oct-18 12:19
Sajeeb Chandan29-Oct-18 12:19 
GeneralMy vote of 5 Pin
raddevus10-Sep-18 4:24
mvaraddevus10-Sep-18 4:24 
PraiseThank you! Awesome article Pin
WestieBoy24-Jul-18 19:14
WestieBoy24-Jul-18 19:14 
GeneralGracias Pin
slider_k99911-May-18 11:49
slider_k99911-May-18 11:49 
PraiseAwesome article, thank you Pin
Member 1288850920-Nov-17 13:24
Member 1288850920-Nov-17 13:24 
GeneralMy vote of 5 Pin
RSamyPSamy19-Oct-17 7:14
RSamyPSamy19-Oct-17 7:14 
QuestionC# AES CTR 128bit Encrypt Pin
MaKhaing28-Feb-17 15:57
MaKhaing28-Feb-17 15:57 
GeneralMy vote of 3 Pin
Christopher Aicher12-Feb-17 6:34
Christopher Aicher12-Feb-17 6:34 
QuestionCan this work as a console application? + another question Pin
User 1289172328-Dec-16 15:06
User 1289172328-Dec-16 15:06 
QuestionDifferent password for encryption and decryption Pin
spritti983-Dec-16 23:32
spritti983-Dec-16 23:32 
QuestionThis is generating different encrypted text on different machines Pin
Member 1243484825-Nov-16 0:33
Member 1243484825-Nov-16 0:33 
QuestionSecureString Pin
ILOVETOFU25-Aug-16 22:14
ILOVETOFU25-Aug-16 22:14 
QuestionHow to implement file encryption and decryption? Pin
Member 1227869624-Jan-16 7:26
Member 1227869624-Jan-16 7:26 
AnswerRe: How to implement file encryption and decryption? Pin
adriancs24-Jan-16 15:17
mvaadriancs24-Jan-16 15:17 
QuestionHow to remove randomized salt? Pin
Member 1227869622-Jan-16 8:04
Member 1227869622-Jan-16 8:04 
Questionabout salt Pin
Member 1103130415-Dec-15 0:56
Member 1103130415-Dec-15 0:56 
QuestionDecrypting SecureString Pin
chriha16-Oct-15 6:36
chriha16-Oct-15 6:36 
QuestionWhy OFB mode not working in visual studio 2015 Pin
vishalgpt4-Oct-15 20:41
vishalgpt4-Oct-15 20:41 
QuestionTwice SHA256 Pin
Member 1097347710-Sep-15 11:39
Member 1097347710-Sep-15 11:39 
AnswerRe: Twice SHA256 Pin
adriancs12-Sep-15 16:20
mvaadriancs12-Sep-15 16:20 
AnswerRe: Twice SHA256 Pin
chriselviss17-Sep-15 22:45
chriselviss17-Sep-15 22:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.