-
-
Save rdev5/baca6e2e8fe487d94cc6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @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