diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md index 1456639e01b5..af28e1641ce7 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added `KeyVaultKeyIdentifier` to parse key URIs. +- Added `LocalCryptographyClient` to do cryptography operations locally using a `JsonWebKey`. ## 4.1.0 (2020-08-11) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs index eb333c4bc934..e56c1a9b06a8 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/api/Azure.Security.KeyVault.Keys.netstandard2.0.cs @@ -64,6 +64,7 @@ public ImportKeyOptions(string name, Azure.Security.KeyVault.Keys.JsonWebKey key } public partial class JsonWebKey { + public JsonWebKey(System.Collections.Generic.IEnumerable keyOps) { } public JsonWebKey(System.Security.Cryptography.Aes aesProvider, System.Collections.Generic.IEnumerable keyOps = null) { } public JsonWebKey(System.Security.Cryptography.ECDsa ecdsa, bool includePrivateParameters = false, System.Collections.Generic.IEnumerable keyOps = null) { } public JsonWebKey(System.Security.Cryptography.RSA rsaProvider, bool includePrivateParameters = false, System.Collections.Generic.IEnumerable keyOps = null) { } @@ -373,6 +374,36 @@ public KeyResolver(Azure.Core.TokenCredential credential, Azure.Security.KeyVaul [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public override string ToString() { throw null; } } + public partial class LocalCryptographyClient : Azure.Core.Cryptography.IKeyEncryptionKey + { + protected LocalCryptographyClient() { } + public LocalCryptographyClient(Azure.Security.KeyVault.Keys.JsonWebKey jsonWebKey) { } + public string KeyId { get { throw null; } } + byte[] Azure.Core.Cryptography.IKeyEncryptionKey.UnwrapKey(string algorithm, System.ReadOnlyMemory encryptedKey, System.Threading.CancellationToken cancellationToken) { throw null; } + System.Threading.Tasks.Task Azure.Core.Cryptography.IKeyEncryptionKey.UnwrapKeyAsync(string algorithm, System.ReadOnlyMemory encryptedKey, System.Threading.CancellationToken cancellationToken) { throw null; } + byte[] Azure.Core.Cryptography.IKeyEncryptionKey.WrapKey(string algorithm, System.ReadOnlyMemory key, System.Threading.CancellationToken cancellationToken) { throw null; } + System.Threading.Tasks.Task Azure.Core.Cryptography.IKeyEncryptionKey.WrapKeyAsync(string algorithm, System.ReadOnlyMemory key, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.DecryptResult Decrypt(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm algorithm, byte[] ciphertext, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task DecryptAsync(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm algorithm, byte[] ciphertext, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.EncryptResult Encrypt(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm algorithm, byte[] plaintext, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task EncryptAsync(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm algorithm, byte[] plaintext, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.SignResult Sign(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] digest, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SignAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] digest, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.SignResult SignData(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] data, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.SignResult SignData(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, System.IO.Stream data, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SignDataAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] data, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task SignDataAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, System.IO.Stream data, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.UnwrapResult UnwrapKey(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm algorithm, byte[] encryptedKey, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task UnwrapKeyAsync(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm algorithm, byte[] encryptedKey, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.VerifyResult Verify(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] digest, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task VerifyAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] digest, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.VerifyResult VerifyData(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] data, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.VerifyResult VerifyData(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, System.IO.Stream data, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task VerifyDataAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, byte[] data, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task VerifyDataAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm algorithm, System.IO.Stream data, byte[] signature, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Security.KeyVault.Keys.Cryptography.WrapResult WrapKey(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm algorithm, byte[] key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task WrapKeyAsync(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm algorithm, byte[] key, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct SignatureAlgorithm : System.IEquatable { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCryptographyProvider.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCryptographyProvider.cs index e57802a05b44..be9bff4696ec 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCryptographyProvider.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCryptographyProvider.cs @@ -10,7 +10,7 @@ namespace Azure.Security.KeyVault.Keys.Cryptography { internal class AesCryptographyProvider : LocalCryptographyProvider { - internal AesCryptographyProvider(KeyVaultKey key) : base(key) + internal AesCryptographyProvider(JsonWebKey keyMaterial, KeyProperties keyProperties) : base(keyMaterial, keyProperties) { } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs index ad917df8946b..33753878d2f6 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/CryptographyClient.cs @@ -149,6 +149,8 @@ internal CryptographyClient(Uri keyId, KeyVaultPipeline pipeline) /// along with all other information needed to decrypt it. This information should be stored with the encrypted data. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task EncryptAsync(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) @@ -203,6 +205,8 @@ public virtual async Task EncryptAsync(EncryptionAlgorithm algori /// along with all other information needed to decrypt it. This information should be stored with the encrypted data. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual EncryptResult Encrypt(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) @@ -256,6 +260,8 @@ public virtual EncryptResult Encrypt(EncryptionAlgorithm algorithm, byte[] plain /// along with information regarding the algorithm and key used to decrypt it. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task DecryptAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) @@ -310,6 +316,8 @@ public virtual async Task DecryptAsync(EncryptionAlgorithm algori /// along with information regarding the algorithm and key used to decrypt it. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual DecryptResult Decrypt(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) @@ -363,6 +371,8 @@ public virtual DecryptResult Decrypt(EncryptionAlgorithm algorithm, byte[] ciphe /// along with all other information needed to unwrap it. This information should be stored with the wrapped key. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task WrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) @@ -417,6 +427,8 @@ public virtual async Task WrapKeyAsync(KeyWrapAlgorithm algorithm, b /// along with all other information needed to unwrap it. This information should be stored with the wrapped key. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual WrapResult WrapKey(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) @@ -470,6 +482,8 @@ public virtual WrapResult WrapKey(KeyWrapAlgorithm algorithm, byte[] key, Cancel /// along with information regarding the algorithm and key used to unwrap it. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task UnwrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) @@ -524,6 +538,8 @@ public virtual async Task UnwrapKeyAsync(KeyWrapAlgorithm algorith /// along with information regarding the algorithm and key used to unwrap it. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual UnwrapResult UnwrapKey(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) @@ -577,6 +593,8 @@ public virtual UnwrapResult UnwrapKey(KeyWrapAlgorithm algorithm, byte[] encrypt /// along with all other information needed to verify it. This information should be stored with the signature. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task SignAsync(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) @@ -631,6 +649,8 @@ public virtual async Task SignAsync(SignatureAlgorithm algorithm, by /// along with all other information needed to verify it. This information should be stored with the signature. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual SignResult Sign(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) @@ -684,6 +704,8 @@ public virtual SignResult Sign(SignatureAlgorithm algorithm, byte[] digest, Canc /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task VerifyAsync(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) @@ -738,6 +760,8 @@ public virtual async Task VerifyAsync(SignatureAlgorithm algorithm /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual VerifyResult Verify(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) @@ -791,6 +815,8 @@ public virtual VerifyResult Verify(SignatureAlgorithm algorithm, byte[] digest, /// along with all other information needed to verify it. This information should be stored with the signature. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task SignDataAsync(SignatureAlgorithm algorithm, byte[] data, CancellationToken cancellationToken = default) @@ -849,6 +875,8 @@ public virtual async Task SignDataAsync(SignatureAlgorithm algorithm /// along with all other information needed to verify it. This information should be stored with the signature. /// /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual SignResult SignData(SignatureAlgorithm algorithm, byte[] data, CancellationToken cancellationToken = default) @@ -908,6 +936,8 @@ public virtual SignResult SignData(SignatureAlgorithm algorithm, byte[] data, Ca /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task SignDataAsync(SignatureAlgorithm algorithm, Stream data, CancellationToken cancellationToken = default) @@ -967,6 +997,8 @@ public virtual async Task SignDataAsync(SignatureAlgorithm algorithm /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual SignResult SignData(SignatureAlgorithm algorithm, Stream data, CancellationToken cancellationToken = default) @@ -1025,6 +1057,8 @@ public virtual SignResult SignData(SignatureAlgorithm algorithm, Stream data, Ca /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task VerifyDataAsync(SignatureAlgorithm algorithm, byte[] data, byte[] signature, CancellationToken cancellationToken = default) @@ -1084,6 +1118,8 @@ public virtual async Task VerifyDataAsync(SignatureAlgorithm algor /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual VerifyResult VerifyData(SignatureAlgorithm algorithm, byte[] data, byte[] signature, CancellationToken cancellationToken = default) @@ -1142,6 +1178,8 @@ public virtual VerifyResult VerifyData(SignatureAlgorithm algorithm, byte[] data /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual async Task VerifyDataAsync(SignatureAlgorithm algorithm, Stream data, byte[] signature, CancellationToken cancellationToken = default) @@ -1201,6 +1239,8 @@ public virtual async Task VerifyDataAsync(SignatureAlgorithm algor /// /// The specified does not match the key corresponding to the key identifier. /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. /// The operation is not supported with the specified key. /// The server returned an error. See for details returned from the server. public virtual VerifyResult VerifyData(SignatureAlgorithm algorithm, Stream data, byte[] signature, CancellationToken cancellationToken = default) @@ -1281,13 +1321,13 @@ async Task IKeyEncryptionKey.UnwrapKeyAsync(string algorithm, ReadOnlyMe return result.Key; } - private static byte[] CreateDigest(SignatureAlgorithm algorithm, byte[] data) + internal static byte[] CreateDigest(SignatureAlgorithm algorithm, byte[] data) { using HashAlgorithm hashAlgo = algorithm.GetHashAlgorithm(); return hashAlgo.ComputeHash(data); } - private static byte[] CreateDigest(SignatureAlgorithm algorithm, Stream data) + internal static byte[] CreateDigest(SignatureAlgorithm algorithm, Stream data) { using HashAlgorithm hashAlgo = algorithm.GetHashAlgorithm(); return hashAlgo.ComputeHash(data); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/EcCryptographyProvider.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/EcCryptographyProvider.cs index b33d4cb11908..1778e1123aec 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/EcCryptographyProvider.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/EcCryptographyProvider.cs @@ -13,13 +13,12 @@ internal class EcCryptographyProvider : LocalCryptographyProvider private readonly KeyCurveName _curve; private readonly JsonWebKey _keyMaterial; - internal EcCryptographyProvider(KeyVaultKey key) : base(key) + internal EcCryptographyProvider(JsonWebKey keyMaterial, KeyProperties keyProperties) : base(keyMaterial, keyProperties) { // Unset the KeyMaterial since we want to conditionally set it if supported. KeyMaterial = null; // Only set the JWK if we support the algorithm locally. - JsonWebKey keyMaterial = key.Key; if (keyMaterial != null && keyMaterial.CurveName.HasValue) { // Save the key material to use for operational support validation. diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyClient.cs new file mode 100644 index 000000000000..0dde7f4262b2 --- /dev/null +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyClient.cs @@ -0,0 +1,630 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using Azure.Core; +using Azure.Core.Cryptography; + +#pragma warning disable AZC0015 // Unexpected client method return type. + +namespace Azure.Security.KeyVault.Keys.Cryptography +{ + /// + /// A client used to perform local cryptographic operations without connecting to Azure Key Vault. + /// + public class LocalCryptographyClient : IKeyEncryptionKey + { + private readonly JsonWebKey _jsonWebKey; + private readonly ICryptographyProvider _provider; + + /// + /// Initializes a new instance of the class. + /// + /// A used for cryptographic operations. + /// is null. + /// The has a that is not supported. +#pragma warning disable AZC0007 // DO provide a minimal constructor that takes only the parameters required to connect to the service. + public LocalCryptographyClient(JsonWebKey jsonWebKey) + { + _jsonWebKey = jsonWebKey ?? throw new ArgumentNullException(nameof(jsonWebKey)); + _provider = LocalCryptographyProviderFactory.Create(jsonWebKey, null) ?? throw new NotSupportedException(@$"Key type ""{jsonWebKey.KeyType}"" is not supported"); + } +#pragma warning restore AZC0007 // DO provide a minimal constructor that takes only the parameters required to connect to the service. + + /// + /// Initializes a new instance of the class for mocking. + /// + protected LocalCryptographyClient() + { + } + + /// + /// Gets the of the key used to perform local cryptographic operations for the client. + /// + public string KeyId => _jsonWebKey.Id; + + /// + /// Encrypts the specified plain text. + /// + /// The to use. + /// The data to encrypt. + /// A to cancel the operation. + /// + /// The result of the encrypt operation. The returned contains the encrypted data + /// along with all other information needed to decrypt it. This information should be stored with the encrypted data. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + public virtual async Task EncryptAsync(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) + { + EncryptResult result = null; + if (_provider.SupportsOperation(KeyOperation.Encrypt)) + { + result = await _provider.EncryptAsync(algorithm, plaintext, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Encrypt)); + } + + /// + /// Encrypts the specified plain text. + /// + /// The to use. + /// The data to encrypt. + /// A to cancel the operation. + /// + /// The result of the encrypt operation. The returned contains the encrypted data + /// along with all other information needed to decrypt it. This information should be stored with the encrypted data. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + public virtual EncryptResult Encrypt(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) + { + EncryptResult result = null; + if (_provider.SupportsOperation(KeyOperation.Encrypt)) + { + result = _provider.Encrypt(algorithm, plaintext, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Encrypt)); + } + + /// + /// Decrypts the specified cipher text. + /// + /// The to use. + /// The encrypted data to decrypt. + /// A to cancel the operation. + /// + /// The result of the decrypt operation. The returned contains the encrypted data + /// along with information regarding the algorithm and key used to decrypt it. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + public virtual async Task DecryptAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) + { + DecryptResult result = null; + if (_provider.SupportsOperation(KeyOperation.Decrypt)) + { + result = await _provider.DecryptAsync(algorithm, ciphertext, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Decrypt)); + } + + /// + /// Decrypts the specified cipher text. + /// + /// The to use. + /// The encrypted data to decrypt. + /// A to cancel the operation. + /// + /// The result of the decrypt operation. The returned contains the encrypted data + /// along with information regarding the algorithm and key used to decrypt it. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + public virtual DecryptResult Decrypt(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) + { + DecryptResult result = null; + if (_provider.SupportsOperation(KeyOperation.Decrypt)) + { + result = _provider.Decrypt(algorithm, ciphertext, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Decrypt)); + } + + /// + /// Encrypts the specified key. + /// + /// The to use. + /// The key to encrypt. + /// A to cancel the operation. + /// + /// The result of the wrap operation. The returned contains the wrapped key + /// along with all other information needed to unwrap it. This information should be stored with the wrapped key. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + public virtual async Task WrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) + { + WrapResult result = null; + if (_provider.SupportsOperation(KeyOperation.WrapKey)) + { + result = await _provider.WrapKeyAsync(algorithm, key, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.WrapKey)); + } + + /// + /// Encrypts the specified key. + /// + /// The to use. + /// The key to encrypt. + /// A to cancel the operation. + /// + /// The result of the wrap operation. The returned contains the wrapped key + /// along with all other information needed to unwrap it. This information should be stored with the wrapped key. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual WrapResult WrapKey(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) + { + WrapResult result = null; + if (_provider.SupportsOperation(KeyOperation.WrapKey)) + { + result = _provider.WrapKey(algorithm, key, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.WrapKey)); + } + + /// + /// Decrypts the specified encrypted key. + /// + /// The to use. + /// The encrypted key. + /// A to cancel the operation. + /// + /// The result of the unwrap operation. The returned contains the key + /// along with information regarding the algorithm and key used to unwrap it. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task UnwrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) + { + UnwrapResult result = null; + if (_provider.SupportsOperation(KeyOperation.UnwrapKey)) + { + result = await _provider.UnwrapKeyAsync(algorithm, encryptedKey, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.UnwrapKey)); + } + + /// + /// Decrypts the specified encrypted key. + /// + /// The to use. + /// The encrypted key. + /// A to cancel the operation. + /// + /// The result of the unwrap operation. The returned contains the key + /// along with information regarding the algorithm and key used to unwrap it. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual UnwrapResult UnwrapKey(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) + { + UnwrapResult result = null; + if (_provider.SupportsOperation(KeyOperation.UnwrapKey)) + { + result = _provider.UnwrapKey(algorithm, encryptedKey, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.UnwrapKey)); + } + + /// + /// Signs the specified digest. + /// + /// The to use. + /// The pre-hashed digest to sign. The hash algorithm used to compute the digest must be compatable with the specified algorithm. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task SignAsync(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) + { + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + result = await _provider.SignAsync(algorithm, digest, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Signs the specified digest. + /// + /// The to use. + /// The pre-hashed digest to sign. The hash algorithm used to compute the digest must be compatable with the specified algorithm. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual SignResult Sign(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) + { + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + result = _provider.Sign(algorithm, digest, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the digest. + /// The pre-hashed digest corresponding to the signature. The hash algorithm used to compute the digest must be compatable with the specified algorithm. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task VerifyAsync(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) + { + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + result = await _provider.VerifyAsync(algorithm, digest, signature, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the digest. + /// The pre-hashed digest corresponding to the signature. The hash algorithm used to compute the digest must be compatable with the specified algorithm. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual VerifyResult Verify(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) + { + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + result = _provider.Verify(algorithm, digest, signature, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + /// Signs the specified data. + /// + /// The to use. + /// The data to sign. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task SignDataAsync(SignatureAlgorithm algorithm, byte[] data, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = await _provider.SignAsync(algorithm, digest, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Signs the specified data. + /// + /// The to use. + /// The data to sign. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual SignResult SignData(SignatureAlgorithm algorithm, byte[] data, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = _provider.Sign(algorithm, digest, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Signs the specified data. + /// + /// The to use. + /// The data to sign. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task SignDataAsync(SignatureAlgorithm algorithm, Stream data, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = await _provider.SignAsync(algorithm, digest, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Signs the specified data. + /// + /// The to use. + /// The data to sign. + /// A to cancel the operation. + /// + /// The result of the sign operation. The returned contains the signature + /// along with all other information needed to verify it. This information should be stored with the signature. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual SignResult SignData(SignatureAlgorithm algorithm, Stream data, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + SignResult result = null; + if (_provider.SupportsOperation(KeyOperation.Sign)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = _provider.Sign(algorithm, digest, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Sign)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the data. + /// The data corresponding to the signature. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task VerifyDataAsync(SignatureAlgorithm algorithm, byte[] data, byte[] signature, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = await _provider.VerifyAsync(algorithm, digest, signature, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the data. + /// The data corresponding to the signature. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual VerifyResult VerifyData(SignatureAlgorithm algorithm, byte[] data, byte[] signature, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = _provider.Verify(algorithm, digest, signature, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the data. + /// The data corresponding to the signature. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual async Task VerifyDataAsync(SignatureAlgorithm algorithm, Stream data, byte[] signature, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = await _provider.VerifyAsync(algorithm, digest, signature, cancellationToken).ConfigureAwait(false); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + /// Verifies the specified signature. + /// + /// The to use. This must be the same algorithm used to sign the data. + /// The data corresponding to the signature. + /// The signature to verify. + /// A to cancel the operation. + /// + /// The result of the verify operation. If the signature is valid the property of the returned will be set to true. + /// + /// The specified does not match the key corresponding to the key identifier. + /// is null. + /// The local cryptographic provider threw an exception. + /// The key is invalid for the current operation. + /// The operation is not supported with the specified key. + + public virtual VerifyResult VerifyData(SignatureAlgorithm algorithm, Stream data, byte[] signature, CancellationToken cancellationToken = default) + { + Argument.AssertNotNull(data, nameof(data)); + + VerifyResult result = null; + if (_provider.SupportsOperation(KeyOperation.Verify)) + { + byte[] digest = CryptographyClient.CreateDigest(algorithm, data); + result = _provider.Verify(algorithm, digest, signature, cancellationToken); + } + + return result ?? throw LocalCryptographyProvider.CreateOperationNotSupported(nameof(KeyOperation.Verify)); + } + + /// + byte[] IKeyEncryptionKey.WrapKey(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken) + { + WrapResult result = WrapKey(new KeyWrapAlgorithm(algorithm), key.ToArray(), cancellationToken); + + return result.EncryptedKey; + } + + /// + async Task IKeyEncryptionKey.WrapKeyAsync(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken) + { + WrapResult result = await WrapKeyAsync(new KeyWrapAlgorithm(algorithm), key.ToArray(), cancellationToken).ConfigureAwait(false); + + return result.EncryptedKey; + } + + /// + byte[] IKeyEncryptionKey.UnwrapKey(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken) + { + byte[] buffer = MemoryMarshal.TryGetArray(encryptedKey, out ArraySegment segment) ? segment.Array : encryptedKey.ToArray(); + UnwrapResult result = UnwrapKey(new KeyWrapAlgorithm(algorithm), buffer, cancellationToken); + + return result.Key; + } + + /// + async Task IKeyEncryptionKey.UnwrapKeyAsync(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken) + { + byte[] buffer = MemoryMarshal.TryGetArray(encryptedKey, out ArraySegment segment) ? segment.Array : encryptedKey.ToArray(); + UnwrapResult result = await UnwrapKeyAsync(new KeyWrapAlgorithm(algorithm), buffer, cancellationToken).ConfigureAwait(false); + + return result.Key; + } + } +} diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProvider.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProvider.cs index 5a6832fbab8a..afda90f761f7 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProvider.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProvider.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -9,13 +10,12 @@ namespace Azure.Security.KeyVault.Keys.Cryptography { internal abstract class LocalCryptographyProvider : ICryptographyProvider { - private readonly KeyVaultKey _key; + private readonly KeyProperties _keyProperties; - public LocalCryptographyProvider(KeyVaultKey key) + public LocalCryptographyProvider(JsonWebKey keyMaterial, KeyProperties keyProperties) { - _key = key ?? throw new ArgumentNullException(nameof(key)); - - KeyMaterial = key.Key; + KeyMaterial = keyMaterial ?? throw new ArgumentNullException(nameof(keyMaterial)); + _keyProperties = keyProperties; } public bool ShouldRemote => KeyMaterial?.Id != null; @@ -28,7 +28,7 @@ public LocalCryptographyProvider(KeyVaultKey key) public virtual DecryptResult Decrypt(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(Decrypt)); } public virtual Task DecryptAsync(EncryptionAlgorithm algorithm, byte[] ciphertext, CancellationToken cancellationToken = default) @@ -39,7 +39,7 @@ public virtual Task DecryptAsync(EncryptionAlgorithm algorithm, b public virtual EncryptResult Encrypt(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(Encrypt)); } public virtual Task EncryptAsync(EncryptionAlgorithm algorithm, byte[] plaintext, CancellationToken cancellationToken = default) @@ -50,7 +50,7 @@ public virtual Task EncryptAsync(EncryptionAlgorithm algorithm, b public virtual SignResult Sign(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(Sign)); } public virtual Task SignAsync(SignatureAlgorithm algorithm, byte[] digest, CancellationToken cancellationToken = default) @@ -61,7 +61,7 @@ public virtual Task SignAsync(SignatureAlgorithm algorithm, byte[] d public virtual UnwrapResult UnwrapKey(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(UnwrapKey)); } public virtual Task UnwrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] encryptedKey, CancellationToken cancellationToken = default) @@ -72,7 +72,7 @@ public virtual Task UnwrapKeyAsync(KeyWrapAlgorithm algorithm, byt public virtual VerifyResult Verify(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(Verify)); } public virtual Task VerifyAsync(SignatureAlgorithm algorithm, byte[] digest, byte[] signature, CancellationToken cancellationToken = default) @@ -83,7 +83,7 @@ public virtual Task VerifyAsync(SignatureAlgorithm algorithm, byte public virtual WrapResult WrapKey(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) { - throw new NotSupportedException(); + throw CreateOperationNotSupported(nameof(WrapKey)); } public virtual Task WrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] key, CancellationToken cancellationToken = default) @@ -92,17 +92,23 @@ public virtual Task WrapKeyAsync(KeyWrapAlgorithm algorithm, byte[] return Task.FromResult(result); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static NotSupportedException CreateOperationNotSupported(string name) => new NotSupportedException($"Operation {name} not supported with the given key"); + protected void ThrowIfTimeInvalid() { - DateTimeOffset now = DateTimeOffset.Now; - if (_key.Properties.NotBefore.HasValue && now < _key.Properties.NotBefore.Value) - { - throw new InvalidOperationException($"The key \"{_key.Name}\" is not valid before {_key.Properties.NotBefore.Value:r}."); - } - - if (_key.Properties.ExpiresOn.HasValue && now > _key.Properties.ExpiresOn.Value) + if (_keyProperties != null) { - throw new InvalidOperationException($"The key \"{_key.Name}\" is not valid after {_key.Properties.ExpiresOn.Value:r}."); + DateTimeOffset now = DateTimeOffset.Now; + if (_keyProperties.NotBefore.HasValue && now < _keyProperties.NotBefore.Value) + { + throw new InvalidOperationException($"The key \"{_keyProperties.Name}\" is not valid before {_keyProperties.NotBefore.Value:r}."); + } + + if (_keyProperties.ExpiresOn.HasValue && now > _keyProperties.ExpiresOn.Value) + { + throw new InvalidOperationException($"The key \"{_keyProperties.Name}\" is not valid after {_keyProperties.ExpiresOn.Value:r}."); + } } } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProviderFactory.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProviderFactory.cs index f463d590bd75..4bebf378ea02 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProviderFactory.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/LocalCryptographyProviderFactory.cs @@ -5,24 +5,25 @@ namespace Azure.Security.KeyVault.Keys.Cryptography { internal static class LocalCryptographyProviderFactory { - public static ICryptographyProvider Create(KeyVaultKey key) + public static ICryptographyProvider Create(KeyVaultKey key) => Create(key?.Key, key?.Properties); + + public static ICryptographyProvider Create(JsonWebKey keyMaterial, KeyProperties keyProperties) { - JsonWebKey keyMaterial = key?.Key; if (keyMaterial != null) { if (keyMaterial.KeyType == KeyType.Rsa || keyMaterial.KeyType == KeyType.RsaHsm) { - return new RsaCryptographyProvider(key); + return new RsaCryptographyProvider(keyMaterial, keyProperties); } if (keyMaterial.KeyType == KeyType.Ec || keyMaterial.KeyType == KeyType.EcHsm) { - return new EcCryptographyProvider(key); + return new EcCryptographyProvider(keyMaterial, keyProperties); } if (keyMaterial.KeyType == KeyType.Oct) { - return new AesCryptographyProvider(key); + return new AesCryptographyProvider(keyMaterial, keyProperties); } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RsaCryptographyProvider.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RsaCryptographyProvider.cs index a48531025411..d2bb4611812e 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RsaCryptographyProvider.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RsaCryptographyProvider.cs @@ -9,7 +9,7 @@ namespace Azure.Security.KeyVault.Keys.Cryptography { internal class RsaCryptographyProvider : LocalCryptographyProvider { - internal RsaCryptographyProvider(KeyVaultKey key) : base(key) + internal RsaCryptographyProvider(JsonWebKey keyMaterial, KeyProperties keyProperties) : base(keyMaterial, keyProperties) { } @@ -222,13 +222,13 @@ public override UnwrapResult UnwrapKey(KeyWrapAlgorithm algorithm, byte[] encryp private byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) { - using RSA rsa = KeyMaterial.ToRSA(true); + using RSA rsa = KeyMaterial.ToRSA(); return rsa.Encrypt(data, padding); } private byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) { - using RSA rsa = KeyMaterial.ToRSA(); + using RSA rsa = KeyMaterial.ToRSA(true); return rsa.Decrypt(data, padding); } } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/JsonWebKey.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/JsonWebKey.cs index 247250e115e0..5ed6d6a93ed9 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/JsonWebKey.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/JsonWebKey.cs @@ -78,7 +78,14 @@ internal JsonWebKey() : this(null) { } - internal JsonWebKey(IEnumerable keyOps) + /// + /// Initializes a new instance of the class with the given key operations. + /// + /// + /// A list of supported values. + /// If null, no operations will be permitted and subsequent cryptography operations may fail. + /// + public JsonWebKey(IEnumerable keyOps) { _keyOps = keyOps is null ? new List() : new List(keyOps); KeyOps = new ReadOnlyCollection(_keyOps); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyType.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyType.cs index b693289b2d44..629853b6283e 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyType.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyType.cs @@ -11,6 +11,12 @@ namespace Azure.Security.KeyVault.Keys /// public readonly struct KeyType : IEquatable { + internal const string EcValue = "EC"; + internal const string EcHsmValue = "EC-HSM"; + internal const string RsaValue = "RSA"; + internal const string RsaHsmValue = "RSA-HSM"; + internal const string OctValue = "oct"; + private readonly string _value; /// @@ -25,27 +31,27 @@ public KeyType(string value) /// /// An Elliptic Curve Cryptographic (ECC) algorithm. /// - public static KeyType Ec { get; } = new KeyType("EC"); + public static KeyType Ec { get; } = new KeyType(EcValue); /// /// An Elliptic Curve Cryptographic (ECC) algorithm backed by HSM. /// - public static KeyType EcHsm { get; } = new KeyType("EC-HSM"); + public static KeyType EcHsm { get; } = new KeyType(EcHsmValue); /// /// An RSA cryptographic algorithm. /// - public static KeyType Rsa { get; } = new KeyType("RSA"); + public static KeyType Rsa { get; } = new KeyType(RsaValue); /// /// An RSA cryptographic algorithm backed by HSM. /// - public static KeyType RsaHsm { get; } = new KeyType("RSA-HSM"); + public static KeyType RsaHsm { get; } = new KeyType(RsaHsmValue); /// /// An AES cryptographic algorithm. /// - public static KeyType Oct { get; } = new KeyType("oct"); + public static KeyType Oct { get; } = new KeyType(OctValue); /// /// Determines if two values are the same. diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeysEventSource.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeysEventSource.cs index e4249066095b..26cd4919e2db 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeysEventSource.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeysEventSource.cs @@ -32,7 +32,7 @@ public void AlgorithmNotSupported(string operation, T algorithm) where T : no } } - [Event(AlgorithmNotSupportedEvent, Level = EventLevel.Verbose, Message = "The algorithm {1} is not supported on this machine. Will try the {0} operation in the service.")] + [Event(AlgorithmNotSupportedEvent, Level = EventLevel.Verbose, Message = "The algorithm {1} is not supported on this machine. Cannot perform the {0} operation locally.")] public void AlgorithmNotSupported(string operation, string algorithm) => WriteEvent(AlgorithmNotSupportedEvent, operation, algorithm); [NonEvent] @@ -45,7 +45,7 @@ public void KeyTypeNotSupported(string operation, KeyVaultKey key) } } - [Event(KeyTypeNotSupportedEvent, Level = EventLevel.Verbose, Message = "The key type {1} is not supported on this machine. Will try the {0} operation in the service.")] + [Event(KeyTypeNotSupportedEvent, Level = EventLevel.Verbose, Message = "The key type {1} is not supported on this machine. Cannot perform the {0} operation locally.")] public void KeyTypeNotSupported(string operation, string keyType) => WriteEvent(KeyTypeNotSupportedEvent, operation, keyType); [Event(PrivateKeyRequiredEvent, Level = EventLevel.Verbose, Message = "A private key is required for a {0} operation")] @@ -61,7 +61,7 @@ public void CryptographicException(string operation, Exception ex) } } - [Event(CryptographicExceptionEvent, Level = EventLevel.Informational, Message = "A cryptographic exception occured: {1}.\r\nWill try the {0} operation in the service.")] + [Event(CryptographicExceptionEvent, Level = EventLevel.Informational, Message = "A cryptographic exception occured: {1}.\r\nCannot perform the {0} operation locally.")] public void CryptographicException(string operation, string message) => WriteEvent(CryptographicExceptionEvent, operation, message); private static string FormatException(Exception ex) diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/AesCryptographyProviderTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/AesCryptographyProviderTests.cs index 9bd32e3db3d4..bc42891dfb77 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/AesCryptographyProviderTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/AesCryptographyProviderTests.cs @@ -24,7 +24,7 @@ public void WrapKeyBeforeValidDate() }, }; - AesCryptographyProvider provider = new AesCryptographyProvider(key); + AesCryptographyProvider provider = new AesCryptographyProvider(key.Key, key.Properties); byte[] ek = { 0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7 }; InvalidOperationException ex = Assert.Throws(() => provider.WrapKey(KeyWrapAlgorithm.A128KW, ek, default)); @@ -45,7 +45,7 @@ public void WrapKeyAfterValidDate() }, }; - AesCryptographyProvider provider = new AesCryptographyProvider(key); + AesCryptographyProvider provider = new AesCryptographyProvider(key.Key, key.Properties); byte[] ek = { 0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7 }; InvalidOperationException ex = Assert.Throws(() => provider.WrapKey(KeyWrapAlgorithm.A128KW, ek, default)); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/CryptographyClientLiveTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/CryptographyClientLiveTests.cs index c898602430ea..06ad9d2c176c 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/CryptographyClientLiveTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/CryptographyClientLiveTests.cs @@ -420,48 +420,7 @@ private async Task CreateTestKeyWithKeyMaterial(SignatureAlgorithm { string keyName = Recording.GenerateId(); - JsonWebKey keyMaterial = null; - switch (algorithm.ToString()) - { - case SignatureAlgorithm.PS256Value: - case SignatureAlgorithm.PS384Value: - case SignatureAlgorithm.PS512Value: - case SignatureAlgorithm.RS256Value: - case SignatureAlgorithm.RS384Value: - case SignatureAlgorithm.RS512Value: - keyMaterial = KeyUtilities.CreateRsaKey(includePrivateParameters: true); - break; - - case SignatureAlgorithm.ES256Value: - case SignatureAlgorithm.ES256KValue: - case SignatureAlgorithm.ES384Value: - case SignatureAlgorithm.ES512Value: -#if NET461 - Assert.Ignore("Creating JsonWebKey with ECDsa is not supported on net461."); -#else - KeyCurveName curveName = algorithm.GetEcKeyCurveName(); - ECCurve curve = ECCurve.CreateFromOid(curveName.Oid); - - using (ECDsa ecdsa = ECDsa.Create()) - { - try - { - ecdsa.GenerateKey(curve); - keyMaterial = new JsonWebKey(ecdsa, includePrivateParameters: true); - } - catch (NotSupportedException) - { - Assert.Inconclusive("This platform does not support OID {0}", curveName.Oid); - } - } -#endif - - break; - - default: - throw new ArgumentException("Invalid Algorithm", nameof(algorithm)); - } - + JsonWebKey keyMaterial = KeyUtilities.CreateKey(algorithm, includePrivateParameters: true); KeyVaultKey key = await Client.ImportKeyAsync(keyName, keyMaterial); keyMaterial.Id = key.Key.Id; diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/EcCryptographyProviderTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/EcCryptographyProviderTests.cs index a71109d9bd00..d1bd3ae57484 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/EcCryptographyProviderTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/EcCryptographyProviderTests.cs @@ -24,7 +24,7 @@ public void SupportsOperation(string operationValue, bool supported) { JsonWebKey jwk = KeyModelFactory.JsonWebKey(KeyType.Ec, curveName: KeyCurveName.P256, keyOps: new[] { KeyOperation.Sign, KeyOperation.Verify }); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); KeyOperation operation = new KeyOperation(operationValue); Assert.AreEqual(supported, client.SupportsOperation(operation)); @@ -35,7 +35,7 @@ public void SupportsOperationUnsupportedCurve() { JsonWebKey jwk = KeyModelFactory.JsonWebKey(KeyType.Ec, curveName: "invalid", keyOps: new[] { KeyOperation.Sign, KeyOperation.Verify }); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); // The provider caches the original allow key operations to facilitate tracing. Operation will still be sent to the service. Assert.IsTrue(client.SupportsOperation(KeyOperation.Sign)); @@ -46,7 +46,7 @@ public void SupportsOperationUnauthorizedOperation() { JsonWebKey jwk = KeyModelFactory.JsonWebKey(KeyType.Ec, curveName: "invalid", keyOps: new[] { KeyOperation.Verify }); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); Assert.IsFalse(client.SupportsOperation(KeyOperation.Sign)); } @@ -58,7 +58,7 @@ public async Task Sign() ecdsa.GenerateKey(ECCurve.NamedCurves.nistP256); JsonWebKey jwk = new JsonWebKey(ecdsa, true) { Id = "test" }; - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; @@ -75,7 +75,7 @@ public void SignThrowsOnNullDigest() using ECDsa ecdsa = ECDsa.Create(); JsonWebKey jwk = new JsonWebKey(ecdsa); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); Assert.Throws(() => client.Sign(algorithm, null, default)); @@ -84,7 +84,7 @@ public void SignThrowsOnNullDigest() [TestCaseSource(nameof(GetInvalidKeys))] public void SignThrowsOnInvalidKey(JsonWebKey jwk, SignatureAlgorithm algorithm) { - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); byte[] digest = new byte[1] { 0xff }; Assert.Throws(() => client.Sign(algorithm, digest, default), "Expected exception with wrong key length"); @@ -102,7 +102,7 @@ public void SignReturnsNullWithoutPrivateKey() Id = "test", }; - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); Assert.IsNull(client.Sign(algorithm, new byte[] { 0xff }, default)); @@ -113,7 +113,7 @@ public void SignReturnsNullOnUnsupported() { JsonWebKey jwk = KeyModelFactory.JsonWebKey(KeyType.Ec, curveName: "invalid", keyOps: new[] { KeyOperation.Sign }); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignResult result = client.Sign(default, new byte[] { 0xff }, default); Assert.IsNull(result); @@ -126,7 +126,7 @@ public async Task Verify() ecdsa.GenerateKey(ECCurve.NamedCurves.nistP256); JsonWebKey jwk = new JsonWebKey(ecdsa, true) { Id = "test" }; - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; @@ -144,7 +144,7 @@ public void VerifyThrowsOnNullDigest() using ECDsa ecdsa = ECDsa.Create(); JsonWebKey jwk = new JsonWebKey(ecdsa); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); Assert.Throws(() => client.Verify(algorithm, null, null, default)); @@ -156,7 +156,7 @@ public void VerifyThrowsOnNullSignature() using ECDsa ecdsa = ECDsa.Create(); JsonWebKey jwk = new JsonWebKey(ecdsa); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); SignatureAlgorithm algorithm = GetSignatureAlgorithm(jwk); Assert.Throws(() => client.Verify(algorithm, new byte[] { 0xff }, null, default)); @@ -165,7 +165,7 @@ public void VerifyThrowsOnNullSignature() [TestCaseSource(nameof(GetInvalidKeys))] public void VerifyThrowsOnInvalidKey(JsonWebKey jwk, SignatureAlgorithm algorithm) { - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); byte[] digest = new byte[] { 0xff }; byte[] signature = new byte[] { 0xff, 0xff }; @@ -177,7 +177,7 @@ public void VerifyReturnsNullOnUnsupported() { JsonWebKey jwk = KeyModelFactory.JsonWebKey(KeyType.Ec, curveName: "invalid", keyOps: new[] { KeyOperation.Sign }); - EcCryptographyProvider client = new EcCryptographyProvider(new KeyVaultKey { Key = jwk }); + EcCryptographyProvider client = new EcCryptographyProvider(jwk, null); byte[] digest = new byte[] { 0xff }; byte[] signature = new byte[] { 0xff, 0xff }; @@ -200,7 +200,7 @@ public void SignBeforeValidDate() }, }; - EcCryptographyProvider client = new EcCryptographyProvider(key); + EcCryptographyProvider client = new EcCryptographyProvider(key.Key, key.Properties); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; InvalidOperationException ex = Assert.Throws(() => client.Sign(GetSignatureAlgorithm(key.Key), digest, default)); @@ -221,7 +221,7 @@ public void SignAfterValidDate() }, }; - EcCryptographyProvider client = new EcCryptographyProvider(key); + EcCryptographyProvider client = new EcCryptographyProvider(key.Key, key.Properties); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; InvalidOperationException ex = Assert.Throws(() => client.Sign(GetSignatureAlgorithm(key.Key), digest, default)); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/JsonWebKeyTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/JsonWebKeyTests.cs index eb29d8928554..40afc320bfc6 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/JsonWebKeyTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/JsonWebKeyTests.cs @@ -14,6 +14,13 @@ namespace Azure.Security.KeyVault.Keys.Tests { public class JsonWebKeyTests { + [Test] + public void EmptyKeyOps() + { + JsonWebKey jwk = new JsonWebKey(null); + Assert.IsEmpty(jwk.KeyOps); + } + [Test] public void AesDefaultsKeyOps() { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientLiveTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientLiveTests.cs index 37921164381f..e2315e652d08 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientLiveTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientLiveTests.cs @@ -586,7 +586,7 @@ public async Task DeleteEcHsmKey() DeleteKeyOperation operation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await operation.WaitForCompletionAsync(); + DeletedKey deletedKey = await operation.WaitForCompletionAsync(PollingInterval, default); Assert.NotNull(deletedKey.DeletedOn); Assert.NotNull(deletedKey.RecoveryId); @@ -608,7 +608,7 @@ public async Task DeleteRsaHsmKey() DeleteKeyOperation operation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await operation.WaitForCompletionAsync(); + DeletedKey deletedKey = await operation.WaitForCompletionAsync(PollingInterval, default); Assert.NotNull(deletedKey.DeletedOn); Assert.NotNull(deletedKey.RecoveryId); @@ -660,7 +660,7 @@ public async Task GetDeletedEcHsmKey() DeleteKeyOperation operation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await operation.WaitForCompletionAsync(); + DeletedKey deletedKey = await operation.WaitForCompletionAsync(PollingInterval, default); await WaitForDeletedKey(keyName); @@ -686,7 +686,7 @@ public async Task GetDeletedRsaHsmKey() DeleteKeyOperation operation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await operation.WaitForCompletionAsync(); + DeletedKey deletedKey = await operation.WaitForCompletionAsync(PollingInterval, default); await WaitForDeletedKey(keyName); @@ -745,7 +745,7 @@ public async Task RecoverDeletedEcHsmKey() DeleteKeyOperation deleteOperation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await deleteOperation.WaitForCompletionAsync(); + DeletedKey deletedKey = await deleteOperation.WaitForCompletionAsync(PollingInterval, default); await WaitForDeletedKey(keyName); @@ -753,7 +753,7 @@ public async Task RecoverDeletedEcHsmKey() RecoverDeletedKeyOperation recoverOperation = await Client.StartRecoverDeletedKeyAsync(keyName); - KeyVaultKey recoverKeyResult = await recoverOperation.WaitForCompletionAsync(); + KeyVaultKey recoverKeyResult = await recoverOperation.WaitForCompletionAsync(PollingInterval, default); await WaitForKey(keyName); @@ -777,7 +777,7 @@ public async Task RecoverDeletedRsaHsmKey() DeleteKeyOperation deleteOperation = await Client.StartDeleteKeyAsync(keyName); - DeletedKey deletedKey = await deleteOperation.WaitForCompletionAsync(); + DeletedKey deletedKey = await deleteOperation.WaitForCompletionAsync(PollingInterval, default); await WaitForDeletedKey(keyName); @@ -785,7 +785,7 @@ public async Task RecoverDeletedRsaHsmKey() RecoverDeletedKeyOperation recoverOperation = await Client.StartRecoverDeletedKeyAsync(keyName); - KeyVaultKey recoverKeyResult = await recoverOperation.WaitForCompletionAsync(); + KeyVaultKey recoverKeyResult = await recoverOperation.WaitForCompletionAsync(PollingInterval, default); await WaitForKey(keyName); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyUtilities.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyUtilities.cs index b19c26fd8c26..b6cd12cd8051 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyUtilities.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyUtilities.cs @@ -1,13 +1,89 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Security.Cryptography; +using Azure.Security.KeyVault.Keys.Cryptography; +using NUnit.Framework; namespace Azure.Security.KeyVault.Keys.Tests { internal static class KeyUtilities { + public static JsonWebKey CreateKey(SignatureAlgorithm algorithm, bool includePrivateParameters = false, IEnumerable keyOps = null) + { + switch (algorithm.ToString()) + { + case SignatureAlgorithm.PS256Value: + case SignatureAlgorithm.PS384Value: + case SignatureAlgorithm.PS512Value: + case SignatureAlgorithm.RS256Value: + case SignatureAlgorithm.RS384Value: + case SignatureAlgorithm.RS512Value: + return CreateRsaKey(includePrivateParameters, keyOps); + + case SignatureAlgorithm.ES256Value: + case SignatureAlgorithm.ES256KValue: + case SignatureAlgorithm.ES384Value: + case SignatureAlgorithm.ES512Value: +#if NET461 + throw new IgnoreException("Creating JsonWebKey with ECDsa is not supported on net461."); +#else + KeyCurveName curveName = algorithm.GetEcKeyCurveName(); + ECCurve curve = ECCurve.CreateFromOid(curveName.Oid); + + using (ECDsa ecdsa = ECDsa.Create()) + { + try + { + ecdsa.GenerateKey(curve); + return new JsonWebKey(ecdsa, includePrivateParameters, keyOps); + } + catch (NotSupportedException) + { + throw new IgnoreException($"This platform does not support OID {curveName.Oid}"); + } + } +#endif + + default: + throw new ArgumentException("Invalid Algorithm", nameof(algorithm)); + } + } + + public static JsonWebKey CreateKey(KeyWrapAlgorithm algorithm, bool includePrivateParameters = false, IEnumerable keyOps = null) + { + switch (algorithm.ToString()) + { + case KeyWrapAlgorithm.A128KWValue: + case KeyWrapAlgorithm.A192KWValue: + case KeyWrapAlgorithm.A256KWValue: + return CreateAesKey(algorithm.GetKeySizeInBytes(), keyOps); + + case KeyWrapAlgorithm.Rsa15Value: + case KeyWrapAlgorithm.RsaOaepValue: + case KeyWrapAlgorithm.RsaOaep256Value: + return CreateRsaKey(includePrivateParameters, keyOps); + + default: + throw new ArgumentException("Invalid Algorithm", nameof(algorithm)); + } + } + + public static JsonWebKey CreateAesKey(int sizeInBytes, IEnumerable keyOps = null) + { + byte[] k = new byte[] { 0xA4, 0x16, 0x55, 0x00, 0xB3, 0xDC, 0xB4, 0x38, 0x2E, 0xD9, 0xE5, 0x5D, 0x56, 0xB0, 0x60, 0x35, + 0x09, 0x59, 0xDB, 0xE7, 0x08, 0x20, 0xDF, 0x26, 0x4F, 0x42, 0x43, 0x30, 0x43, 0x4C, 0x0F, 0x6F } + .Take(sizeInBytes); + + return new JsonWebKey(keyOps ?? new[] { KeyOperation.WrapKey, KeyOperation.UnwrapKey }) + { + KeyType = KeyType.Oct, + K = k, + }; + } + public static JsonWebKey CreateRsaKey(bool includePrivateParameters = false, IEnumerable keyOps = null) { using RSA rsa = RSA.Create(); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeysTestBase.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeysTestBase.cs index b270f18e1923..711bb85dd273 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeysTestBase.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeysTestBase.cs @@ -21,7 +21,9 @@ public abstract class KeysTestBase : RecordedTestBase { public const string AzureKeyVaultUrlEnvironmentVariable = "AZURE_KEYVAULT_URL"; - protected readonly TimeSpan PollingInterval = TimeSpan.FromSeconds(5); + protected TimeSpan PollingInterval => Recording.Mode == RecordedTestMode.Playback + ? TimeSpan.Zero + : TimeSpan.FromSeconds(2); public KeyClient Client { get; set; } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/LocalCryptographyClientTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/LocalCryptographyClientTests.cs new file mode 100644 index 000000000000..f171abb5b9bc --- /dev/null +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/LocalCryptographyClientTests.cs @@ -0,0 +1,395 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using Azure.Core.TestFramework; +using Azure.Security.KeyVault.Keys.Cryptography; +using NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace Azure.Security.KeyVault.Keys.Tests +{ + public class LocalCryptographyClientTests : ClientTestBase + { + public LocalCryptographyClientTests(bool isAsync) : base(isAsync) + { + TestDiagnostics = false; + } + + private byte[] TestData { get; } = Encoding.UTF8.GetBytes("test"); + + private byte[] TestKey { get; } = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; + + private Stream TestStream => new MemoryStream(TestData); + + [Test] + [SyncOnly] + public void LocalCryptographyClientRequiresJsonWebKey() + { + ArgumentException ex = Assert.Throws(() => new LocalCryptographyClient(null)); + Assert.AreEqual("jsonWebKey", ex.ParamName); + } + + [Test] + [SyncOnly] + public void LocalCryptographyClientRequiredJsonWebKeyType() + { + JsonWebKey jwk = new JsonWebKey(null); + + Assert.Throws(() => new LocalCryptographyClient(jwk)); + } + + [Test] + [SyncOnly] + public void KeyIdFromJsonWebKey() + { + JsonWebKey jwk = new JsonWebKey(null) + { + Id = nameof(KeyIdFromJsonWebKey), + KeyType = KeyType.Rsa, + }; + + LocalCryptographyClient client = new LocalCryptographyClient(jwk); + + Assert.AreEqual(nameof(KeyIdFromJsonWebKey), client.KeyId); + } + + #region Encrypt / Decrypt + [Test] + public void EncryptOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.EncryptAsync(new EncryptionAlgorithm("ignored"), TestData)); + } + + [Test] + public void EncryptAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.EncryptAsync(new EncryptionAlgorithm("ignored"), TestData)); + } + + + [Test] + public void DecryptOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.DecryptAsync(new EncryptionAlgorithm("ignored"), TestData)); + } + + [Test] + public void DecryptAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.DecryptAsync(new EncryptionAlgorithm("ignored"), TestData)); + } + + [Test] + public async Task DecryptRequiresPrivateKey() + { + JsonWebKey jwk = CreateKey(KeyType.Rsa, keyOps: new[] { KeyOperation.Encrypt, KeyOperation.Decrypt }); + LocalCryptographyClient client = CreateClient(jwk); + + EncryptResult encrypted = await client.EncryptAsync(EncryptionAlgorithm.RsaOaep, TestData); + + Assert.ThrowsAsync(new InstanceOfTypeConstraint(typeof(CryptographicException)), async () => await client.DecryptAsync(EncryptionAlgorithm.RsaOaep, encrypted.Ciphertext)); + } + + [Test] + public async Task EncryptDecryptRoundtrip([EnumValues(Exclude = new[] { nameof(EncryptionAlgorithm.RsaOaep256) })] EncryptionAlgorithm algorithm) + { + JsonWebKey jwk = CreateKey(KeyType.Rsa, includePrivateParameters: true); + LocalCryptographyClient client = CreateClient(jwk); + + EncryptResult encrypted = await client.EncryptAsync(algorithm, TestData); + + DecryptResult decrypted = await client.DecryptAsync(algorithm, encrypted.Ciphertext); + string actual = Encoding.UTF8.GetString(decrypted.Plaintext); + + Assert.AreEqual("test", actual); + } + #endregion + + #region Sign / Verify + [Test] + public void SignOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignAsync(new SignatureAlgorithm("ignored"), TestData)); + } + + [Test] + public void SignAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignAsync(new SignatureAlgorithm("ignored"), TestData)); + } + + + [Test] + public void VerifyOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyAsync(new SignatureAlgorithm("ignored"), TestData, TestData)); + } + + [Test] + public void VerifyAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyAsync(new SignatureAlgorithm("ignored"), TestData, TestData)); + } + + [Test] + public void SignRequiresPrivateKey([EnumValues] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, keyOps: new[] { KeyOperation.Sign, KeyOperation.Verify }); + LocalCryptographyClient client = CreateClient(jwk); + + byte[] digest = algorithm.GetHashAlgorithm().ComputeHash(TestData); + Assert.ThrowsAsync(new InstanceOfTypeConstraint(typeof(CryptographicException)), async () => await client.SignAsync(algorithm, digest)); + } + + [Test] + public async Task SignVerifyRoundtrip([EnumValues(Exclude = new[] { nameof(SignatureAlgorithm.PS256), nameof(SignatureAlgorithm.PS384), nameof(SignatureAlgorithm.PS512) })] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, includePrivateParameters: true); + LocalCryptographyClient client = CreateClient(jwk); + + byte[] digest = algorithm.GetHashAlgorithm().ComputeHash(TestData); + SignResult signed = await client.SignAsync(algorithm, digest); + + VerifyResult verified = await client.VerifyAsync(algorithm, digest, signed.Signature); + Assert.IsTrue(verified.IsValid); + } + #endregion + + #region SignData / VerifyData + [Test] + public void SignDataOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignDataAsync(new SignatureAlgorithm("ignored"), TestData)); + } + + [Test] + public void SignDataAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignDataAsync(new SignatureAlgorithm("ignored"), TestData)); + } + + + [Test] + public void VerifyDataOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyDataAsync(new SignatureAlgorithm("ignored"), TestData, TestData)); + } + + [Test] + public void VerifyDataAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyDataAsync(new SignatureAlgorithm("ignored"), TestData, TestData)); + } + + [Test] + public void SignDataRequiresPrivateKey([EnumValues] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, keyOps: new[] { KeyOperation.Sign, KeyOperation.Verify }); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(new InstanceOfTypeConstraint(typeof(CryptographicException)) , async () => await client.SignDataAsync(algorithm, TestData)); + } + + [Test] + public async Task SignDataVerifyDataRoundtrip([EnumValues(Exclude = new[] { nameof(SignatureAlgorithm.PS256), nameof(SignatureAlgorithm.PS384), nameof(SignatureAlgorithm.PS512) })] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, includePrivateParameters: true); + LocalCryptographyClient client = CreateClient(jwk); + + SignResult signed = await client.SignDataAsync(algorithm, TestData); + + VerifyResult verified = await client.VerifyDataAsync(algorithm, TestData, signed.Signature); + Assert.IsTrue(verified.IsValid); + } + #endregion + + #region SignDataStream / VerifyDataStream + [Test] + public void SignDataStreamOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignDataAsync(new SignatureAlgorithm("ignored"), TestStream)); + } + + [Test] + public void SignDataStreamAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.SignDataAsync(new SignatureAlgorithm("ignored"), TestStream)); + } + + + [Test] + public void VerifyDataStreamOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyDataAsync(new SignatureAlgorithm("ignored"), TestStream, TestData)); + } + + [Test] + public void VerifyDataStreamAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Rsa), nameof(KeyType.RsaHsm), nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.VerifyDataAsync(new SignatureAlgorithm("ignored"), TestStream, TestData)); + } + + [Test] + public void SignDataStreamRequiresPrivateKey([EnumValues] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, keyOps: new[] { KeyOperation.Sign, KeyOperation.Verify }); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(new InstanceOfTypeConstraint(typeof(CryptographicException)), async () => await client.SignDataAsync(algorithm, TestStream)); + } + + [Test] + public async Task SignDataStreamVerifyDataStreamRoundtrip([EnumValues(Exclude = new[] { nameof(SignatureAlgorithm.PS256), nameof(SignatureAlgorithm.PS384), nameof(SignatureAlgorithm.PS512) })] SignatureAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, includePrivateParameters: true); + LocalCryptographyClient client = CreateClient(jwk); + + SignResult signed = await client.SignDataAsync(algorithm, TestStream); + + VerifyResult verified = await client.VerifyDataAsync(algorithm, TestStream, signed.Signature); + Assert.IsTrue(verified.IsValid); + } +#endregion + +#region WrapKey / UnwrapKey + [Test] + public void WrapKeyOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.WrapKeyAsync(new KeyWrapAlgorithm("ignored"), TestData)); + } + + [Test] + public void WrapKeyAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.WrapKeyAsync(new KeyWrapAlgorithm("ignored"), TestData)); + } + + + [Test] + public void UnwrapKeyOperationNotSupported() + { + JsonWebKey jwk = new JsonWebKey(RSA.Create(), keyOps: Array.Empty()); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.UnwrapKeyAsync(new KeyWrapAlgorithm("ignored"), TestData)); + } + + [Test] + public void UnwrapKeyAlgorithmNotSupported([EnumValues(Exclude = new[] { nameof(KeyType.Ec), nameof(KeyType.EcHsm) })] KeyType keyType) + { + JsonWebKey jwk = CreateKey(keyType); + LocalCryptographyClient client = CreateClient(jwk); + + Assert.ThrowsAsync(async () => await client.UnwrapKeyAsync(new KeyWrapAlgorithm("ignored"), TestData)); + } + + [Test] + public async Task UnwrapKeyRequiresPrivateKey() + { + JsonWebKey jwk = CreateKey(KeyType.Rsa, keyOps: new[] { KeyOperation.WrapKey, KeyOperation.UnwrapKey }); + LocalCryptographyClient client = CreateClient(jwk); + + WrapResult wrapped = await client.WrapKeyAsync(KeyWrapAlgorithm.RsaOaep, TestData); + + Assert.ThrowsAsync(new InstanceOfTypeConstraint(typeof(CryptographicException)), async () => await client.UnwrapKeyAsync(KeyWrapAlgorithm.RsaOaep, wrapped.EncryptedKey)); + } + + [Test] + public async Task WrapKeyUnwrapKeyRoundtrip([EnumValues(Exclude = new[] { nameof(KeyWrapAlgorithm.RsaOaep256) })] KeyWrapAlgorithm algorithm) + { + JsonWebKey jwk = KeyUtilities.CreateKey(algorithm, includePrivateParameters: true); + LocalCryptographyClient client = CreateClient(jwk); + + WrapResult wrapped = await client.WrapKeyAsync(algorithm, TestKey); + UnwrapResult unwrapped = await client.UnwrapKeyAsync(algorithm, wrapped.EncryptedKey); + + CollectionAssert.AreEqual(TestKey, unwrapped.Key); + } + #endregion + + private static JsonWebKey CreateKey(KeyType type, bool includePrivateParameters = false, IEnumerable keyOps = null) + { + switch (type.ToString()) + { +#if NET461 + case KeyType.EcValue: + case KeyType.EcHsmValue: + throw new IgnoreException("Creating JsonWebKey with ECDsa is not supported on net461."); +#else + case KeyType.EcValue: + case KeyType.EcHsmValue: + return new JsonWebKey(ECDsa.Create(), includePrivateParameters, keyOps); +#endif + case KeyType.RsaValue: + case KeyType.RsaHsmValue: + return new JsonWebKey(RSA.Create(), includePrivateParameters, keyOps); + + case KeyType.OctValue: + return new JsonWebKey(Aes.Create(), keyOps); + + default: + throw new NotSupportedException(@$"Key type ""{type}"" is not supported"); + } + } + } +} diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/RsaCryptographyProviderTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/RsaCryptographyProviderTests.cs index 8b46c276f091..6425bc0eb968 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/RsaCryptographyProviderTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/RsaCryptographyProviderTests.cs @@ -25,7 +25,7 @@ public void EncryptBeforeValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] plaintext = Encoding.UTF8.GetBytes("test"); InvalidOperationException ex = Assert.Throws(() => provider.Encrypt(EncryptionAlgorithm.RsaOaep256, plaintext, default)); @@ -46,7 +46,7 @@ public void EncryptAfterValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] plaintext = Encoding.UTF8.GetBytes("test"); InvalidOperationException ex = Assert.Throws(() => provider.Encrypt(EncryptionAlgorithm.RsaOaep256, plaintext, default)); @@ -67,7 +67,7 @@ public void SignBeforeValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; InvalidOperationException ex = Assert.Throws(() => provider.Sign(SignatureAlgorithm.PS256, digest, default)); @@ -88,7 +88,7 @@ public void SignAfterValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] digest = new byte[] { 0x9f, 0x86, 0xd0, 0x81, 0x88, 0x4c, 0x7d, 0x65, 0x9a, 0x2f, 0xea, 0xa0, 0xc5, 0x5a, 0xd0, 0x15, 0xa3, 0xbf, 0x4f, 0x1b, 0x2b, 0x0b, 0x82, 0x2c, 0xd1, 0x5d, 0x6c, 0x15, 0xb0, 0xf0, 0x0a, 0x08 }; InvalidOperationException ex = Assert.Throws(() => provider.Sign(SignatureAlgorithm.PS256, digest, default)); @@ -109,7 +109,7 @@ public void WrapKeyBeforeValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] ek = { 0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7 }; InvalidOperationException ex = Assert.Throws(() => provider.WrapKey(KeyWrapAlgorithm.RsaOaep256, ek, default)); @@ -130,7 +130,7 @@ public void WrapKeyAfterValidDate() }, }; - RsaCryptographyProvider provider = new RsaCryptographyProvider(key); + RsaCryptographyProvider provider = new RsaCryptographyProvider(key.Key, key.Properties); byte[] ek = { 0x64, 0xE8, 0xC3, 0xF9, 0xCE, 0x0F, 0x5B, 0xA2, 0x63, 0xE9, 0x77, 0x79, 0x05, 0x81, 0x8A, 0x2A, 0x93, 0xC8, 0x19, 0x1E, 0x7D, 0x6E, 0x8A, 0xE7 }; InvalidOperationException ex = Assert.Throws(() => provider.WrapKey(KeyWrapAlgorithm.RsaOaep256, ek, default));