Skip to content

Instantly share code, notes, and snippets.

@rdev5
Created June 25, 2014 17:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rdev5/baca6e2e8fe487d94cc6 to your computer and use it in GitHub Desktop.
Save rdev5/baca6e2e8fe487d94cc6 to your computer and use it in GitHub Desktop.
// @class AESEncrypt
// @author Matt Borja
// @base https://github.com/Jasig/cas/blob/master/cas-server-extension-clearpass/src/main/java/org/jasig/cas/extension/clearpass/EncryptedMapDecorator.java
// @description This is a port of the EncryptedMapDecorator (with its fixes) from the ClearPass extensions for Jasig's Central Authentication Service (CAS)
public class AESProvider {
private const int AES_BLOCK_SIZE = 128; // Valid block sizes: 128; Refers to RijndaelManaged.LegalBlocksizes
public int AES_KEY_LENGTH = 256; // Valid key lengths: 128, 192, 256; Refers to RijndaelManaged.LegalKeysizes
private const int SALT_ITERATIONS = 65536;
private const int INTEGER_LEN = 4;
private const CipherMode CIPHER_MODE = CipherMode.CBC; // Valid options: CBC, CFB, CTS, ECB, OFB
private const PaddingMode PADDING_MODE = PaddingMode.PKCS7;
// @method StringToByteArray
// @param string hex
// @source http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
// @description Converts hex string back to byte array
public byte[] StringToByteArray(string hex)
{
byte[] bytes = new byte[hex.Length / 2];
for (int i = 0; i < hex.Length; i += 2)
{
bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
}
return bytes;
}
// @method SecureRandom
// @param int size
// @return byte[]
// @description Generates cryptographically strong sequence of random bytes of specified size.
public byte[] SecureRandom(int size)
{
byte[] bytes = new byte[size];
RNGCryptoServiceProvider.Create().GetBytes(bytes);
return bytes;
}
// @method LegalKeyLength()
// @param int length
// @description Checks if length is a legal key size for use with AES128/192/256
public bool LegalKeyLength(int length)
{
return length == 128 || length == 192 || length == 256;
}
// @method GetSaltSize()
// @return int
// @description Get the minimum recommended byte length of salt based on AES_KEY_LENGTH (e.g. 128-bits or longer)
public int GetSaltLength()
{
return AES_KEY_LENGTH / 8;
}
// @method GetKeyLength()
// @return int
// @description Get the byte length of AES_KEY_LENGTH;
public int GetKeyLength()
{
return AES_KEY_LENGTH / 8;
}
// @method GetBlockSize()
// @return int
// @description Get the byte length of AES_BLOCK_SIZE
public static int GetBlockSize()
{
return AES_BLOCK_SIZE / 8;
}
// @method GetIVSize()
// @return int
// @description Get the byte length of IV based on AES_BLOCK_SIZE
public static int GetIVSize()
{
return GetBlockSize();
}
// @method GenerateIV
// @return byte[]
// @description Generates cryptographiaclly strong sequence of random bytes using GetIVSize()
public byte[] GenerateIV()
{
return SecureRandom(GetIVSize());
}
// @method GenerateSalt()
// @return byte[]
// @description Generates cryptographiaclly strong sequence of random bytes using GetSaltLength()
public byte[] GenerateSalt()
{
return SecureRandom(GetSaltLength());
}
// @method DeriveKey
// @param string passphrase
// @param byte[] salt
// @return byte[]
// @description Password-Based Key Derivation Function (PBKDF) based on Rfc2898DeriveBytes for the specified AES_KEY_LENGTH (e.g. AES-256 has a key length of 32 bytes)
public byte[] DeriveKey(string passphrase, byte[] salt)
{
return (new Rfc2898DeriveBytes(passphrase, salt, SALT_ITERATIONS)).GetBytes(AES_KEY_LENGTH / 8);
}
// @method Int2Byte
// @param int value
// @description Convert integer value to byte[] (http://stackoverflow.com/questions/1318933/c-sharp-int-to-byte)
public static byte[] Int2Byte(int value)
{
byte[] bytes = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
return bytes;
}
// @method Byte2Int
// @param byte bytes
// @description Convert array of bytes back to integer (http://msdn.microsoft.com/en-us/library/bb384066.aspx)
public static int Byte2Int(byte[] bytes)
{
if (BitConverter.IsLittleEndian)
Array.Reverse(bytes);
return BitConverter.ToInt32(bytes, 0);
}
// @method Encrypt
// @param string plaintext
// @param string passphrase
// @param string passphrase salt (Base64 encoded)
// @return string
// @description Performs AES encryption on plaintext with PBKDF + IV, packs IV size, IV value, and ciphertext, and finally returns Base64 encoded string
public string Encrypt(string plaintext, string passphrase, string encoded_salt)
{
byte[] ciphertext;
byte[] iv = GenerateIV();
ciphertext = RawEncrypt(plaintext, DeriveKey(passphrase, System.Convert.FromBase64String(encoded_salt)), iv);
// Pack IV size, IV value, and ciphertext
byte[] packed = new byte[INTEGER_LEN + iv.Length + ciphertext.Length];
// Start packing
Array.Copy(Int2Byte(iv.Length), 0, packed, 0, INTEGER_LEN);
Array.Copy(iv, 0, packed, INTEGER_LEN, iv.Length);
Array.Copy(ciphertext, 0, packed, INTEGER_LEN + iv.Length, ciphertext.Length);
return System.Convert.ToBase64String(packed);
}
// @method RawEncrypt
// @param string plaintext
// @param string passphrase
// @param string passphrase salt (Base64 encoded)
// @return string
// @description Performs raw AES encryption on plaintext with PBKDF + IV, packs IV size, IV value, and ciphertext, and finally returns Base64 encoded string
public byte[] RawEncrypt(string plaintext, byte[] key, byte[] iv)
{
byte[] ciphertext;
RijndaelManaged aes = new RijndaelManaged();
aes.Mode = CIPHER_MODE;
aes.Padding = PADDING_MODE;
aes.KeySize = AES_KEY_LENGTH;
aes.BlockSize = AES_BLOCK_SIZE;
aes.Key = key;
aes.IV = iv;
ICryptoTransform cipher = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, cipher, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(plaintext);
}
ciphertext = ms.ToArray();
}
}
return ciphertext;
}
// @method ArraysCopyOfRange
// @param int index
// @param int length
// @description Port of Java's Arrays.copyOfRange to C# (http://stackoverflow.com/questions/21269727/how-to-convert-java-arrays-copyofrange-function-to-c)
public static byte[] CopyOfRange(byte[] src, int start, int end) {
int len = end - start;
byte[] dest = new byte[len];
Array.Copy(src, start, dest, 0, len);
return dest;
}
// @method Decrypt
// @param string ciphertext (Base64 encoded)
// @param string passphrase
// @param string passphrase salt (Base64 encoded)
// @return string plaintext
// @description Performs AES decryption on ciphertext with: PBKDF + IV (extracted from ciphertext)
public string Decrypt(string encoded_ciphertext, string passphrase, string encoded_salt)
{
// Start unpacking
byte[] packed = System.Convert.FromBase64String(encoded_ciphertext);
int iv_size = Byte2Int(CopyOfRange(packed, 0, INTEGER_LEN));
byte[] iv_value = CopyOfRange(packed, INTEGER_LEN, INTEGER_LEN + iv_size);
byte[] ciphertext = CopyOfRange(packed, INTEGER_LEN + iv_size, packed.Length);
return RawDecrypt(ciphertext, DeriveKey(passphrase, System.Convert.FromBase64String(encoded_salt)), iv_value);
}
// @method RawDecrypt
// @param string ciphertext (Base64 encoded)
// @param string passphrase
// @param string passphrase salt (Base64 encoded)
// @return string plaintext
// @description Performs raw AES decryption on ciphertext with: PBKDF + IV (extracted from ciphertext)
public string RawDecrypt(byte[] ciphertext, byte[] key, byte[] iv)
{
string plaintext;
RijndaelManaged aes = new RijndaelManaged();
aes.Mode = CIPHER_MODE;
aes.Padding = PADDING_MODE;
aes.KeySize = AES_KEY_LENGTH;
aes.BlockSize = AES_BLOCK_SIZE;
aes.Key = key;
aes.IV = iv;
ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(ciphertext))
{
using (CryptoStream cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
plaintext = sr.ReadToEnd();
}
}
}
return plaintext;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment