diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs index 2abcd2a6b1098..d6c76260777a9 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/api/Azure.Security.KeyVault.Administration.netstandard2.0.cs @@ -54,7 +54,7 @@ public static partial class KeyVaultAdministrationModelFactory public static Azure.Security.KeyVault.Administration.KeyVaultRoleAssignmentProperties KeyVaultRoleAssignmentProperties(Azure.Security.KeyVault.Administration.KeyVaultRoleScope? scope = default(Azure.Security.KeyVault.Administration.KeyVaultRoleScope?), string roleDefinitionId = null, string principalId = null) { throw null; } public static Azure.Security.KeyVault.Administration.KeyVaultRoleDefinition KeyVaultRoleDefinition(string id = null, string name = null, Azure.Security.KeyVault.Administration.KeyVaultRoleDefinitionType? type = default(Azure.Security.KeyVault.Administration.KeyVaultRoleDefinitionType?), string roleName = null, string description = null, Azure.Security.KeyVault.Administration.KeyVaultRoleType? roleType = default(Azure.Security.KeyVault.Administration.KeyVaultRoleType?), System.Collections.Generic.IEnumerable permissions = null, System.Collections.Generic.IEnumerable assignableScopes = null) { throw null; } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public static Azure.Security.KeyVault.Administration.KeyVaultRoleDefinition KeyVaultRoleDefinition(string id = null, string name = null, Azure.Security.KeyVault.Administration.KeyVaultRoleDefinitionType? type = default(Azure.Security.KeyVault.Administration.KeyVaultRoleDefinitionType?), string roleName = null, string description = null, Azure.Security.KeyVault.Administration.KeyVaultRoleType? roleType = default(Azure.Security.KeyVault.Administration.KeyVaultRoleType?), System.Collections.Generic.IList permissions = null, System.Collections.Generic.IList assignableScopes = null) { throw null; } + public static Azure.Security.KeyVault.Administration.KeyVaultRoleDefinition KeyVaultRoleDefinition(string id, string name, Azure.Security.KeyVault.Administration.KeyVaultRoleDefinitionType? type, string roleName, string description, Azure.Security.KeyVault.Administration.KeyVaultRoleType? roleType, System.Collections.Generic.IList permissions, System.Collections.Generic.IList assignableScopes) { throw null; } public static Azure.Security.KeyVault.Administration.KeyVaultRestoreOperation RestoreOperation(Azure.Response response, Azure.Security.KeyVault.Administration.KeyVaultBackupClient client, string id, System.DateTimeOffset? startTime = default(System.DateTimeOffset?), System.DateTimeOffset? endTime = default(System.DateTimeOffset?), string errorMessage = null) { throw null; } public static Azure.Security.KeyVault.Administration.KeyVaultRestoreResult RestoreResult(System.DateTimeOffset startTime, System.DateTimeOffset endTime) { throw null; } public static Azure.Security.KeyVault.Administration.KeyVaultRoleAssignment RoleAssignment(string id, string name, string type, Azure.Security.KeyVault.Administration.KeyVaultRoleAssignmentProperties properties) { throw null; } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationModelFactory.cs b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationModelFactory.cs index 1a63edbfb5a9d..3cfc7746dd042 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationModelFactory.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Administration/src/KeyVaultAdministrationModelFactory.cs @@ -26,7 +26,7 @@ public static partial class KeyVaultAdministrationModelFactory /// Role definition assignable scopes. /// A new instance for mocking. [EditorBrowsable(EditorBrowsableState.Never)] - public static KeyVaultRoleDefinition KeyVaultRoleDefinition(string id = default, string name = default, KeyVaultRoleDefinitionType? type = default, string roleName = default, string description = default, KeyVaultRoleType? roleType = default, IList permissions = default, IList assignableScopes = default) + public static KeyVaultRoleDefinition KeyVaultRoleDefinition(string id, string name, KeyVaultRoleDefinitionType? type, string roleName, string description, KeyVaultRoleType? roleType, IList permissions, IList assignableScopes) { return new KeyVaultRoleDefinition(id, name, type, roleName, description, roleType, permissions, assignableScopes); } diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md index 411b9a93e62ec..1a7357fc05296 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/CHANGELOG.md @@ -14,6 +14,10 @@ Changes from both the last release and the last beta include: - Added `DownloadCertificateOptions` to pass `X509KeyStorageFlags` appropriate for different host applications. ([#23016](https://github.com/Azure/azure-sdk-for-net/issues/23016)) - Added certificate version to distributed tracing. ([#12907](https://github.com/Azure/azure-sdk-for-net/issues/12907)) +### Breaking Changes + +- (Since 4.3.0-beta.4) To pass `X509KeyStorageFlags` you must now pass a single required `DownloadCertificateOptions` with a required `certificateName`. + ### Other Changes - The default service version is now "7.3". diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs index c7775bf74cbb9..6a2be436387d9 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/api/Azure.Security.KeyVault.Certificates.netstandard2.0.cs @@ -22,12 +22,10 @@ public CertificateClient(System.Uri vaultUri, Azure.Core.TokenCredential credent public virtual System.Threading.Tasks.Task>> DeleteContactsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response DeleteIssuer(string issuerName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> DeleteIssuerAsync(string issuerName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual Azure.Response DownloadCertificate(string certificateName, string version = null, Azure.Security.KeyVault.Certificates.DownloadCertificateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual Azure.Response DownloadCertificate(string certificateName, string version, System.Threading.CancellationToken cancellationToken) { throw null; } - public virtual System.Threading.Tasks.Task> DownloadCertificateAsync(string certificateName, string version = null, Azure.Security.KeyVault.Certificates.DownloadCertificateOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] - public virtual System.Threading.Tasks.Task> DownloadCertificateAsync(string certificateName, string version, System.Threading.CancellationToken cancellationToken) { throw null; } + public virtual Azure.Response DownloadCertificate(Azure.Security.KeyVault.Certificates.DownloadCertificateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response DownloadCertificate(string certificateName, string version = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> DownloadCertificateAsync(Azure.Security.KeyVault.Certificates.DownloadCertificateOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> DownloadCertificateAsync(string certificateName, string version = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response GetCertificate(string certificateName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> GetCertificateAsync(string certificateName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Security.KeyVault.Certificates.CertificateOperation GetCertificateOperation(string certificateName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -331,8 +329,10 @@ internal DeletedCertificate() { } } public partial class DownloadCertificateOptions { - public DownloadCertificateOptions() { } + public DownloadCertificateOptions(string certificateName) { } + public string CertificateName { get { throw null; } } public System.Security.Cryptography.X509Certificates.X509KeyStorageFlags KeyStorageFlags { get { throw null; } set { } } + public string Version { get { throw null; } set { } } } public partial class ImportCertificateOptions { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample4_DownloadCertificate.md b/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample4_DownloadCertificate.md index ddb18bcb0c30c..108f71ba10dbb 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample4_DownloadCertificate.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample4_DownloadCertificate.md @@ -33,12 +33,12 @@ if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) keyStorageFlags |= X509KeyStorageFlags.EphemeralKeySet; } -DownloadCertificateOptions options = new DownloadCertificateOptions +DownloadCertificateOptions options = new DownloadCertificateOptions(certificateName) { KeyStorageFlags = keyStorageFlags }; -using X509Certificate2 certificate = client.DownloadCertificate(certificateName, options: options); +using X509Certificate2 certificate = client.DownloadCertificate(options); using RSA key = certificate.GetRSAPrivateKey(); byte[] signature = key.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); @@ -61,12 +61,12 @@ if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) keyStorageFlags |= X509KeyStorageFlags.EphemeralKeySet; } -DownloadCertificateOptions options = new DownloadCertificateOptions +DownloadCertificateOptions options = new DownloadCertificateOptions(certificateName) { KeyStorageFlags = keyStorageFlags }; -using X509Certificate2 certificate = await client.DownloadCertificateAsync(certificateName, options: options); +using X509Certificate2 certificate = await client.DownloadCertificateAsync(options); using RSA key = certificate.GetRSAPrivateKey(); byte[] signature = key.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs index 1034ce3ee1fde..5438132284244 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs @@ -178,9 +178,8 @@ public virtual async Task StartCreateCertificateAsync(stri /// The is not supported. /// Cannot create an on this platform. /// The request failed. See and the exception message for details. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Response DownloadCertificate(string certificateName, string version, CancellationToken cancellationToken) => - DownloadCertificate(certificateName, version, null, cancellationToken); + public virtual Response DownloadCertificate(string certificateName, string version = null, CancellationToken cancellationToken = default) => + DownloadCertificate(new DownloadCertificateOptions(certificateName) { Version = version }, cancellationToken); /// /// Creates an from the specified certificate. @@ -193,29 +192,26 @@ public virtual Response DownloadCertificate(string certificate /// property. /// This operation requires the certificates/get and secrets/get permissions. /// - /// The name of the certificate to download. - /// Optional version of a certificate to download. - /// Additional options for downloading and creating an . + /// Options for downloading and creating an . /// A controlling the request lifetime. /// An from the specified certificate. - /// is empty. - /// is null. + /// is null. /// The managed secret did not contain a certificate. /// The is not supported. /// Cannot create an on this platform. /// The request failed. See and the exception message for details. - public virtual Response DownloadCertificate(string certificateName, string version = null, DownloadCertificateOptions options = null, CancellationToken cancellationToken = default) + public virtual Response DownloadCertificate(DownloadCertificateOptions options, CancellationToken cancellationToken = default) { - Argument.AssertNotNullOrEmpty(certificateName, nameof(certificateName)); + Argument.AssertNotNull(options, nameof(options)); using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(CertificateClient)}.{nameof(DownloadCertificate)}"); - scope.AddAttribute("certificate", certificateName); - scope.AddAttribute("version", version); + scope.AddAttribute("certificate", options.CertificateName); + scope.AddAttribute("version", options.Version); scope.Start(); try { - KeyVaultCertificateWithPolicy certificate = _pipeline.SendRequest(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, certificateName, "/", version); + KeyVaultCertificateWithPolicy certificate = _pipeline.SendRequest(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, options.CertificateName, "/", options.Version); Response secretResponse = _pipeline.SendRequest(RequestMethod.Get, () => new KeyVaultSecret(), certificate.SecretId, cancellationToken); KeyVaultSecret secret = secretResponse.Value; @@ -226,7 +222,6 @@ public virtual Response DownloadCertificate(string certificate throw new InvalidDataException($"Secret {certificate.SecretId} contains no value"); } - options ??= new(); if (secret.ContentType is null || secret.ContentType == CertificateContentType.Pkcs12) { byte[] rawData = Convert.FromBase64String(value); @@ -270,9 +265,8 @@ public virtual Response DownloadCertificate(string certificate /// The is not supported. /// Cannot create an on this platform. /// The request failed. See and the exception message for details. - [EditorBrowsable(EditorBrowsableState.Never)] - public virtual Task> DownloadCertificateAsync(string certificateName, string version, CancellationToken cancellationToken) => - DownloadCertificateAsync(certificateName, version, null, cancellationToken); + public virtual async Task> DownloadCertificateAsync(string certificateName, string version = null, CancellationToken cancellationToken = default) => + await DownloadCertificateAsync(new DownloadCertificateOptions(certificateName) { Version = version }, cancellationToken).ConfigureAwait(false); /// /// Creates an from the specified certificate. @@ -285,29 +279,26 @@ public virtual Task> DownloadCertificateAsync(string /// property. /// This operation requires the certificates/get and secrets/get permissions. /// - /// The name of the certificate to download. - /// Optional version of a certificate to download. /// Additional options for downloading and creating an . /// A controlling the request lifetime. /// An from the specified certificate. - /// is empty. - /// is null. + /// is null. /// The managed secret did not contain a certificate. /// The is not supported. /// Cannot create an on this platform. /// The request failed. See and the exception message for details. - public virtual async Task> DownloadCertificateAsync(string certificateName, string version = null, DownloadCertificateOptions options = null, CancellationToken cancellationToken = default) + public virtual async Task> DownloadCertificateAsync(DownloadCertificateOptions options, CancellationToken cancellationToken = default) { - Argument.AssertNotNullOrEmpty(certificateName, nameof(certificateName)); + Argument.AssertNotNull(options, nameof(options)); using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(CertificateClient)}.{nameof(DownloadCertificate)}"); - scope.AddAttribute("certificate", certificateName); - scope.AddAttribute("version", version); + scope.AddAttribute("certificate", options.CertificateName); + scope.AddAttribute("version", options.Version); scope.Start(); try { - KeyVaultCertificateWithPolicy certificate = await _pipeline.SendRequestAsync(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, certificateName, "/", version).ConfigureAwait(false); + KeyVaultCertificateWithPolicy certificate = await _pipeline.SendRequestAsync(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, options.CertificateName, "/", options.Version).ConfigureAwait(false); Response secretResponse = await _pipeline.SendRequestAsync(RequestMethod.Get, () => new KeyVaultSecret(), certificate.SecretId, cancellationToken).ConfigureAwait(false); KeyVaultSecret secret = secretResponse.Value; @@ -318,7 +309,6 @@ public virtual async Task> DownloadCertificateAsync(s throw new InvalidDataException($"Secret {certificate.SecretId} contains no value"); } - options ??= new(); if (secret.ContentType is null || secret.ContentType == CertificateContentType.Pkcs12) { byte[] rawData = Convert.FromBase64String(value); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DownloadCertificateOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DownloadCertificateOptions.cs index c6a728edf6fff..16d5737effde9 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DownloadCertificateOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/src/DownloadCertificateOptions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Security.Cryptography.X509Certificates; +using Azure.Core; namespace Azure.Security.KeyVault.Certificates { @@ -10,6 +11,27 @@ namespace Azure.Security.KeyVault.Certificates /// public class DownloadCertificateOptions { + /// + /// Initializes a new instance of the class. + /// + /// The name of the certificate to download. + public DownloadCertificateOptions(string certificateName) + { + Argument.AssertNotNullOrEmpty(certificateName, nameof(certificateName)); + + CertificateName = certificateName; + } + + /// + /// Gets the name of the certificate to download. + /// + public string CertificateName { get; } + + /// + /// Gets or sets the optional version of a certificate to download. + /// + public string Version { get; set; } + /// /// Gets or sets a combination of the enumeration values that control where and how to import the certificate. /// The default is . diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientTests.cs index 5409e6f86ad4e..6e85ce2cee43b 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/CertificateClientTests.cs @@ -111,6 +111,19 @@ public void ChallengeBasedAuthenticationRequiresHttps() Assert.ThrowsAsync(() => Client.GetCertificateAsync("test")); } + [Test] + public void DownloadCertificateParameterValidation() + { + ArgumentException ex = Assert.ThrowsAsync(async () => await Client.DownloadCertificateAsync((string)null)); + Assert.AreEqual("certificateName", ex.ParamName); + + ex = Assert.ThrowsAsync(async () => await Client.DownloadCertificateAsync(string.Empty)); + Assert.AreEqual("certificateName", ex.ParamName); + + ex = Assert.ThrowsAsync(async () => await Client.DownloadCertificateAsync((DownloadCertificateOptions)null)); + Assert.AreEqual("options", ex.ParamName); + } + [Test] public void DownloadSecretDenied() { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/DownloadCertificateOptionsTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/DownloadCertificateOptionsTests.cs new file mode 100644 index 0000000000000..610c53e3d5122 --- /dev/null +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/DownloadCertificateOptionsTests.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using NUnit.Framework; + +namespace Azure.Security.KeyVault.Certificates.Tests +{ + public class DownloadCertificateOptionsTests + { + [Test] + public void ConstructorArgumentValidation() + { + ArgumentException ex = Assert.Throws(() => new DownloadCertificateOptions(null)); + Assert.AreEqual("certificateName", ex.ParamName); + + ex = Assert.Throws(() => new DownloadCertificateOptions(string.Empty)); + Assert.AreEqual("certificateName", ex.ParamName); + } + } +} diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificate.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificate.cs index 543726c7db2b0..f44ac62bf3769 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificate.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificate.cs @@ -21,9 +21,9 @@ public void DownloadCertificateSync() // Environment variable with the Key Vault endpoint. string keyVaultUrl = TestEnvironment.KeyVaultUrl; -#region Snippet:CertificatesSample4CertificateClient + #region Snippet:CertificatesSample4CertificateClient CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential()); -#endregion + #endregion string certificateName = $"rsa-{Guid.NewGuid()}"; CertificateOperation operation = client.StartCreateCertificate(certificateName, CertificatePolicy.Default); @@ -38,33 +38,33 @@ public void DownloadCertificateSync() byte[] data = Encoding.UTF8.GetBytes("test"); byte[] hash = sha.ComputeHash(data); - #region Snippet:CertificatesSample4DownloadCertificate + #region Snippet:CertificatesSample4DownloadCertificate X509KeyStorageFlags keyStorageFlags = X509KeyStorageFlags.MachineKeySet; if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { keyStorageFlags |= X509KeyStorageFlags.EphemeralKeySet; } - DownloadCertificateOptions options = new DownloadCertificateOptions + DownloadCertificateOptions options = new DownloadCertificateOptions(certificateName) { KeyStorageFlags = keyStorageFlags }; - using X509Certificate2 certificate = client.DownloadCertificate(certificateName, options: options); + using X509Certificate2 certificate = client.DownloadCertificate(options); using RSA key = certificate.GetRSAPrivateKey(); byte[] signature = key.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); Debug.WriteLine($"Signature: {Convert.ToBase64String(signature)}"); - #endregion + #endregion - #region Snippet:CertificatesSample4PublicKey + #region Snippet:CertificatesSample4PublicKey Response certificateResponse = client.GetCertificate(certificateName); using X509Certificate2 publicCertificate = new X509Certificate2(certificateResponse.Value.Cer); using RSA publicKey = publicCertificate.GetRSAPublicKey(); bool verified = publicKey.VerifyHash(hash, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); Debug.WriteLine($"Signature verified: {verified}"); - #endregion + #endregion Assert.IsTrue(verified); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificateAsync.cs b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificateAsync.cs index f5f0d67a0f96a..5a734518a08ca 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificateAsync.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample4_DownloadCertificateAsync.cs @@ -38,12 +38,12 @@ public async Task DownloadCertificateAsync() keyStorageFlags |= X509KeyStorageFlags.EphemeralKeySet; } - DownloadCertificateOptions options = new DownloadCertificateOptions + DownloadCertificateOptions options = new DownloadCertificateOptions(certificateName) { KeyStorageFlags = keyStorageFlags }; - using X509Certificate2 certificate = await client.DownloadCertificateAsync(certificateName, options: options); + using X509Certificate2 certificate = await client.DownloadCertificateAsync(options); using RSA key = certificate.GetRSAPrivateKey(); byte[] signature = key.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md index 6f3cf4043a75b..ea3f22acb93d2 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/CHANGELOG.md @@ -26,6 +26,10 @@ Changes from both the last release and the last beta include: - Attempt to cache key locally from `KeyClient.GetCryptographyClient`. ([#25254](https://github.com/Azure/azure-sdk-for-net/issues/25254)) - Added key version to distributed tracing. ([#12907](https://github.com/Azure/azure-sdk-for-net/issues/12907)) +### Breaking Changes + +- (Since 4.3.0-beta.7) `KeyClient.ReleaseKey` and `ReleaseKeyAsync` now take `name` and `targetAttestationToken`, or a `ReleaseKeyOptions` with a required `name` and `targetAttestationToken` along with additional properties. + ### Other Changes - `KeyProperties.Version` is no longer required when calling `KeyClient.UpdateKeyProperties` or `UpdateKeyPropertiesAsync`. 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 169fd4e304c63..64bba60ea2453 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 @@ -140,10 +140,10 @@ public KeyClient(System.Uri vaultUri, Azure.Core.TokenCredential credential, Azu public virtual System.Threading.Tasks.Task> ImportKeyAsync(string name, Azure.Security.KeyVault.Keys.JsonWebKey keyMaterial, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response PurgeDeletedKey(string name, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task PurgeDeletedKeyAsync(string name, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual Azure.Response ReleaseKey(string name, string targetAttestationToken, Azure.Security.KeyVault.Keys.ReleaseKeyOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual Azure.Response ReleaseKey(string name, string version, string targetAttestationToken, Azure.Security.KeyVault.Keys.ReleaseKeyOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> ReleaseKeyAsync(string name, string targetAttestationToken, Azure.Security.KeyVault.Keys.ReleaseKeyOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public virtual System.Threading.Tasks.Task> ReleaseKeyAsync(string name, string version, string targetAttestationToken, Azure.Security.KeyVault.Keys.ReleaseKeyOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReleaseKey(Azure.Security.KeyVault.Keys.ReleaseKeyOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual Azure.Response ReleaseKey(string name, string targetAttestationToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReleaseKeyAsync(Azure.Security.KeyVault.Keys.ReleaseKeyOptions options, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } + public virtual System.Threading.Tasks.Task> ReleaseKeyAsync(string name, string targetAttestationToken, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response RestoreKeyBackup(byte[] backup, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual System.Threading.Tasks.Task> RestoreKeyBackupAsync(byte[] backup, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } public virtual Azure.Response RotateKey(string name, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -369,9 +369,12 @@ protected RecoverDeletedKeyOperation() { } } public partial class ReleaseKeyOptions { - public ReleaseKeyOptions() { } + public ReleaseKeyOptions(string name, string targetAttestationToken) { } public Azure.Security.KeyVault.Keys.KeyExportEncryptionAlgorithm? Algorithm { get { throw null; } set { } } + public string Name { get { throw null; } } public string Nonce { get { throw null; } set { } } + public string TargetAttestationToken { get { throw null; } } + public string Version { get { throw null; } set { } } } public partial class ReleaseKeyResult { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs index a28eabd9454bb..b421bed3ea382 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/KeyClient.cs @@ -1241,7 +1241,6 @@ public virtual async Task> GetRandomBytesAsync(int count, Cance /// /// The name of the key to release. /// The attestation assertion for the target of the key release. - /// Optional containing additional options to release a key. /// A controlling the request lifetime. /// /// The key must be exportable. @@ -1251,41 +1250,33 @@ public virtual async Task> GetRandomBytesAsync(int count, Cance /// or contains an empty string. /// or is null. /// The server returned an error. See for details returned from the server. - public virtual Response ReleaseKey(string name, string targetAttestationToken, ReleaseKeyOptions options = default, CancellationToken cancellationToken = default) => - ReleaseKey(name, null, targetAttestationToken, options, cancellationToken); + public virtual Response ReleaseKey(string name, string targetAttestationToken, CancellationToken cancellationToken = default) => + ReleaseKey(new ReleaseKeyOptions(name, targetAttestationToken), cancellationToken); /// /// Releases a key. /// - /// The name of the key to release. - /// Optional version of the key to release. - /// The attestation assertion for the target of the key release. - /// Optional containing additional options to release a key. + /// containing the name, attestation assertion for the target, and additional options to release a key. /// A controlling the request lifetime. /// /// The key must be exportable. /// This operation requires the keys/release permission. /// /// The key release result containing the released key. - /// or contains an empty string. - /// or is null. + /// is null. /// The server returned an error. See for details returned from the server. - public virtual Response ReleaseKey(string name, string version, string targetAttestationToken, ReleaseKeyOptions options = default, CancellationToken cancellationToken = default) + public virtual Response ReleaseKey(ReleaseKeyOptions options, CancellationToken cancellationToken = default) { - Argument.AssertNotNullOrEmpty(name, nameof(name)); - Argument.AssertNotNullOrEmpty(targetAttestationToken, nameof(targetAttestationToken)); + Argument.AssertNotNull(options, nameof(options)); using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(KeyClient)}.{nameof(ReleaseKey)}"); - scope.AddAttribute("key", name); - scope.AddAttribute("version", version); + scope.AddAttribute("key", options.Name); + scope.AddAttribute("version", options.Version); scope.Start(); - options ??= new(); - options.TargetAttestationToken = targetAttestationToken; - try { - return _pipeline.SendRequest(RequestMethod.Post, options, () => new ReleaseKeyResult(), cancellationToken, KeysPath, name, "/", version, "/release"); + return _pipeline.SendRequest(RequestMethod.Post, options, () => new ReleaseKeyResult(), cancellationToken, KeysPath, options.Name, "/", options.Version, "/release"); } catch (Exception e) { @@ -1299,7 +1290,6 @@ public virtual Response ReleaseKey(string name, string version /// /// The name of the key to release. /// The attestation assertion for the target of the key release. - /// Optional containing additional options to release a key. /// A controlling the request lifetime. /// /// The key must be exportable. @@ -1309,41 +1299,33 @@ public virtual Response ReleaseKey(string name, string version /// or contains an empty string. /// or is null. /// The server returned an error. See for details returned from the server. - public virtual Task> ReleaseKeyAsync(string name, string targetAttestationToken, ReleaseKeyOptions options = default, CancellationToken cancellationToken = default) => - ReleaseKeyAsync(name, null, targetAttestationToken, options, cancellationToken); + public virtual async Task> ReleaseKeyAsync(string name, string targetAttestationToken, CancellationToken cancellationToken = default) => + await ReleaseKeyAsync(new ReleaseKeyOptions(name, targetAttestationToken), cancellationToken).ConfigureAwait(false); /// /// Releases a key. /// - /// The name of the key to release. - /// Optional version of the key to release. - /// The attestation assertion for the target of the key release. - /// Optional containing additional options to release a key. + /// containing the name, attestation assertion for the target, and additional options to release a key. /// A controlling the request lifetime. /// /// The key must be exportable. /// This operation requires the keys/release permission. /// /// The key release result containing the released key. - /// or contains an empty string. - /// or is null. + /// is null. /// The server returned an error. See for details returned from the server. - public virtual async Task> ReleaseKeyAsync(string name, string version, string targetAttestationToken, ReleaseKeyOptions options = default, CancellationToken cancellationToken = default) + public virtual async Task> ReleaseKeyAsync(ReleaseKeyOptions options, CancellationToken cancellationToken = default) { - Argument.AssertNotNullOrEmpty(name, nameof(name)); - Argument.AssertNotNullOrEmpty(targetAttestationToken, nameof(targetAttestationToken)); + Argument.AssertNotNull(options, nameof(options)); using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(KeyClient)}.{nameof(ReleaseKey)}"); - scope.AddAttribute("key", name); - scope.AddAttribute("version", version); + scope.AddAttribute("key", options.Name); + scope.AddAttribute("version", options.Version); scope.Start(); - options ??= new(); - options.TargetAttestationToken = targetAttestationToken; - try { - return await _pipeline.SendRequestAsync(RequestMethod.Post, options, () => new ReleaseKeyResult(), cancellationToken, KeysPath, name, "/", version, "/release").ConfigureAwait(false); + return await _pipeline.SendRequestAsync(RequestMethod.Post, options, () => new ReleaseKeyResult(), cancellationToken, KeysPath, options.Name, "/", options.Version, "/release").ConfigureAwait(false); } catch (Exception e) { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/ReleaseKeyOptions.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/ReleaseKeyOptions.cs index 8087986b9eb79..3aab9c58333dc 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/src/ReleaseKeyOptions.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/src/ReleaseKeyOptions.cs @@ -1,13 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Text.Json; +using Azure.Core; namespace Azure.Security.KeyVault.Keys { /// - /// Options for and - /// . + /// Options for and + /// . /// public class ReleaseKeyOptions : IJsonSerializable { @@ -18,10 +20,29 @@ public class ReleaseKeyOptions : IJsonSerializable /// /// Initializes a new instance of the class. /// - public ReleaseKeyOptions() + /// The name of the key to release. + /// The attestation assertion for the target of the key release. + /// or contains an empty string. + /// or is null. + public ReleaseKeyOptions(string name, string targetAttestationToken) { + Argument.AssertNotNullOrEmpty(name, nameof(name)); + Argument.AssertNotNullOrEmpty(targetAttestationToken, nameof(targetAttestationToken)); + + Name = name; + TargetAttestationToken = targetAttestationToken; } + /// + /// Gets the name of the key to release. + /// + public string Name { get; } + + /// + /// Gets or sets the optional version of the key to release. + /// + public string Version { get; set; } + /// /// Gets or sets a client-provided nonce for freshness. /// @@ -33,9 +54,9 @@ public ReleaseKeyOptions() public KeyExportEncryptionAlgorithm? Algorithm { get; set; } /// - /// Gets or sets the attestation assertion for the target of the key release. + /// Gets the attestation assertion for the target of the key release. /// - internal string TargetAttestationToken { get; set; } + public string TargetAttestationToken { get; } internal void WriteProperties(Utf8JsonWriter json) { diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientTests.cs index fb8192d9f66bd..f2be3a941721f 100644 --- a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientTests.cs +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/KeyClientTests.cs @@ -220,6 +220,9 @@ public void ReleaseKeyParameterValidation() ex = Assert.ThrowsAsync(async () => await Client.ReleaseKeyAsync("test", string.Empty)); Assert.AreEqual("targetAttestationToken", ex.ParamName); + + ex = Assert.ThrowsAsync(async () => await Client.ReleaseKeyAsync(null)); + Assert.AreEqual("options", ex.ParamName); } [Test] diff --git a/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/ReleaseKeyOptionsTests.cs b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/ReleaseKeyOptionsTests.cs new file mode 100644 index 0000000000000..6fb6fbe6b1609 --- /dev/null +++ b/sdk/keyvault/Azure.Security.KeyVault.Keys/tests/ReleaseKeyOptionsTests.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using NUnit.Framework; + +namespace Azure.Security.KeyVault.Keys.Tests +{ + public class ReleaseKeyOptionsTests + { + [Test] + public void ConstructorArgumentValidation() + { + ArgumentException ex = Assert.Throws(() => new ReleaseKeyOptions(null, null)); + Assert.AreEqual("name", ex.ParamName); + + ex = Assert.Throws(() => new ReleaseKeyOptions(string.Empty, null)); + Assert.AreEqual("name", ex.ParamName); + + ex = Assert.Throws(() => new ReleaseKeyOptions("test", null)); + Assert.AreEqual("targetAttestationToken", ex.ParamName); + + ex = Assert.Throws(() => new ReleaseKeyOptions("test", string.Empty)); + Assert.AreEqual("targetAttestationToken", ex.ParamName); + } + } +}