//[Fact] - Keeping this test for reference but we don't want to run it as an inner-loop test because // it creates a key on disk. public static void StoredKeyAES() { CngAlgorithm algname = new CngAlgorithm("AES"); string keyName = "CoreFxTest-" + Guid.NewGuid(); CngKey _cngKey = CngKey.Create(algname, keyName); try { using (Aes alg = new AesCng(keyName)) { int keySize = alg.KeySize; Assert.Equal(256, keySize); // Since this is a stored key, it's not going to surrender the actual key bytes. Assert.ThrowsAny<CryptographicException>(() => alg.Key); } } finally { _cngKey.Delete(); } }
/// <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> 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 AesCng { 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(); } } } }
/// <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> /// <remarks> /// Adds overhead of (Optional-Payload + BlockSize(16) + Message-Padded-To-Blocksize + HMac-Tag(32)) * 1.33 Base64 /// </remarks> 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 AesCng { 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(); } }
//[Fact] - Keeping this test for reference but we don't want to run it as an inner-loop test because // it creates a key on disk. public static void AesRoundTrip256BitsNoneECBUsingStoredKey() { CngAlgorithm algname = new CngAlgorithm("AES"); string keyName = "CoreFxTest-" + Guid.NewGuid(); CngKey _cngKey = CngKey.Create(algname, keyName); try { using (Aes alg = new AesCng(keyName)) { try { alg.Padding = PaddingMode.None; alg.Mode = CipherMode.ECB; int keySize = alg.KeySize; byte[] plainText = "15a818701f0f7c99fe4b1b4b860f131b".HexToByteArray(); byte[] cipher = alg.Encrypt(plainText); byte[] decrypted = alg.Decrypt(cipher); byte[] expectedDecrypted = "15a818701f0f7c99fe4b1b4b860f131b".HexToByteArray(); Assert.Equal<byte>(expectedDecrypted, decrypted); } catch (Exception e) { Console.WriteLine(e.Message); } } } finally { _cngKey.Delete(); } }