using System; using System.IO; using System.Security.Cryptography; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; namespace Xylem.Common.Cryptology.Security { /// /// Class for cryptology. /// public static class Cryptology { private const Int32 MacSize = 16; private const Int32 MacBitSize = MacSize * 8; /// /// AES-GCM Decryption using bouncy Castle nuget package /// NOTE - add nonce + salt as aad into GCM encryption/authentication /// /// /// /// /// byte array of key components /// encrypted cipher byte array = nonce | salt | cipherText | MAC /// encrypted cipher byte array /// /// - Initial based on code snipped from Ray Savarda. /// public static Byte[] AesGcmDecryption(Int32 saltSize, Int32 nonceSize, Int32 keySize, Byte[] primaryKey, Byte[] encryptedData) { // Extract nonce and salt from encryptedData var nonce = new Byte[nonceSize]; Array.Copy(encryptedData, nonce, nonceSize); var salt = new Byte[saltSize]; Array.Copy(encryptedData, nonceSize, salt, 0, saltSize); var aes256Key = BuildAes256Key(primaryKey, salt, keySize); //extract aad for GCM call var aad = new Byte[nonceSize + saltSize]; Array.Copy(encryptedData, 0, aad, 0, aad.Length); var cipher = new GcmBlockCipher(new AesEngine()); var parameters = new AeadParameters(new KeyParameter(aes256Key), MacBitSize, nonce, aad); cipher.Init(false, parameters); var gcmCipherText = new Byte[encryptedData.Length - aad.Length]; Array.Copy(encryptedData, aad.Length, gcmCipherText, 0, encryptedData.Length - aad.Length); var rawData = new Byte[cipher.GetOutputSize(gcmCipherText.Length)]; //Note aad handled by AadParameters call var len = cipher.ProcessBytes(gcmCipherText, 0, gcmCipherText.Length, rawData, 0); //TODO THW Need to add exception handlers - if MAC failure, get exception! /*var gcmResultCount =*/ cipher.DoFinal(rawData, len); return rawData; } /// /// AES-GCM Encryption using bouncy Castle nuget package /// NOTE - add nonce + salt as aad into GCM encryption/authentication /// /// /// /// /// byte array of key components /// raw data stream for encryption - payload /// encrypted cipher byte array = nonce | salt | cipherText | MAC /// /// - Initial based on code snipped from Ray Savarda. /// public static Byte[] AesGcmEncryption(Int32 saltSize, Int32 nonceSize, Int32 keySize, Byte[] primaryKey, Byte[] rawData) { // Initial salt generation var salt = BuildRandomValue(saltSize); var nonce = BuildRandomValue(nonceSize); var aes256Key = BuildAes256Key(primaryKey, salt, keySize); // put together the aad so it can be included in GCM call var aad = new Byte[nonceSize + saltSize]; Array.Copy(nonce, 0, aad, 0, nonce.Length); Array.Copy(salt, 0, aad, nonce.Length, salt.Length); var cipher = new GcmBlockCipher(new AesEngine()); //include AAD for MAC protection! var parameters = new AeadParameters(new KeyParameter(aes256Key), MacBitSize, nonce, aad); cipher.Init(true, parameters); var cipherText = new Byte[cipher.GetOutputSize(rawData.Length)]; var len = cipher.ProcessBytes(rawData, 0, rawData.Length, cipherText, 0); cipher.DoFinal(cipherText, len); //Note Cipher text includes MAC at end! //var noMac = new Byte[cipherText.Length - MacSize]; //Array.Copy(cipherText, noMac, cipherText.Length - MacSize); //var onlyMac = new Byte[MacSize]; //Array.ConstrainedCopy(cipherText, cipherText.Length - MacSize, onlyMac, 0, onlyMac.Length); //Assemble output memory stream = nonce + salt + cipherText + MAC var outMs = new MemoryStream(); using (var binaryWriter = new BinaryWriter(outMs)) { //Prepend Nonce binaryWriter.Write(nonce); //add salt binaryWriter.Write(salt); //Write Cipher Text binaryWriter.Write(cipherText); } return outMs.ToArray(); } /// /// Build random value /// /// /// byte array of random value /// /// - Initial based on code snipped from Ray Savarda. /// public static Byte[] BuildRandomValue(Int32 size) { var random = new Byte[size]; var rngCsp = new RNGCryptoServiceProvider(); rngCsp.GetBytes(random); return random; } /// /// Build AES256 key /// /// primary information for key generation /// /// /// AES256 key /// /// - Initial based on code snipped from Ray Savarda. /// public static Byte[] BuildAes256Key(Byte[] primaryKey, Byte[] salt, Int32 aes256KeySize) { const Int32 iterations = 10000; //(typically 2,000 - 10,000) var aes256Key = new Rfc2898DeriveBytes(primaryKey, salt, iterations, HashAlgorithmName.SHA256); return aes256Key.GetBytes(aes256KeySize); } } }