Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
BeforeAll {
$vaultName = 'nori-kv765'
. "..\Scripts\Common.ps1"
}

Describe "Import Certificate with policy" {
It "ImportCertificateFromFileWithPolicyParameterSet" {
$certName = Get-CertificateName
$certFilePath = "..\Resources\importCertWithPolicy.pfx"
$policy = New-AzKeyVaultCertificatePolicy -SecretContentType "application/x-pkcs12" -SubjectName "CN=contoso.com" -IssuerName "Self" -ValidityInMonths 6 -ReuseKeyOnRenewal

$cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -FilePath $certFilePath -Policy $policy
$cert.Policy.SecretContentType | Should -Be "application/x-pkcs12"
}
It "ImportCertificateFromFileWithPolicyFileParameterSet" {
$certName = Get-CertificateName
$certFilePath = "..\Resources\importCertWithPolicy.pfx"
$policyPath = "..\Resources\certPolicy.json"

$cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -FilePath $certFilePath -PolicyPath $policyPath
$cert.Policy.SecretContentType | Should -Be "application/x-pkcs12"
}
It "ImportWithPrivateKeyFromStringWithPolicyFileParameterSet" {
$certName = Get-CertificateName
$certFilePath = "..\Resources\importCertWithPolicy.pfx"
$policyPath = "..\Resources\certPolicy.json"
$Base64StringCertificate = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($certFilePath))

$cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -CertificateString $Base64StringCertificate -PolicyPath $policyPath
$cert.Policy.SecretContentType | Should -Be "application/x-pkcs12"
}
It "ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet" {
$certName = Get-CertificateName
$certFilePath = "..\Resources\importCertWithPolicy.pfx"
$policyPath = "..\Resources\certPolicy.json"
$certCollection = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new()
$certCollection.Import($certFilePath, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)

$cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -CertificateCollection $certCollection -PolicyPath $policyPath
$cert.Policy.SecretContentType | Should -Be "application/x-pkcs12"
}
}

40 changes: 40 additions & 0 deletions src/KeyVault/KeyVault.Test/Resources/certPolicy.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"id": "https://myvault.vault.azure.net/certificates/updateCert01/policy",
"key_props": {
"exportable": true,
"kty": "RSA",
"key_size": 2048,
"reuse_key": false
},

"secret_props": {
"contentType": "application/x-pkcs12"
},

"x509_props": {
"subject": "CN=KeyVaultTest",
"ekus": [],
"key_usage": [],
"validity_months": 297
},

"lifetime_actions": [
{
"trigger": {
"lifetime_percentage": 80
},
"action": {
"action_type": "EmailContacts"
}
}
],

"issuer": {
"name": "Unknown"
},
"attributes": {
"enabled": true,
"created": 1482188947,
"updated": 1482188947
}
}
Binary file not shown.
1 change: 1 addition & 0 deletions src/KeyVault/KeyVault/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- Additional information about change #1
-->
## Upcoming Release
* Added parameter `PolicyPath` in `Import-AzKeyVaultCertificate` to support custom policy [#20780]

## Version 4.9.2
* Updated Azure.Core to 1.28.0.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
using Microsoft.Azure.Commands.Common.Exceptions;
using Microsoft.Azure.Commands.KeyVault.Properties;

using Newtonsoft.Json;
using System.Text.Json;

namespace Microsoft.Azure.Commands.KeyVault
{
/// <summary>
Expand All @@ -36,15 +39,18 @@ namespace Microsoft.Azure.Commands.KeyVault
/// importing an existing certificate package file that contains both the
/// certificate and private key (example: PFX or P12 files).
/// </summary>
[Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileParameterSet)]
[Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileWithPolicyParameterSet)]
Comment thread
NoriZC marked this conversation as resolved.
Outdated
[OutputType(typeof(PSKeyVaultCertificate))]
public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
{
#region Parameter Set Names

private const string ImportCertificateFromFileParameterSet = "ImportCertificateFromFile";
private const string ImportWithPrivateKeyFromCollectionParameterSet = "ImportWithPrivateKeyFromCollection";
private const string ImportWithPrivateKeyFromStringParameterSet = "ImportWithPrivateKeyFromString";
private const string ImportCertificateFromFileWithPolicyParameterSet = "ImportCertificateFromFileWithPolicy";
Comment thread
NoriZC marked this conversation as resolved.
Outdated
private const string ImportWithPrivateKeyFromCollectionWithPolicyParameterSet = "ImportWithPrivateKeyFromCollectionWithPolicy";
private const string ImportWithPrivateKeyFromStringWithPolicyParameterSet = "ImportWithPrivateKeyFromStringWithPolicy";
private const string ImportCertificateFromFileWithPolicyFileParameterSet = "ImportCertificateFromFileWithPolicyFile";
private const string ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet = "ImportWithPrivateKeyFromCollectionWithPolicyFile";
private const string ImportWithPrivateKeyFromStringWithPolicyFileParameterSet = "ImportWithPrivateKeyFromStringWithPolicyFile";

#endregion

Expand Down Expand Up @@ -76,23 +82,32 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
/// File Path
/// </summary>
[Parameter(Mandatory = true,
ParameterSetName = ImportCertificateFromFileParameterSet,
ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet,
HelpMessage = "Specifies the path to the file that contains the certificate to add to key vault.")]
[Parameter(Mandatory = true,
ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet,
HelpMessage = "Specifies the path to the file that contains the certificate to add to key vault.")]
public string FilePath { get; set; }

/// <summary>
/// Base64 encoded representation of the certificate object to import
/// </summary>
[Parameter(Mandatory = true,
ParameterSetName = ImportWithPrivateKeyFromStringParameterSet,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet,
HelpMessage = "Base64 encoded representation of the certificate object to import. This certificate needs to contain the private key.")]
[Parameter(Mandatory = true,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet,
HelpMessage = "Base64 encoded representation of the certificate object to import. This certificate needs to contain the private key.")]
public string CertificateString { get; set; }

/// <summary>
/// Specifies type of the certificate to be imported.
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringParameterSet,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet,
HelpMessage = "Specifies the type of the certificate to be imported. Regards certificate string as PFX format by default.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet,
HelpMessage = "Specifies the type of the certificate to be imported. Regards certificate string as PFX format by default.")]
[PSArgumentCompleter(Constants.Pkcs12ContentType, Constants.PemContentType)]
public string ContentType { get; set; } = Constants.Pkcs12ContentType;
Expand All @@ -101,20 +116,65 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
/// Password
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = ImportCertificateFromFileParameterSet,
ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet,
HelpMessage = "Specifies the password for the certificate and private key file to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet,
HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet,
HelpMessage = "Specifies the password for the certificate collection and private key to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet,
HelpMessage = "Specifies the password for the certificate and private key file to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringParameterSet,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet,
HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet,
HelpMessage = "Specifies the password for the certificate collection and private key to import.")]
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated
public SecureString Password { get; set; }

/// <summary>
/// File Path
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet,
HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")]
Comment thread
NoriZC marked this conversation as resolved.
Outdated
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet,
HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet,
HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")]
public string PolicyPath { get; set; }
Comment thread
BethanyZhou marked this conversation as resolved.

/// <summary>
/// File Path
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet,
HelpMessage = "Specifies the certificate policy to import to key vault.")]
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet,
HelpMessage = "Specifies the certificate policy to import to key vault.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet,
HelpMessage = "Specifies the certificate policy to import to key vault.")]
public PSKeyVaultCertificatePolicy Policy { get; set; }
Comment thread
NoriZC marked this conversation as resolved.
Outdated

/// <summary>
/// Certificate Collection
/// </summary>
[Parameter(Mandatory = true,
Position = 2,
ValueFromPipeline = true,
ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet,
HelpMessage = "Specifies the certificate collection to add to key vault.")]
[Parameter(Mandatory = true,
Position = 2,
ValueFromPipeline = true,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet,
HelpMessage = "Specifies the certificate collection to add to key vault.")]
public X509Certificate2Collection CertificateCollection { get; set; }

Expand All @@ -131,6 +191,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
protected override void BeginProcessing()
{
FilePath = this.TryResolvePath(FilePath);
PolicyPath = this.TryResolvePath(PolicyPath);
base.BeginProcessing();
}

Expand All @@ -144,6 +205,13 @@ private void ValidateParameters()
throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.FilePath), nameof(FilePath));
}
}
if (this.IsParameterBound(c => c.PolicyPath))
{
if (!File.Exists(PolicyPath))
Comment on lines +179 to +181

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (this.IsParameterBound(c => c.PolicyPath))
{
if (!File.Exists(PolicyPath))
if (this.IsParameterBound(c => c.PolicyPath)&&!File.Exists(PolicyPath))

NIT

{
throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.PolicyPath), nameof(PolicyPath));
}
}
}

public override void ExecuteCmdlet()
Expand All @@ -153,16 +221,27 @@ public override void ExecuteCmdlet()
ValidateParameters();
Comment thread
BethanyZhou marked this conversation as resolved.

PSKeyVaultCertificate certBundle = null;
PSKeyVaultCertificatePolicy policy = null;
if (!string.IsNullOrEmpty(PolicyPath))
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated
{
policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath);
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated
}
else if ( Policy != null)
{
policy = Policy;
}

switch (ParameterSetName)
{
case ImportCertificateFromFileParameterSet:
case ImportCertificateFromFileWithPolicyParameterSet:
case ImportCertificateFromFileWithPolicyFileParameterSet:

// Pem file can't be handled by X509Certificate2Collection in dotnet standard
// Just read it as raw data and pass it to service side
if (IsPemFile(FilePath))
{
byte[] pemBytes = File.ReadAllBytes(FilePath);
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType);
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, certPolicy: policy);
}
else
{
Expand All @@ -179,8 +258,9 @@ public override void ExecuteCmdlet()

if (doImport)
{

byte[] base64Bytes = userProvidedCertColl.Export(X509ContentType.Pfx, Password?.ConvertToString());
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary());
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary(), certPolicy:policy);
}
else
{
Expand All @@ -193,13 +273,15 @@ public override void ExecuteCmdlet()
}
break;

case ImportWithPrivateKeyFromCollectionParameterSet:
certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateCollection, Tag?.ConvertToDictionary());
case ImportWithPrivateKeyFromCollectionWithPolicyParameterSet:
case ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet:
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: policy);
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated

break;

case ImportWithPrivateKeyFromStringParameterSet:
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType);
case ImportWithPrivateKeyFromStringWithPolicyParameterSet:
case ImportWithPrivateKeyFromStringWithPolicyFileParameterSet:
certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: policy);

break;
}
Expand Down
6 changes: 3 additions & 3 deletions src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,11 +164,11 @@ public interface IKeyVaultDataServiceClient

PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, byte[] certBytes, Dictionary<string, string> tags);

PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType);
PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null);

PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType);
PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null);

PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType);
PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, SecureString certPassword, IDictionary<string, string> tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null);

PSDeletedKeyVaultCertificate DeleteCertificate(string vaultName, string certName);

Expand Down
Loading