|
1 | | -// Copyright (c) Microsoft Corporation. All rights reserved. |
| 1 | +// Copyright (c) Microsoft Corporation. All rights reserved. |
2 | 2 | // Licensed under the MIT License. |
3 | 3 |
|
4 | 4 | using System; |
5 | 5 | using System.Collections.Generic; |
6 | 6 | using System.Globalization; |
| 7 | +using System.Security.Cryptography; |
| 8 | +using System.Security.Cryptography.X509Certificates; |
7 | 9 | using System.Threading; |
8 | 10 | using System.Threading.Tasks; |
9 | 11 | using Azure.Core; |
@@ -152,6 +154,104 @@ public virtual async Task<CertificateOperation> StartCreateCertificateAsync(stri |
152 | 154 | } |
153 | 155 | } |
154 | 156 |
|
| 157 | +#pragma warning disable AZC0015 // Unexpected client method return type. |
| 158 | + /// <summary> |
| 159 | + /// Creates an <see cref="X509Certificate2"/> from the specified certificate. |
| 160 | + /// </summary> |
| 161 | + /// <remarks> |
| 162 | + /// Because <see cref="KeyVaultCertificate.Cer"/> contains only the public key, this method attempts to download the managed secret |
| 163 | + /// that contains the full certificate. If you do not have permissions to get the secret, |
| 164 | + /// <see cref="RequestFailedException"/> will be thrown with an appropriate error response. |
| 165 | + /// If you want an <see cref="X509Certificate2"/> with only the public key, instantiate it passing only the |
| 166 | + /// <see cref="KeyVaultCertificate.Cer"/> property. |
| 167 | + /// </remarks> |
| 168 | + /// <param name="certificateName">The name of the certificate to download.</param> |
| 169 | + /// <param name="version">Optional version of a certificate to download.</param> |
| 170 | + /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> |
| 171 | + /// <returns>An <see cref="X509Certificate2"/> from the specified certificate.</returns> |
| 172 | + /// <exception cref="ArgumentException"><paramref name="certificateName"/> is empty.</exception> |
| 173 | + /// <exception cref="ArgumentNullException"><paramref name="certificateName"/> is null.</exception> |
| 174 | + /// <exception cref="InvalidOperationException">The managed secret did not contain a certificate.</exception> |
| 175 | + /// <exception cref="RequestFailedException">The request failed. See <see cref="RequestFailedException.ErrorCode"/> and the exception message for details.</exception> |
| 176 | + public virtual Response<X509Certificate2> DownloadCertificate(string certificateName, string version = null, CancellationToken cancellationToken = default) |
| 177 | + { |
| 178 | + Argument.AssertNotNullOrEmpty(certificateName, nameof(certificateName)); |
| 179 | + |
| 180 | + using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(CertificateClient)}.{nameof(DownloadCertificate)}"); |
| 181 | + scope.AddAttribute("certificate", certificateName); |
| 182 | + scope.Start(); |
| 183 | + |
| 184 | + try |
| 185 | + { |
| 186 | + KeyVaultCertificateWithPolicy certificate = _pipeline.SendRequest(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, certificateName, "/", version); |
| 187 | + Response<KeyVaultSecret> secretResponse = _pipeline.SendRequest(RequestMethod.Get, () => new KeyVaultSecret(), certificate.SecretId, cancellationToken); |
| 188 | + |
| 189 | + string value = secretResponse.Value.Value; |
| 190 | + if (string.IsNullOrEmpty(value)) |
| 191 | + { |
| 192 | + throw new InvalidOperationException($"Secret {certificate.SecretId} contains no value"); |
| 193 | + } |
| 194 | + |
| 195 | + byte[] rawData = Convert.FromBase64String(value); |
| 196 | + |
| 197 | + return Response.FromValue(new X509Certificate2(rawData), secretResponse.GetRawResponse()); |
| 198 | + } |
| 199 | + catch (Exception e) |
| 200 | + { |
| 201 | + scope.Failed(e); |
| 202 | + throw; |
| 203 | + } |
| 204 | + } |
| 205 | + |
| 206 | + /// <summary> |
| 207 | + /// Creates an <see cref="X509Certificate2"/> from the specified certificate. |
| 208 | + /// </summary> |
| 209 | + /// <remarks> |
| 210 | + /// Because <see cref="KeyVaultCertificate.Cer"/> contains only the public key, this method attempts to download the managed secret |
| 211 | + /// that contains the full certificate. If you do not have permissions to get the secret, |
| 212 | + /// <see cref="RequestFailedException"/> will be thrown with an appropriate error response. |
| 213 | + /// If you want an <see cref="X509Certificate2"/> with only the public key, instantiate it passing only the |
| 214 | + /// <see cref="KeyVaultCertificate.Cer"/> property. |
| 215 | + /// </remarks> |
| 216 | + /// <param name="certificateName">The name of the certificate to download.</param> |
| 217 | + /// <param name="version">Optional version of a certificate to download.</param> |
| 218 | + /// <param name="cancellationToken">A <see cref="CancellationToken"/> controlling the request lifetime.</param> |
| 219 | + /// <returns>An <see cref="X509Certificate2"/> from the specified certificate.</returns> |
| 220 | + /// <exception cref="ArgumentException"><paramref name="certificateName"/> is empty.</exception> |
| 221 | + /// <exception cref="ArgumentNullException"><paramref name="certificateName"/> is null.</exception> |
| 222 | + /// <exception cref="InvalidOperationException">The managed secret did not contain a certificate.</exception> |
| 223 | + /// <exception cref="RequestFailedException">The request failed. See <see cref="RequestFailedException.ErrorCode"/> and the exception message for details.</exception> |
| 224 | + public virtual async Task<Response<X509Certificate2>> DownloadCertificateAsync(string certificateName, string version = null, CancellationToken cancellationToken = default) |
| 225 | + { |
| 226 | + Argument.AssertNotNullOrEmpty(certificateName, nameof(certificateName)); |
| 227 | + |
| 228 | + using DiagnosticScope scope = _pipeline.CreateScope($"{nameof(CertificateClient)}.{nameof(DownloadCertificate)}"); |
| 229 | + scope.AddAttribute("certificate", certificateName); |
| 230 | + scope.Start(); |
| 231 | + |
| 232 | + try |
| 233 | + { |
| 234 | + KeyVaultCertificateWithPolicy certificate = await _pipeline.SendRequestAsync(RequestMethod.Get, () => new KeyVaultCertificateWithPolicy(), cancellationToken, CertificatesPath, certificateName, "/", version).ConfigureAwait(false); |
| 235 | + Response<KeyVaultSecret> secretResponse = await _pipeline.SendRequestAsync(RequestMethod.Get, () => new KeyVaultSecret(), certificate.SecretId, cancellationToken).ConfigureAwait(false); |
| 236 | + |
| 237 | + string value = secretResponse.Value.Value; |
| 238 | + if (string.IsNullOrEmpty(value)) |
| 239 | + { |
| 240 | + throw new InvalidOperationException($"Secret {certificate.SecretId} contains no value"); |
| 241 | + } |
| 242 | + |
| 243 | + byte[] rawData = Convert.FromBase64String(value); |
| 244 | + |
| 245 | + return Response.FromValue(new X509Certificate2(rawData), secretResponse.GetRawResponse()); |
| 246 | + } |
| 247 | + catch (Exception e) |
| 248 | + { |
| 249 | + scope.Failed(e); |
| 250 | + throw; |
| 251 | + } |
| 252 | + } |
| 253 | +#pragma warning restore AZC0015 // Unexpected client method return type. |
| 254 | + |
155 | 255 | /// <summary> |
156 | 256 | /// Returns the latest version of the <see cref="KeyVaultCertificate"/> along with its <see cref="CertificatePolicy"/>. This operation requires the certificates/get permission. |
157 | 257 | /// </summary> |
|
0 commit comments