Skip to content

Commit 311f34e

Browse files
authored
Improve docs and add sample for cert import (Azure#16610)
Resolves Azure#13043
1 parent 1d5ec76 commit 311f34e

14 files changed

+400
-25
lines changed

sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ description: Samples for the Azure.Security.KeyVault.Certificates client library
1313

1414
- [Setting, getting, updating, and deleting certificates](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample1_HelloWorld.md)
1515
- [Listing certificates, certificate versions, and deleted certificates](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample2_GetCertificates.md)
16+
- [Importing PKCS#12 (PFX) and PEM-formatted certificates](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample2_ImportCertificate.md)

sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample1_HelloWorld.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ You can use the [DefaultAzureCredential][DefaultAzureCredential] to try a number
1111
In the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application.
1212

1313
```C# Snippet:CertificatesSample1CertificateClient
14-
var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
14+
CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
1515
```
1616

1717
## Creating a certificate
@@ -36,7 +36,8 @@ while (!certOp.HasCompleted)
3636
We can now get the created certificate along with its policy from the Azure Key Vault.
3737

3838
```C# Snippet:CertificatesSample1GetCertificateWithPolicy
39-
KeyVaultCertificateWithPolicy certificate = client.GetCertificate(certName);
39+
Response<KeyVaultCertificateWithPolicy> certificateResponse = client.GetCertificate(certName);
40+
KeyVaultCertificateWithPolicy certificate = certificateResponse.Value;
4041

4142
Debug.WriteLine($"Certificate was returned with name {certificate.Name} which expires {certificate.Properties.ExpiresOn}");
4243
```
@@ -49,8 +50,8 @@ We find that the certificate has been compromised and we want to disable it so a
4950
CertificateProperties certificateProperties = certificate.Properties;
5051
certificateProperties.Enabled = false;
5152

52-
KeyVaultCertificate updatedCert = client.UpdateCertificateProperties(certificateProperties);
53-
Debug.WriteLine($"Certificate enabled set to '{updatedCert.Properties.Enabled}'");
53+
Response<KeyVaultCertificate> updatedCertResponse = client.UpdateCertificateProperties(certificateProperties);
54+
Debug.WriteLine($"Certificate enabled set to '{updatedCertResponse.Value.Properties.Enabled}'");
5455
```
5556

5657
## Creating a certificate with a new version

sdk/keyvault/Azure.Security.KeyVault.Certificates/samples/Sample2_GetCertificates.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ You can use the [DefaultAzureCredential][DefaultAzureCredential] to try a number
1111
In the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application.
1212

1313
```C# Snippet:CertificatesSample2CertificateClient
14-
var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
14+
CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
1515
```
1616

1717
## Creating certificates
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Importing PKCS#12 (PFX) and PEM-formatted certificates
2+
3+
This sample demonstrates how to import both PKCS#12 (PFX) and PEM-formatted certificates into Azure Key Vault.
4+
To get started, you'll need a URI to an Azure Key Vault. See the [README](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/README.md) for links and instructions.
5+
6+
## Creating a CertificateClient
7+
8+
To create a new `CertificateClient` to import certificates, you need the endpoint to an Azure Key Vault and credentials.
9+
You can use the [DefaultAzureCredential][DefaultAzureCredential] to try a number of common authentication methods optimized for both running as a service and development.
10+
11+
In the sample below, you can set `keyVaultUrl` based on an environment variable, configuration setting, or any way that works for your application.
12+
13+
```C# Snippet:CertificatesSample3CertificateClient
14+
CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
15+
```
16+
17+
## Import a PFX certificate
18+
19+
Assuming you already have a PFX containing your key pair, you can import it into Key Vault.
20+
You can do this without setting a policy, but the policy is needed if you want the private key to be exportable
21+
or to configure actions when a certificate is close to expiration:
22+
23+
```C# Snippet:CertificatesSample3ImportPfxCertificate
24+
string name = $"cert-{Guid.NewGuid()}";
25+
byte[] pfx = File.ReadAllBytes("certificate.pfx");
26+
ImportCertificateOptions importOptions = new ImportCertificateOptions(name, pfx)
27+
{
28+
Policy = new CertificatePolicy(WellKnownIssuerNames.Self, "CN=contoso.com")
29+
{
30+
// Required when setting a policy; if no policy required, Pfx is assumed.
31+
ContentType = CertificateContentType.Pkcs12,
32+
33+
// Optionally mark the private key exportable.
34+
Exportable = true
35+
}
36+
};
37+
38+
client.ImportCertificate(importOptions);
39+
```
40+
41+
## Import a PEM-formatted certificate
42+
43+
PEM-formatted certificates are more common when using tools like _openssl_. To import a PEM-formatted certificate,
44+
you must set a `CertificatePolicy` that sets the `ContentType` to `CertificateContentType.Pem` or the certificate
45+
will fail to import:
46+
47+
```C# Snippet:CertificatesSample3ImportPemCertificate
48+
string name = $"cert-{Guid.NewGuid()}";
49+
byte[] pem = File.ReadAllBytes("certificate.cer");
50+
ImportCertificateOptions importOptions = new ImportCertificateOptions(name, pem)
51+
{
52+
Policy = new CertificatePolicy(WellKnownIssuerNames.Self, "CN=contoso.com")
53+
{
54+
// Required when the certificate bytes are a PEM-formatted certificate.
55+
ContentType = CertificateContentType.Pem,
56+
57+
// Optionally mark the private key exportable.
58+
Exportable = true
59+
}
60+
};
61+
62+
client.ImportCertificate(importOptions);
63+
```
64+
65+
## Source
66+
67+
To see the full example source, see:
68+
69+
* [Synchronous Sample3_ImportCertificate.cs](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample3_ImportCertificate.cs)
70+
* [Asynchronous Sample3_ImportCertificateAsync.cs](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample3_ImportCertificateAsync.cs)
71+
72+
[DefaultAzureCredential]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/identity/Azure.Identity/README.md

sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificateClient.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ public virtual async Task<Response<KeyVaultCertificateWithPolicy>> RestoreCertif
668668
}
669669

670670
/// <summary>
671-
/// Imports a pre-existing certificate to the key vault. The specified certificate must be in PFX or ASCII PEM format, and must contain the private key as well as the X.509 certificates. This operation requires the
671+
/// Imports a pre-existing certificate to the key vault. The specified certificate must be in PFX or ASCII PEM-format, and must contain the private key as well as the X.509 certificates. This operation requires the
672672
/// certificates/import permission.
673673
/// </summary>
674674
/// <param name="importCertificateOptions">The details of the certificate to import to the key vault.</param>
@@ -697,7 +697,7 @@ public virtual Response<KeyVaultCertificateWithPolicy> ImportCertificate(ImportC
697697
}
698698

699699
/// <summary>
700-
/// Imports a pre-existing certificate to the key vault. The specified certificate must be in PFX or ASCII PEM format, and must contain the private key as well as the X.509 certificates. This operation requires the
700+
/// Imports a pre-existing certificate to the key vault. The specified certificate must be in PFX or ASCII PEM-format, and must contain the private key as well as the X.509 certificates. This operation requires the
701701
/// certificates/import permission.
702702
/// </summary>
703703
/// <param name="importCertificateOptions">The details of the certificate to import to the key vault.</param>

sdk/keyvault/Azure.Security.KeyVault.Certificates/src/CertificatePolicy.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,13 @@ public string IssuerName
172172
}
173173

174174
/// <summary>
175-
/// Gets or sets the <see cref="CertificateContentType"/> of the certificate when downloaded from GetSecret.
175+
/// Gets or sets the <see cref="CertificateContentType"/> of the certificate.
176176
/// </summary>
177+
/// <remarks>
178+
/// Set to <see cref="CertificateContentType.Pkcs12"/> when <see cref="KeyVaultCertificate.Cer"/> contains your raw PKCS#12/PFX bytes,
179+
/// or to <see cref="CertificateContentType.Pem"/> when <see cref="KeyVaultCertificate.Cer"/> contains your ASCII PEM-encoded bytes.
180+
/// If not specified, <see cref="CertificateContentType.Pkcs12"/> is assumed.
181+
/// </remarks>
177182
public CertificateContentType? ContentType { get; set; }
178183

179184
/// <summary>

sdk/keyvault/Azure.Security.KeyVault.Certificates/src/ImportCertificateOptions.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ public class ImportCertificateOptions : IJsonSerializable
2828
/// Initializes a new instance of the <see cref="ImportCertificateOptions"/> class.
2929
/// </summary>
3030
/// <param name="name">A name for the imported certificate.</param>
31-
/// <param name="certificate">The PFX or ASCII PEM formatted value of the certificate containing both the X.509 certificates and the private key.</param>
31+
/// <param name="certificate">The PFX or ASCII PEM-formatted value of the certificate containing both the X.509 certificates and the private key.</param>
32+
/// <remarks>
33+
/// If importing an ASCII PEM-formatted certificate, you must also create a <see cref="CertificatePolicy"/> with <see cref="CertificatePolicy.ContentType"/>
34+
/// set to <see cref="CertificateContentType.Pem"/>, and set the <see cref="Policy"/> property. If the <see cref="Policy"/> property or
35+
/// <see cref="CertificatePolicy.ContentType"/> property is not set, <see cref="CertificateContentType.Pkcs12"/> is assumed and the import will fail.
36+
/// </remarks>
3237
/// <exception cref="ArgumentException"><paramref name="name"/> is empty.</exception>
3338
/// <exception cref="ArgumentNullException"><paramref name="name"/> or <paramref name="certificate"/> is null.</exception>
3439
public ImportCertificateOptions(string name, byte[] certificate)
@@ -46,13 +51,16 @@ public ImportCertificateOptions(string name, byte[] certificate)
4651
public string Name { get; }
4752

4853
/// <summary>
49-
/// Gets the PFX or PEM formatted value of the certificate containing both the X.509 certificates and the private key.
54+
/// Gets the PFX or ASCII PEM-formatted value of the certificate containing both the X.509 certificates and the private key.
5055
/// </summary>
5156
public byte[] Certificate { get; }
5257

5358
/// <summary>
54-
/// Gets the policy which governs the lifecycle of the imported certificate and its properties when it is rotated.
59+
/// Gets or sets the policy which governs the lifecycle of the imported certificate and its properties when it is rotated.
5560
/// </summary>
61+
/// <remarks>
62+
/// If setting the policy, <see cref="CertificatePolicy.ContentType"/> must be set to a valid <see cref="CertificateContentType"/> value.
63+
/// </remarks>
5664
public CertificatePolicy Policy { get; set; }
5765

5866
/// <summary>

sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/SampleFixture.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public class SampleFixture: SamplesBase<KeyVaultTestEnvironment>
2323
#pragma warning disable SA1402 // File may only contain a single type
2424
public partial class HelloWorld : SampleFixture { }
2525
public partial class GetCertificates : SampleFixture { }
26+
public partial class ImportCertificate : SampleFixture { }
2627
public partial class Snippets : SampleFixture { }
2728
#pragma warning restore SA1402 // File may only contain a single type
2829
}

sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample1_HelloWorld.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System;
77
using System.Diagnostics;
88
using System.Threading;
9-
using Azure.Security.KeyVault.Tests;
109

1110
namespace Azure.Security.KeyVault.Certificates.Samples
1211
{
@@ -22,7 +21,7 @@ public void HelloWorldSync()
2221
string keyVaultUrl = TestEnvironment.KeyVaultUrl;
2322

2423
#region Snippet:CertificatesSample1CertificateClient
25-
var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
24+
CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
2625
#endregion
2726

2827
#region Snippet:CertificatesSample1CreateCertificate
@@ -38,7 +37,8 @@ public void HelloWorldSync()
3837
#endregion
3938

4039
#region Snippet:CertificatesSample1GetCertificateWithPolicy
41-
KeyVaultCertificateWithPolicy certificate = client.GetCertificate(certName);
40+
Response<KeyVaultCertificateWithPolicy> certificateResponse = client.GetCertificate(certName);
41+
KeyVaultCertificateWithPolicy certificate = certificateResponse.Value;
4242

4343
Debug.WriteLine($"Certificate was returned with name {certificate.Name} which expires {certificate.Properties.ExpiresOn}");
4444
#endregion
@@ -47,8 +47,8 @@ public void HelloWorldSync()
4747
CertificateProperties certificateProperties = certificate.Properties;
4848
certificateProperties.Enabled = false;
4949

50-
KeyVaultCertificate updatedCert = client.UpdateCertificateProperties(certificateProperties);
51-
Debug.WriteLine($"Certificate enabled set to '{updatedCert.Properties.Enabled}'");
50+
Response<KeyVaultCertificate> updatedCertResponse = client.UpdateCertificateProperties(certificateProperties);
51+
Debug.WriteLine($"Certificate enabled set to '{updatedCertResponse.Value.Properties.Enabled}'");
5252
#endregion
5353

5454
#region Snippet:CertificatesSample1CreateCertificateWithNewVersion

sdk/keyvault/Azure.Security.KeyVault.Certificates/tests/samples/Sample1_HelloWorldAsync.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
using System;
77
using System.Diagnostics;
88
using System.Threading.Tasks;
9-
using Azure.Security.KeyVault.Tests;
109

1110
namespace Azure.Security.KeyVault.Certificates.Samples
1211
{
@@ -24,7 +23,7 @@ public async Task HelloWorldAsync()
2423
// Instantiate a certificate client that will be used to call the service. Notice that the client is using
2524
// default Azure credentials. To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID',
2625
// 'AZURE_CLIENT_KEY' and 'AZURE_TENANT_ID' are set with the service principal credentials.
27-
var client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
26+
CertificateClient client = new CertificateClient(new Uri(keyVaultUrl), new DefaultAzureCredential());
2827

2928
// Let's create a self-signed certificate using the default policy. If the certificate
3029
// already exists in the Key Vault, then a new version of the key is created.
@@ -36,7 +35,8 @@ public async Task HelloWorldAsync()
3635
// amount of time, so applications should only wait on the operation to complete in the case the issuance time is well
3736
// known and within the scope of the application lifetime. In this case we are creating a self-signed certificate which
3837
// should be issued in a relatively short amount of time.
39-
KeyVaultCertificateWithPolicy certificate = await certOp.WaitForCompletionAsync();
38+
Response<KeyVaultCertificateWithPolicy> certificateResponse = await certOp.WaitForCompletionAsync();
39+
KeyVaultCertificateWithPolicy certificate = certificateResponse.Value;
4040

4141
// At some time later we could get the created certificate along with its policy from the Key Vault.
4242
certificate = await client.GetCertificateAsync(certName);
@@ -48,9 +48,9 @@ public async Task HelloWorldAsync()
4848
CertificateProperties certificateProperties = certificate.Properties;
4949
certificateProperties.Enabled = false;
5050

51-
KeyVaultCertificate updatedCert = await client.UpdateCertificatePropertiesAsync(certificateProperties);
51+
Response<KeyVaultCertificate> updatedCertResponse = await client.UpdateCertificatePropertiesAsync(certificateProperties);
5252

53-
Debug.WriteLine($"Certificate enabled set to '{updatedCert.Properties.Enabled}'");
53+
Debug.WriteLine($"Certificate enabled set to '{updatedCertResponse.Value.Properties.Enabled}'");
5454

5555
// We need to create a new version of the certificate that applications can use to replace the compromised certificate.
5656
// Creating a certificate with the same name and policy as the compromised certificate will create another version of the

0 commit comments

Comments
 (0)