Encrypt and decrypt a string in C#? [closed]
How can I encrypt and decrypt a string in C#?
Solution 1:
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;
}
}
Solution 2:
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);
}
}
}
Solution 3:
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
Solution 4:
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.