-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for AES encryption and key wrap algorithms (#16025)
* Add oct-HSM key type Resolves #14887 * Add additional encryption algorithms to Keys Resolves #14888 * Add AES-CBC and AES-GCM implementations/proxies * Add AES support to AesCryptographyProvider * Use factory methods for encrypt/decrypt options * Update public API
- Loading branch information
Showing
39 changed files
with
1,940 additions
and
233 deletions.
There are no files selected for viewing
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
66 changes: 65 additions & 1 deletion
66
sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs
Large diffs are not rendered by default.
Oops, something went wrong.
85 changes: 85 additions & 0 deletions
85
sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCbc.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System.Security.Cryptography; | ||
|
||
namespace Azure.Security.KeyVault.Keys.Cryptography | ||
{ | ||
/// <summary> | ||
/// Copied from Microsoft.Azure.KeyVault.Cryptography for vanilla AESCBC as defined in https://tools.ietf.org/html/rfc3394 | ||
/// </summary> | ||
internal class AesCbc | ||
{ | ||
public const int BlockByteSize = 16; | ||
|
||
public static readonly AesCbc Aes128Cbc = new AesCbc("A128CBC", 128, PaddingMode.Zeros); | ||
public static readonly AesCbc Aes192Cbc = new AesCbc("A192CBC", 192, PaddingMode.Zeros); | ||
public static readonly AesCbc Aes256Cbc = new AesCbc("A256CBC", 256, PaddingMode.Zeros); | ||
|
||
public static readonly AesCbc Aes128CbcPad = new AesCbc("A128CBCPAD", 128, PaddingMode.PKCS7); | ||
public static readonly AesCbc Aes192CbcPad = new AesCbc("A192CBCPAD", 192, PaddingMode.PKCS7); | ||
public static readonly AesCbc Aes256CbcPad = new AesCbc("A256CBCPAD", 256, PaddingMode.PKCS7); | ||
|
||
private AesCbc(string name, int keySize, PaddingMode padding) | ||
{ | ||
Name = name; | ||
KeySizeInBytes = keySize >> 3; | ||
Padding = padding; | ||
} | ||
|
||
public string Name { get; } | ||
|
||
public int KeySizeInBytes { get; } | ||
|
||
public PaddingMode Padding { get; } | ||
|
||
private static Aes Create( byte[] key, byte[] iv, PaddingMode padding ) | ||
{ | ||
var aes = Aes.Create(); | ||
|
||
aes.Mode = CipherMode.CBC; | ||
aes.Padding = padding; | ||
aes.KeySize = key.Length * 8; | ||
aes.Key = key; | ||
aes.IV = iv; | ||
|
||
return aes; | ||
} | ||
|
||
public ICryptoTransform CreateDecryptor( byte[] key, byte[] iv ) | ||
{ | ||
if ( key == null ) | ||
throw new CryptographicException( "No key material" ); | ||
|
||
if (key.Length < KeySizeInBytes) | ||
throw new CryptographicException("key", $"key must be at least {KeySizeInBytes << 3} bits"); | ||
|
||
if ( iv == null ) | ||
throw new CryptographicException( "No initialization vector" ); | ||
|
||
// Create the AES provider | ||
using ( var aes = Create( key.Take(KeySizeInBytes), iv, Padding ) ) | ||
{ | ||
return aes.CreateDecryptor(); | ||
} | ||
} | ||
|
||
public ICryptoTransform CreateEncryptor( byte[] key, byte[] iv ) | ||
{ | ||
if ( key == null ) | ||
throw new CryptographicException( "No key material" ); | ||
|
||
if (key.Length < KeySizeInBytes) | ||
throw new CryptographicException("key", $"key must be at least {KeySizeInBytes << 3} bits"); | ||
|
||
if ( iv == null ) | ||
throw new CryptographicException( "No initialization vector" ); | ||
|
||
// Create the AES provider | ||
using ( var aes = Create( key.Take(KeySizeInBytes), iv, Padding ) ) | ||
{ | ||
return aes.CreateEncryptor(); | ||
} | ||
} | ||
} | ||
} |
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
74 changes: 74 additions & 0 deletions
74
sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesGcmProxy.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Reflection; | ||
using System.Security.Cryptography; | ||
|
||
namespace Azure.Security.KeyVault.Keys.Cryptography | ||
{ | ||
/// <summary> | ||
/// Since System.Security.Cryptography.AesGcm requires targeting netstandard2.1, | ||
/// for needing this instance only we will proxy calls via reflection instead of multi-targeting. | ||
/// This feature will light up on netcoreapp3.0 or newer. | ||
/// </summary> | ||
internal class AesGcmProxy : IDisposable | ||
{ | ||
public const int NonceByteSize = 12; | ||
|
||
private static MethodInfo s_decryptMethod; | ||
private static MethodInfo s_encryptMethod; | ||
|
||
private readonly object _aes; | ||
|
||
private AesGcmProxy(object aes) | ||
{ | ||
_aes = aes ?? throw new ArgumentNullException(nameof(aes)); | ||
} | ||
|
||
public static bool TryCreate(byte[] key, out AesGcmProxy proxy) | ||
{ | ||
Type t = typeof(Aes).Assembly.GetType("System.Security.Cryptography.AesGcm", false); | ||
if (t != null) | ||
{ | ||
try | ||
{ | ||
object aes = Activator.CreateInstance(t, key); | ||
|
||
proxy = new AesGcmProxy(aes); | ||
return true; | ||
} | ||
catch | ||
{ | ||
} | ||
} | ||
|
||
proxy = null; | ||
return false; | ||
} | ||
|
||
public void Decrypt(byte[] nonce, byte[] ciphertext, byte[] tag, byte[] plaintext, byte[] associatedData = default) | ||
{ | ||
if (s_decryptMethod is null) | ||
{ | ||
s_decryptMethod = _aes.GetType().GetMethod(nameof(Decrypt), new Type[] { typeof(byte[]), typeof(byte[]), typeof(byte[]), typeof(byte[]), typeof(byte[]) }) ?? | ||
throw new InvalidOperationException($"{nameof(Decrypt)} method not found"); | ||
} | ||
|
||
s_decryptMethod.Invoke(_aes, new object[] { nonce, ciphertext, tag, plaintext, associatedData }); | ||
} | ||
|
||
public void Encrypt(byte[] nonce, byte[] plaintext, byte[] ciphertext, byte[] tag, byte[] associatedData = default) | ||
{ | ||
if (s_encryptMethod is null) | ||
{ | ||
s_encryptMethod = _aes.GetType().GetMethod(nameof(Encrypt), new Type[] { typeof(byte[]), typeof(byte[]), typeof(byte[]), typeof(byte[]), typeof(byte[]) }) ?? | ||
throw new InvalidOperationException($"{nameof(Encrypt)} method not found"); | ||
} | ||
|
||
s_encryptMethod.Invoke(_aes, new object[] { nonce, plaintext, ciphertext, tag, associatedData }); | ||
} | ||
|
||
public void Dispose() => ((IDisposable)_aes)?.Dispose(); | ||
} | ||
} |
Oops, something went wrong.