Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 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 @@ -36,15 +36,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 = ImportCertificateFromFileWithPolicyFileParameterSet)]
[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 ImportCertificateFromFileWithPolicyObjectParameterSet = "ImportCertificateFromFileWithPolicyObject";
private const string ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet = "ImportWithPrivateKeyFromCollectionWithPolicyObject";
private const string ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet = "ImportWithPrivateKeyFromStringWithPolicyObject";
private const string ImportCertificateFromFileWithPolicyFileParameterSet = "ImportCertificateFromFileWithPolicyFile";
private const string ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet = "ImportWithPrivateKeyFromCollectionWithPolicyFile";
private const string ImportWithPrivateKeyFromStringWithPolicyFileParameterSet = "ImportWithPrivateKeyFromStringWithPolicyFile";

#endregion

Expand Down Expand Up @@ -76,23 +79,32 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
/// File Path
/// </summary>
[Parameter(Mandatory = true,
ParameterSetName = ImportCertificateFromFileParameterSet,
ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet,
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 = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet,
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 = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet,
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 +113,68 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase
/// Password
/// </summary>
[Parameter(Mandatory = false,
ParameterSetName = ImportCertificateFromFileParameterSet,
ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet,
HelpMessage = "Specifies the password for the certificate and private key file to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet,
HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet,
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 = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")]

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.

Update the description of PolicyPath and PolicyObject to demonstrate they are mutual-exclusive.

[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet,
HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")]
[Parameter(Mandatory = false,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet,
HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition. ")]
public string PolicyPath { get; set; }
Comment thread
BethanyZhou marked this conversation as resolved.

/// <summary>
/// File Path
/// </summary>
[Parameter(Mandatory = false,
ValueFromPipeline = true,
ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet,
HelpMessage = "An in-memory object to specify management policy for the certificate.")]
[Parameter(Mandatory = false,
ValueFromPipeline = true,
ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet,
HelpMessage = "An in-memory object to specify management policy for the certificate.")]
[Parameter(Mandatory = false,
ValueFromPipeline = true,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet,
HelpMessage = "An in-memory object to specify management policy for the certificate.")]
public PSKeyVaultCertificatePolicy PolicyObject { get; set; }

/// <summary>
/// Certificate Collection
/// </summary>
[Parameter(Mandatory = true,
Position = 2,
ValueFromPipeline = true,
ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet,
ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet,
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,32 @@ 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 ( PolicyObject != null)
{
policy = PolicyObject;
}

if ( policy != null && policy.SecretContentType != ContentType)
Comment thread
BethanyZhou marked this conversation as resolved.
Outdated
{
throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {policy.SecretContentType} in Certificate Policy.", ContentType);
}

switch (ParameterSetName)
{
case ImportCertificateFromFileParameterSet:
case ImportCertificateFromFileWithPolicyObjectParameterSet:
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 +263,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 +278,15 @@ public override void ExecuteCmdlet()
}
break;

case ImportWithPrivateKeyFromCollectionParameterSet:
certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateCollection, Tag?.ConvertToDictionary());
case ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet:
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 ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet:
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