From 5ca7ada2e675260b5a8881770d6010bbe915c348 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Tue, 28 Mar 2023 16:43:51 +0800 Subject: [PATCH 01/21] Half way upload. --- .../ImportAzureKeyVaultCertificate.cs | 54 ++++++++++++++++++- .../Models/IKeyVaultDataServiceClient.cs | 2 +- .../Models/KeyVaultDataServiceClient.cs | 2 +- .../Track2KeyVaultDataServiceClient.cs | 3 +- 4 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index fd5db57e2bb0..36b4194bf097 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -27,6 +27,9 @@ using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Properties; +using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; +using Newtonsoft.Json; + namespace Microsoft.Azure.Commands.KeyVault { /// @@ -52,6 +55,8 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// /// VaultName + /// + /// /// [Parameter(Mandatory = true, Position = 0, @@ -108,6 +113,14 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")] public SecureString Password { get; set; } + /// + /// File Path + /// + [Parameter(Mandatory = true, + ParameterSetName = ImportCertificateFromFileParameterSet, + HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + public string PolicyPath { get; set; } + /// /// Certificate Collection /// @@ -137,7 +150,7 @@ protected override void BeginProcessing() private void ValidateParameters() { // Verify the FileNotFound whether exists - if (this.IsParameterBound(c => c.FilePath)) + if (this.IsParameterBound(c => c.FilePath) ) { if (!File.Exists(FilePath)) { @@ -157,12 +170,24 @@ public override void ExecuteCmdlet() switch (ParameterSetName) { case ImportCertificateFromFileParameterSet: + CertificatePolicy policy = null; + if (!String.IsNullOrEmpty(PolicyPath)) + { + policy = (CertificatePolicy)GetPolicyFromFile(PolicyPath); + } // 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); + if (policy != null) + { + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, PolicyPath); + } + else + { + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType); + } } else { @@ -179,6 +204,7 @@ public override void ExecuteCmdlet() if (doImport) { + byte[] base64Bytes = userProvidedCertColl.Export(X509ContentType.Pfx, Password?.ConvertToString()); certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary()); } @@ -227,5 +253,29 @@ internal X509Certificate2Collection InitializeCertificateCollection() return certificateCollection; } + + private CertificatePolicy GetPolicyFromFile(string filePath) + { + CertificatePolicy policy; + /* new CertificatePolicy() + { + ContentType = contentType + } + */ + if (".json".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase)) + { + using (StreamReader r = new StreamReader(filePath)) + { + string jsonContent = r.ReadToEnd(); + // dynamic array = JsonConvert.DeserializeObject(jsonContent); + policy = (CertificatePolicy)JsonConvert.DeserializeObject(jsonContent); + } + } + else + { + throw new AzPSArgumentException(string.Format(Resources.UnsupportedFileFormat, this.PolicyPath), nameof(PolicyPath)); + } + return policy; + } } } diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs index a0d1584057b4..e4c8aa08021b 100644 --- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs @@ -164,7 +164,7 @@ public interface IKeyVaultDataServiceClient PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, byte[] certBytes, Dictionary tags); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null); PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs index 11798d35b8c1..941b15e34101 100644 --- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs @@ -817,7 +817,7 @@ public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null) { return ImportCertificate(vaultName, certName, Convert.ToBase64String(certificate), certPassword, tags, contentType); } diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index 1c275a998d85..825d00066f8f 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -10,6 +10,7 @@ using System.Text; using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties; +using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; namespace Microsoft.Azure.Commands.KeyVault.Track2Models { @@ -244,7 +245,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return VaultClient.ImportCertificate(vaultName, certName, Convert.FromBase64String(certificate), certPassword, tags, contentType); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null) { return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType); } From 3a126d53d10cc8edc6212251680b91ec8ebce8e4 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Thu, 30 Mar 2023 17:18:25 +0800 Subject: [PATCH 02/21] Second time commit-halp way --- src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs | 2 ++ .../KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index fb1aa76bef37..5500e1312ff6 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -18,6 +18,8 @@ using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using Track2Certificate = Azure.Security.KeyVault.Certificates; +Track2Certificate.CertificatePolicy; namespace Microsoft.Azure.Commands.KeyVault.Models { diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index 825d00066f8f..5cd96f7411df 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -10,7 +10,7 @@ using System.Text; using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties; -using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; +//using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; namespace Microsoft.Azure.Commands.KeyVault.Track2Models { From f94fbf7ea238d4965c7dd82d047e82588c885b60 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sun, 2 Apr 2023 22:58:07 +0800 Subject: [PATCH 03/21] sync 4/2 --- .../Models/PSKeyVaultCertificatePolicy.cs | 151 +++++++++++++++++- 1 file changed, 150 insertions(+), 1 deletion(-) diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 5500e1312ff6..75442f16ef3d 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -14,12 +14,13 @@ using Microsoft.Azure.KeyVault.Models; using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using Track2Certificate = Azure.Security.KeyVault.Certificates; -Track2Certificate.CertificatePolicy; +// Track2Certificate.CertificatePolicy; namespace Microsoft.Azure.Commands.KeyVault.Models { @@ -277,6 +278,154 @@ internal CertificatePolicy ToCertificatePolicy() return certificatePolicy; } + internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() + { + Track2Certificate.CertificatePolicy certificatePolicy; + if (!string.IsNullOrWhiteSpace(IssuerName) || !string.IsNullOrWhiteSpace(SubjectName) || ((DnsNames != null) && (!DnsNames.Any()))) + { + // if() + certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); + }else + { + certificatePolicy = new Track2Certificate.CertificatePolicy(); + } + + // KeyProperties + if (!string.IsNullOrWhiteSpace(Kty) || + KeySize.HasValue || + !string.IsNullOrWhiteSpace(Curve) || + ReuseKeyOnRenewal.HasValue || + Exportable.HasValue) + { + certificatePolicy.KeyType = Kty; + certificatePolicy.KeySize = KeySize; + certificatePolicy.KeyCurveName = Curve; + certificatePolicy.Exportable = Exportable; + certificatePolicy.ReuseKey = ReuseKeyOnRenewal; + } + + if (CertificateTransparency.HasValue) + { + certificatePolicy.CertificateTransparency = CertificateTransparency; + } + + if (!string.IsNullOrWhiteSpace(CertificateType)) + { + + certificatePolicy.CertificateType = CertificateType; + } + + if (Enabled.HasValue) + { + certificatePolicy.Enabled = Enabled; + } + + // X509CertificateProperties + if (//!string.IsNullOrWhiteSpace(SubjectName) || + DnsNames != null || + Ekus != null || + KeyUsage != null || + ValidityInMonths.HasValue) + { + /* + var x509CertificateProperties = new X509CertificateProperties + { + Subject = SubjectName, + }; + + if (KeyUsage != null) + { + certificatePolicy.KeyUsage = new List(KeyUsage); + } + + if (Ekus != null) + { + x509CertificateProperties.Ekus = new List(Ekus); + }*/ + + if (DnsNames != null) + { + x509CertificateProperties.SubjectAlternativeNames = new SubjectAlternativeNames + { + DnsNames = new string[DnsNames.Count], + }; + + certificatePolicy.SubjectAlternativeNames.DnsNames = new List(DnsNames); + } + + if (ValidityInMonths.HasValue) + { + x509CertificateProperties.ValidityInMonths = ValidityInMonths.Value; + } + + certificatePolicy.X509CertificateProperties = x509CertificateProperties; + } + + if (RenewAtNumberOfDaysBeforeExpiry.HasValue || + RenewAtPercentageLifetime.HasValue || + EmailAtNumberOfDaysBeforeExpiry.HasValue || + EmailAtPercentageLifetime.HasValue) + { + if ((RenewAtNumberOfDaysBeforeExpiry.HasValue ? 1 : 0) + + (RenewAtPercentageLifetime.HasValue ? 1 : 0) + + (EmailAtNumberOfDaysBeforeExpiry.HasValue ? 1 : 0) + + (EmailAtPercentageLifetime.HasValue ? 1 : 0) > 1) + { + throw new ArgumentException("Only one of the values for RenewAtNumberOfDaysBeforeExpiry, RenewAtPercentageLifetime, EmailAtNumberOfDaysBeforeExpiry, EmailAtPercentageLifetime can be set."); + } + + if (certificatePolicy.LifetimeActions == null) + { + certificatePolicy.LifetimeActions = new List(); + } + + if (RenewAtNumberOfDaysBeforeExpiry.HasValue) + { + certificatePolicy.LifetimeActions.Add( + new LifetimeAction + { + Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.AutoRenew }, + Trigger = new Trigger { DaysBeforeExpiry = RenewAtNumberOfDaysBeforeExpiry }, + } + ); + } + + if (RenewAtPercentageLifetime.HasValue) + { + certificatePolicy.LifetimeActions.Add( + new LifetimeAction + { + Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.AutoRenew }, + Trigger = new Trigger { LifetimePercentage = RenewAtPercentageLifetime }, + } + ); + } + if (EmailAtNumberOfDaysBeforeExpiry.HasValue) + { + certificatePolicy.LifetimeActions.Add( + new LifetimeAction + { + Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.EmailContacts }, + Trigger = new Trigger { DaysBeforeExpiry = EmailAtNumberOfDaysBeforeExpiry }, + } + ); + } + + if (EmailAtPercentageLifetime.HasValue) + { + certificatePolicy.LifetimeActions.Add( + new LifetimeAction + { + Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.EmailContacts }, + Trigger = new Trigger { LifetimePercentage = EmailAtPercentageLifetime }, + } + ); + } + } + + return certificatePolicy; + } + internal static PSKeyVaultCertificatePolicy FromCertificatePolicy(CertificatePolicy certificatePolicy) { return new PSKeyVaultCertificatePolicy From 6bee852ceb74289068b425964adb30f0854c2746 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Mon, 3 Apr 2023 17:16:27 +0800 Subject: [PATCH 04/21] Done transfer to track2 --- .../ImportAzureKeyVaultCertificate.cs | 12 +- .../Models/IKeyVaultDataServiceClient.cs | 2 +- .../Models/KeyVaultDataServiceClient.cs | 2 +- .../Models/PSKeyVaultCertificatePolicy.cs | 121 ++++++++---------- .../Track2KeyVaultDataServiceClient.cs | 2 +- 5 files changed, 64 insertions(+), 75 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 36b4194bf097..55e13ef1c99e 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -170,10 +170,10 @@ public override void ExecuteCmdlet() switch (ParameterSetName) { case ImportCertificateFromFileParameterSet: - CertificatePolicy policy = null; + PSKeyVaultCertificatePolicy policy = null; if (!String.IsNullOrEmpty(PolicyPath)) { - policy = (CertificatePolicy)GetPolicyFromFile(PolicyPath); + policy = (PSKeyVaultCertificatePolicy)GetPolicyFromFile(PolicyPath); } // Pem file can't be handled by X509Certificate2Collection in dotnet standard // Just read it as raw data and pass it to service side @@ -182,7 +182,7 @@ public override void ExecuteCmdlet() byte[] pemBytes = File.ReadAllBytes(FilePath); if (policy != null) { - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, PolicyPath); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, policy); } else { @@ -254,9 +254,9 @@ internal X509Certificate2Collection InitializeCertificateCollection() return certificateCollection; } - private CertificatePolicy GetPolicyFromFile(string filePath) + private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) { - CertificatePolicy policy; + PSKeyVaultCertificatePolicy policy; /* new CertificatePolicy() { ContentType = contentType @@ -268,7 +268,7 @@ private CertificatePolicy GetPolicyFromFile(string filePath) { string jsonContent = r.ReadToEnd(); // dynamic array = JsonConvert.DeserializeObject(jsonContent); - policy = (CertificatePolicy)JsonConvert.DeserializeObject(jsonContent); + policy = (PSKeyVaultCertificatePolicy)JsonConvert.DeserializeObject(jsonContent); } } else diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs index e4c8aa08021b..3c12d6a51d40 100644 --- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs @@ -164,7 +164,7 @@ public interface IKeyVaultDataServiceClient PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, byte[] certBytes, Dictionary tags); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null); PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs index 941b15e34101..b78c28e9e33d 100644 --- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs @@ -817,7 +817,7 @@ public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null) { return ImportCertificate(vaultName, certName, Convert.ToBase64String(certificate), certPassword, tags, contentType); } diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 75442f16ef3d..5732479ee067 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -34,6 +34,8 @@ public class PSKeyVaultCertificatePolicy public bool? ReuseKeyOnRenewal { get; set; } public string SubjectName { get; set; } public List DnsNames { get; set; } + public List Emails { get; set; } + public List UserPrincipalNames { get; set; } public List KeyUsage { get; set; } public List Ekus { get; set; } public int? ValidityInMonths { get; set; } @@ -277,14 +279,54 @@ internal CertificatePolicy ToCertificatePolicy() return certificatePolicy; } + internal bool HasSubjectAlternativeNames() + { + if ((DnsNames != null && DnsNames.Any()) + || (Emails != null && Emails.Any()) + || (UserPrincipalNames != null && UserPrincipalNames.Any())) + { + return true; + } + else + return false; + } + + internal void SetSubjectAlternativeNames(Track2Certificate.CertificatePolicy certificatePolicy) + { + if (DnsNames != null && DnsNames.Any()) + foreach (var dnsName in DnsNames) + certificatePolicy.SubjectAlternativeNames.DnsNames.Add(dnsName); + if (Emails != null && Emails.Any()) + foreach (var email in Emails) + certificatePolicy.SubjectAlternativeNames.Emails.Add(email); + if (UserPrincipalNames != null && UserPrincipalNames.Any()) + foreach (var principalName in UserPrincipalNames) + certificatePolicy.SubjectAlternativeNames.UserPrincipalNames.Add(principalName); + } internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() { Track2Certificate.CertificatePolicy certificatePolicy; - if (!string.IsNullOrWhiteSpace(IssuerName) || !string.IsNullOrWhiteSpace(SubjectName) || ((DnsNames != null) && (!DnsNames.Any()))) + if (!string.IsNullOrWhiteSpace(IssuerName) || !string.IsNullOrWhiteSpace(SubjectName) || HasSubjectAlternativeNames()) { - // if() - certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); + if (!string.IsNullOrWhiteSpace(SubjectName) && HasSubjectAlternativeNames()) + { + Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName, subjectAlternativeNames); + SetSubjectAlternativeNames(certificatePolicy); + } + else if (!string.IsNullOrWhiteSpace(SubjectName)) + { + certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); + } + else if (HasSubjectAlternativeNames()) + { + Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, subjectAlternativeNames); + SetSubjectAlternativeNames(certificatePolicy); + } + else + certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); }else { certificatePolicy = new Track2Certificate.CertificatePolicy(); @@ -305,61 +347,17 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() } if (CertificateTransparency.HasValue) - { certificatePolicy.CertificateTransparency = CertificateTransparency; - } if (!string.IsNullOrWhiteSpace(CertificateType)) - { - certificatePolicy.CertificateType = CertificateType; - } if (Enabled.HasValue) - { certificatePolicy.Enabled = Enabled; - } - - // X509CertificateProperties - if (//!string.IsNullOrWhiteSpace(SubjectName) || - DnsNames != null || - Ekus != null || - KeyUsage != null || - ValidityInMonths.HasValue) - { - /* - var x509CertificateProperties = new X509CertificateProperties - { - Subject = SubjectName, - }; - - if (KeyUsage != null) - { - certificatePolicy.KeyUsage = new List(KeyUsage); - } - - if (Ekus != null) - { - x509CertificateProperties.Ekus = new List(Ekus); - }*/ - - if (DnsNames != null) - { - x509CertificateProperties.SubjectAlternativeNames = new SubjectAlternativeNames - { - DnsNames = new string[DnsNames.Count], - }; - - certificatePolicy.SubjectAlternativeNames.DnsNames = new List(DnsNames); - } + if (ValidityInMonths.HasValue) if (ValidityInMonths.HasValue) - { - x509CertificateProperties.ValidityInMonths = ValidityInMonths.Value; - } - - certificatePolicy.X509CertificateProperties = x509CertificateProperties; - } + certificatePolicy.ValidityInMonths = ValidityInMonths.Value; if (RenewAtNumberOfDaysBeforeExpiry.HasValue || RenewAtPercentageLifetime.HasValue || @@ -374,18 +372,12 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() throw new ArgumentException("Only one of the values for RenewAtNumberOfDaysBeforeExpiry, RenewAtPercentageLifetime, EmailAtNumberOfDaysBeforeExpiry, EmailAtPercentageLifetime can be set."); } - if (certificatePolicy.LifetimeActions == null) - { - certificatePolicy.LifetimeActions = new List(); - } - if (RenewAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new LifetimeAction + new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("AutoRenew")) { - Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.AutoRenew }, - Trigger = new Trigger { DaysBeforeExpiry = RenewAtNumberOfDaysBeforeExpiry }, + DaysBeforeExpiry = RenewAtNumberOfDaysBeforeExpiry } ); } @@ -393,20 +385,18 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (RenewAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new LifetimeAction + new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("AutoRenew")) { - Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.AutoRenew }, - Trigger = new Trigger { LifetimePercentage = RenewAtPercentageLifetime }, + LifetimePercentage = RenewAtPercentageLifetime } ); } if (EmailAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new LifetimeAction + new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("EmailContacts")) { - Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.EmailContacts }, - Trigger = new Trigger { DaysBeforeExpiry = EmailAtNumberOfDaysBeforeExpiry }, + DaysBeforeExpiry = EmailAtNumberOfDaysBeforeExpiry } ); } @@ -414,10 +404,9 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (EmailAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new LifetimeAction + new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("EmailContacts")) { - Action = new Azure.KeyVault.Models.Action { ActionType = ActionType.EmailContacts }, - Trigger = new Trigger { LifetimePercentage = EmailAtPercentageLifetime }, + LifetimePercentage = EmailAtPercentageLifetime } ); } diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index 5cd96f7411df..20aec8053a52 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -245,7 +245,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return VaultClient.ImportCertificate(vaultName, certName, Convert.FromBase64String(certificate), certPassword, tags, contentType); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, string certPolicyPath = null) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null) { return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType); } From 03756885bf400f8035bd6b233cfb6e6f404b340c Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Wed, 5 Apr 2023 22:57:58 +0800 Subject: [PATCH 05/21] sync 4/5 --- .../ImportAzureKeyVaultCertificate.cs | 10 +- .../Models/IKeyVaultDataServiceClient.cs | 4 +- .../Models/KeyVaultDataServiceClient.cs | 2 +- .../Models/PSKeyVaultCertificatePolicy.cs | 206 +++++++++++++++++- .../Track2KeyVaultDataServiceClient.cs | 8 +- .../Track2Models/Track2VaultClient.cs | 21 +- 6 files changed, 233 insertions(+), 18 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 55e13ef1c99e..2c22c0bb2ed0 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -27,8 +27,8 @@ using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Properties; -using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; using Newtonsoft.Json; +using System.Text.Json; namespace Microsoft.Azure.Commands.KeyVault { @@ -173,7 +173,7 @@ public override void ExecuteCmdlet() PSKeyVaultCertificatePolicy policy = null; if (!String.IsNullOrEmpty(PolicyPath)) { - policy = (PSKeyVaultCertificatePolicy)GetPolicyFromFile(PolicyPath); + policy = GetPolicyFromFile(PolicyPath); } // Pem file can't be handled by X509Certificate2Collection in dotnet standard // Just read it as raw data and pass it to service side @@ -206,7 +206,7 @@ public override void ExecuteCmdlet() { 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 { @@ -268,7 +268,7 @@ private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) { string jsonContent = r.ReadToEnd(); // dynamic array = JsonConvert.DeserializeObject(jsonContent); - policy = (PSKeyVaultCertificatePolicy)JsonConvert.DeserializeObject(jsonContent); + var policyJson = JsonConvert.DeserializeObject(jsonContent); //(PSKeyVaultCertificatePolicy) } } else @@ -277,5 +277,7 @@ private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) } return policy; } + + } } diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs index 3c12d6a51d40..f8ec3f0451c9 100644 --- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs @@ -164,9 +164,9 @@ public interface IKeyVaultDataServiceClient PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, byte[] certBytes, Dictionary tags); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null); PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType); diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs index b78c28e9e33d..d6530c509b2e 100644 --- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs @@ -822,7 +822,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return ImportCertificate(vaultName, certName, Convert.ToBase64String(certificate), certPassword, tags, contentType); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertColl, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertColl, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null) { if (string.IsNullOrEmpty(vaultName)) throw new ArgumentNullException(nameof(vaultName)); diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 5732479ee067..765341bd39f4 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -12,15 +12,22 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +// using Azure.Security.KeyVault.Keys; +using Microsoft.Azure.Commands.Common.Exceptions; +using Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; +using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text.Json; using Track2Certificate = Azure.Security.KeyVault.Certificates; -// Track2Certificate.CertificatePolicy; + +// Track2Certificate.CertificatePolicy namespace Microsoft.Azure.Commands.KeyVault.Models { @@ -443,6 +450,203 @@ internal static PSKeyVaultCertificatePolicy FromCertificatePolicy(CertificatePol Updated = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Updated, }; } + private void ReadKeyProperties(JsonElement json) + { + foreach (JsonProperty item in json.EnumerateObject()) + { + switch (item.Name) + { + case "kty": + Kty = item.Value.GetString(); + break; + case "reuse_key": + ReuseKeyOnRenewal = item.Value.GetBoolean(); + break; + case "exportable": + Exportable = item.Value.GetBoolean(); + break; + case "crv": + Curve = item.Value.GetString(); + break; + case "key_size": + KeySize = item.Value.GetInt32(); + break; + } + } + } + private void ReadSecretProperties(JsonElement json) + { + if (json.TryGetProperty("contentType", out var value)) + { + SecretContentType = value.GetString(); + } + } + + internal interface IJsonDeserializable + { + void ReadProperties(JsonElement json); + } + + private void ReadX509CertificateProperties(JsonElement json) + { + foreach (JsonProperty item in json.EnumerateObject()) + { + switch (item.Name) + { + case "subject": + SubjectName = item.Value.GetString(); + break; + case "sans": + var SubjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + ((IJsonDeserializable)SubjectAlternativeNames).ReadProperties(item.Value); + break; + case "key_usage": + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + KeyUsage.Add(item2.GetString()); + } + + break; + case "ekus": + foreach (JsonElement item3 in item.Value.EnumerateArray()) + { + Ekus.Add(item3.GetString()); + } + + break; + case "validity_months": + ValidityInMonths = item.Value.GetInt32(); + break; + } + } + } + + private void ReadIssuerProperties(JsonElement json) + { + foreach (JsonProperty item in json.EnumerateObject()) + { + switch (item.Name) + { + case "subject": + SubjectName = item.Value.GetString(); + break; + case "sans": + var SubjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + ((IJsonDeserializable)SubjectAlternativeNames).ReadProperties(item.Value); + break; + case "key_usage": + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + KeyUsage.Add(item2.GetString()); + } + + break; + case "ekus": + foreach (JsonElement item3 in item.Value.EnumerateArray()) + { + Ekus.Add(item3.GetString()); + } + + break; + case "validity_months": + ValidityInMonths = item.Value.GetInt32(); + break; + } + } + } + + private void ReadAttributesProperties(JsonElement json) + { + foreach (JsonProperty item in json.EnumerateObject()) + { + switch (item.Name) + { + case "enabled": + Enabled = item.Value.GetBoolean(); + break; + case "created": + CreatedOn = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()); + break; + case "updated": + UpdatedOn = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()); + break; + } + } + } + internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) + { + PSKeyVaultCertificatePolicy policy = new PSKeyVaultCertificatePolicy(); + JsonElement policyJson; + if (".json".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase)) + { + using (StreamReader r = new StreamReader(filePath)) + { + string jsonContent = r.ReadToEnd(); + // dynamic array = JsonConvert.DeserializeObject(jsonContent); + policyJson = JsonConvert.DeserializeObject(jsonContent); //(PSKeyVaultCertificatePolicy) + } + } + else + { + throw new AzPSArgumentException(string.Format(Resources.UnsupportedFileFormat, filePath), nameof(filePath)); + } + foreach (JsonProperty item in policyJson.EnumerateObject()) + { + switch (item.Name) + { + case "key_props": + policy.ReadKeyProperties(item.Value); + break; + case "secret_props": + policy.ReadSecretProperties(item.Value); + break; + case "x509_props": + policy.ReadX509CertificateProperties(item.Value); + break; + case "issuer": + policy.ReadIssuerProperties(item.Value); + break; + case "attributes": + policy.ReadAttributesProperties(item.Value); + break; + case "lifetime_actions": + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + LifetimeActions.Add(LifetimeAction.FromJsonObject(item2)); + } + + break; + } + } + /* + return new PSKeyVaultCertificatePolicy + { + SecretContentType = policyJson["SecretProperties"] == null ? null : certificatePolicy.SecretProperties.ContentType, + Kty = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.KeyType, + KeySize = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.KeySize, + Curve = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.Curve, + Exportable = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.Exportable, + ReuseKeyOnRenewal = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.ReuseKey, + SubjectName = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.Subject, + DnsNames = certificatePolicy.X509CertificateProperties == null || certificatePolicy.X509CertificateProperties.SubjectAlternativeNames == null ? + null : new List(certificatePolicy.X509CertificateProperties.SubjectAlternativeNames.DnsNames), + KeyUsage = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.KeyUsage == null ? null : new List(certificatePolicy.X509CertificateProperties.KeyUsage), + Ekus = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.Ekus == null ? null : new List(certificatePolicy.X509CertificateProperties.Ekus), + ValidityInMonths = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.ValidityInMonths, + CertificateTransparency = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.CertificateTransparency, + IssuerName = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.Name, + CertificateType = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.CertificateType, + RenewAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions, (trigger) => trigger.DaysBeforeExpiry), + RenewAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions, (trigger) => trigger.LifetimePercentage), + EmailAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions, (trigger) => trigger.DaysBeforeExpiry), + EmailAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions, (trigger) => trigger.LifetimePercentage), + Enabled = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Enabled, + Created = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Created, + Updated = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Updated, + }; + */ + return policy; + } private static int? FindIntValueForAutoRenewAction(IEnumerable lifetimeActions, Func intValueGetter) { diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index 20aec8053a52..f864247f3fa0 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -240,14 +240,14 @@ public IEnumerable GetDeletedCertifica throw new NotImplementedException(); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - return VaultClient.ImportCertificate(vaultName, certName, Convert.FromBase64String(certificate), certPassword, tags, contentType); + return VaultClient.ImportCertificate(vaultName, certName, Convert.FromBase64String(certificate), certPassword, tags, contentType, certPolicy); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicyPath = null) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType); + return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType, certPolicy); } public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType) diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index 92f206f5f2fe..bd09d082a3b7 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -211,7 +211,7 @@ private PSKeyRotationPolicy SetKeyRotationPolicy(KeyClient client, string vaultN #endregion #region Certificate actions - internal PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + internal PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { if (string.IsNullOrEmpty(vaultName)) throw new ArgumentNullException(nameof(vaultName)); @@ -221,17 +221,26 @@ internal PSKeyVaultCertificate ImportCertificate(string vaultName, string certNa throw new ArgumentNullException(nameof(certificate)); var certClient = CreateCertificateClient(vaultName); - return ImportCertificate(certClient, certName, certificate, password, tags, contentType); + return ImportCertificate(certClient, certName, certificate, password, tags, contentType, certPolicy); } - private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - var options = new ImportCertificateOptions(certName, certificate) + CertificatePolicy certificatePolicy = null; + if (certPolicy == null) { - Policy = new CertificatePolicy() + certificatePolicy = new CertificatePolicy() { ContentType = contentType - }, + }; + } + else + { + certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); + } + var options = new ImportCertificateOptions(certName, certificate) + { + Policy = certificatePolicy, Password = password?.ConvertToString() }; tags?.ForEach((entry) => From 74547ca3a5e4675936d0fe6b65414624fde59af2 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Thu, 6 Apr 2023 17:09:16 +0800 Subject: [PATCH 06/21] Complete import cert with policy --- .../ImportAzureKeyVaultCertificate.cs | 12 +- .../Models/PSKeyVaultCertificatePolicy.cs | 174 +++++++++++------- 2 files changed, 110 insertions(+), 76 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 2c22c0bb2ed0..3c617e0f507d 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -116,7 +116,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// /// File Path /// - [Parameter(Mandatory = true, + [Parameter(Mandatory = false, ParameterSetName = ImportCertificateFromFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] public string PolicyPath { get; set; } @@ -171,9 +171,9 @@ public override void ExecuteCmdlet() { case ImportCertificateFromFileParameterSet: PSKeyVaultCertificatePolicy policy = null; - if (!String.IsNullOrEmpty(PolicyPath)) + if (!string.IsNullOrEmpty(PolicyPath)) { - policy = GetPolicyFromFile(PolicyPath); + policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); } // Pem file can't be handled by X509Certificate2Collection in dotnet standard // Just read it as raw data and pass it to service side @@ -253,7 +253,7 @@ internal X509Certificate2Collection InitializeCertificateCollection() return certificateCollection; } - + /* private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) { PSKeyVaultCertificatePolicy policy; @@ -261,7 +261,7 @@ private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) { ContentType = contentType } - */ + if (".json".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase)) { using (StreamReader r = new StreamReader(filePath)) @@ -276,7 +276,7 @@ private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) throw new AzPSArgumentException(string.Format(Resources.UnsupportedFileFormat, this.PolicyPath), nameof(PolicyPath)); } return policy; - } + }*/ } diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 765341bd39f4..7ef063cc0936 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -17,11 +17,13 @@ using Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; using Newtonsoft.Json; +using Org.BouncyCastle.Bcpg; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Management.Automation; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.Json; @@ -298,17 +300,19 @@ internal bool HasSubjectAlternativeNames() return false; } - internal void SetSubjectAlternativeNames(Track2Certificate.CertificatePolicy certificatePolicy) + internal Track2Certificate.SubjectAlternativeNames SetSubjectAlternativeNames(Track2Certificate.SubjectAlternativeNames SubjectAlternativeNames) { if (DnsNames != null && DnsNames.Any()) foreach (var dnsName in DnsNames) - certificatePolicy.SubjectAlternativeNames.DnsNames.Add(dnsName); + SubjectAlternativeNames.DnsNames.Add(dnsName); if (Emails != null && Emails.Any()) foreach (var email in Emails) - certificatePolicy.SubjectAlternativeNames.Emails.Add(email); + SubjectAlternativeNames.Emails.Add(email); if (UserPrincipalNames != null && UserPrincipalNames.Any()) foreach (var principalName in UserPrincipalNames) - certificatePolicy.SubjectAlternativeNames.UserPrincipalNames.Add(principalName); + SubjectAlternativeNames.UserPrincipalNames.Add(principalName); + // SubjectAlternativeNames.UserPrincipalNames.Add(principalName); + return SubjectAlternativeNames; } internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() @@ -319,8 +323,9 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (!string.IsNullOrWhiteSpace(SubjectName) && HasSubjectAlternativeNames()) { Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + subjectAlternativeNames = SetSubjectAlternativeNames(subjectAlternativeNames); certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName, subjectAlternativeNames); - SetSubjectAlternativeNames(certificatePolicy); + } else if (!string.IsNullOrWhiteSpace(SubjectName)) { @@ -329,8 +334,9 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() else if (HasSubjectAlternativeNames()) { Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + subjectAlternativeNames = SetSubjectAlternativeNames(subjectAlternativeNames); certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, subjectAlternativeNames); - SetSubjectAlternativeNames(certificatePolicy); + // SetSubjectAlternativeNames(certificatePolicy); } else certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); @@ -338,20 +344,23 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() { certificatePolicy = new Track2Certificate.CertificatePolicy(); } + if (!string.IsNullOrWhiteSpace(SecretContentType)) + certificatePolicy.ContentType = SecretContentType; - // KeyProperties - if (!string.IsNullOrWhiteSpace(Kty) || - KeySize.HasValue || - !string.IsNullOrWhiteSpace(Curve) || - ReuseKeyOnRenewal.HasValue || - Exportable.HasValue) - { + if (!string.IsNullOrWhiteSpace(Kty)) certificatePolicy.KeyType = Kty; + + if (KeySize.HasValue) certificatePolicy.KeySize = KeySize; + + if (!string.IsNullOrWhiteSpace(Curve)) certificatePolicy.KeyCurveName = Curve; - certificatePolicy.Exportable = Exportable; + + if (ReuseKeyOnRenewal.HasValue) certificatePolicy.ReuseKey = ReuseKeyOnRenewal; - } + + if (Exportable.HasValue) + certificatePolicy.Exportable = Exportable; if (CertificateTransparency.HasValue) certificatePolicy.CertificateTransparency = CertificateTransparency; @@ -482,9 +491,35 @@ private void ReadSecretProperties(JsonElement json) } } - internal interface IJsonDeserializable + private void ReadSubjectAlternativeNames(JsonProperty json) { - void ReadProperties(JsonElement json); + foreach (JsonProperty item in json.Value.EnumerateObject()) + { + switch (item.Name) + { + case "dns_names": + DnsNames = new List(); + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + DnsNames.Add(item2.GetString()); + } + break; + case "emails": + Emails = new List(); + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + Emails.Add(item2.GetString()); + } + break; + case "upns": + UserPrincipalNames = new List(); + foreach (JsonElement item2 in item.Value.EnumerateArray()) + { + UserPrincipalNames.Add(item2.GetString()); + } + break; + } + } } private void ReadX509CertificateProperties(JsonElement json) @@ -498,7 +533,8 @@ private void ReadX509CertificateProperties(JsonElement json) break; case "sans": var SubjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); - ((IJsonDeserializable)SubjectAlternativeNames).ReadProperties(item.Value); + ReadSubjectAlternativeNames(item); + // SubjectAlternativeNames .ReadProperties(item.Value); break; case "key_usage": foreach (JsonElement item2 in item.Value.EnumerateArray()) @@ -527,29 +563,16 @@ private void ReadIssuerProperties(JsonElement json) { switch (item.Name) { - case "subject": - SubjectName = item.Value.GetString(); + case "cert_transparency": + CertificateTransparency = item.Value.GetBoolean(); break; - case "sans": - var SubjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); - ((IJsonDeserializable)SubjectAlternativeNames).ReadProperties(item.Value); + + case "cty": + CertificateType = item.Value.GetString(); break; - case "key_usage": - foreach (JsonElement item2 in item.Value.EnumerateArray()) - { - KeyUsage.Add(item2.GetString()); - } - break; - case "ekus": - foreach (JsonElement item3 in item.Value.EnumerateArray()) - { - Ekus.Add(item3.GetString()); - } - - break; - case "validity_months": - ValidityInMonths = item.Value.GetInt32(); + case "name": + IssuerName = item.Value.GetString(); break; } } @@ -565,10 +588,10 @@ private void ReadAttributesProperties(JsonElement json) Enabled = item.Value.GetBoolean(); break; case "created": - CreatedOn = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()); + Created = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()).DateTime; break; case "updated": - UpdatedOn = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()); + Updated = DateTimeOffset.FromUnixTimeSeconds(item.Value.GetInt64()).DateTime; break; } } @@ -582,8 +605,7 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) using (StreamReader r = new StreamReader(filePath)) { string jsonContent = r.ReadToEnd(); - // dynamic array = JsonConvert.DeserializeObject(jsonContent); - policyJson = JsonConvert.DeserializeObject(jsonContent); //(PSKeyVaultCertificatePolicy) + policyJson = JsonDocument.Parse(jsonContent).RootElement; } } else @@ -609,42 +631,54 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) case "attributes": policy.ReadAttributesProperties(item.Value); break; + case "lifetime_actions": + string actionType = null; + string triggerType = null; + int? triggerValue = null; + foreach (JsonElement item2 in item.Value.EnumerateArray()) { - LifetimeActions.Add(LifetimeAction.FromJsonObject(item2)); + foreach (JsonProperty item3 in item2.EnumerateObject()) + { + if (item3.Name == "trigger") + { + if (item3.Value.EnumerateObject().Count() > 1) + throw new AzPSArgumentException(string.Format("Json file property {0} exceed expected number.", item3.Name), nameof(item.Name)); + foreach (JsonProperty item4 in item3.Value.EnumerateObject()) + { + triggerType = item4.Name; + triggerValue = item4.Value.GetInt32(); + } + } + else if (item3.Name == "action") + { + foreach (JsonProperty item4 in item3.Value.EnumerateObject()) + { + if (item4.Name == "action_type") + actionType = item4.Value.GetString(); + } + } + } } + if (actionType == "AutoRenew") + { + if (triggerType == "days_before_expiry") + policy.RenewAtNumberOfDaysBeforeExpiry = triggerValue; + else if (triggerType == "lifetime_percentage") + policy.RenewAtPercentageLifetime = triggerValue; + } + else if (actionType == "EmailContacts") + { + if (triggerType == "days_before_expiry") + policy.EmailAtNumberOfDaysBeforeExpiry = triggerValue; + else if (triggerType == "lifetime_percentage") + policy.EmailAtPercentageLifetime = triggerValue; + } break; } } - /* - return new PSKeyVaultCertificatePolicy - { - SecretContentType = policyJson["SecretProperties"] == null ? null : certificatePolicy.SecretProperties.ContentType, - Kty = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.KeyType, - KeySize = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.KeySize, - Curve = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.Curve, - Exportable = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.Exportable, - ReuseKeyOnRenewal = certificatePolicy.KeyProperties == null ? null : certificatePolicy.KeyProperties.ReuseKey, - SubjectName = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.Subject, - DnsNames = certificatePolicy.X509CertificateProperties == null || certificatePolicy.X509CertificateProperties.SubjectAlternativeNames == null ? - null : new List(certificatePolicy.X509CertificateProperties.SubjectAlternativeNames.DnsNames), - KeyUsage = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.KeyUsage == null ? null : new List(certificatePolicy.X509CertificateProperties.KeyUsage), - Ekus = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.Ekus == null ? null : new List(certificatePolicy.X509CertificateProperties.Ekus), - ValidityInMonths = certificatePolicy.X509CertificateProperties == null ? null : certificatePolicy.X509CertificateProperties.ValidityInMonths, - CertificateTransparency = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.CertificateTransparency, - IssuerName = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.Name, - CertificateType = certificatePolicy.IssuerParameters == null ? null : certificatePolicy.IssuerParameters.CertificateType, - RenewAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions, (trigger) => trigger.DaysBeforeExpiry), - RenewAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions, (trigger) => trigger.LifetimePercentage), - EmailAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions, (trigger) => trigger.DaysBeforeExpiry), - EmailAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions, (trigger) => trigger.LifetimePercentage), - Enabled = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Enabled, - Created = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Created, - Updated = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Updated, - }; - */ return policy; } From 2591b888c69b674b080f599e8d3cc59346b22278 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sat, 8 Apr 2023 00:49:23 +0800 Subject: [PATCH 07/21] complete all cases for policy --- .../PesterTests/Certificate.Tests.ps1 | 35 ++++++++++ .../KeyVault.Test/Resources/certPolicy.json | 40 +++++++++++ .../KeyVault.Test/Resources/importCert00.pfx | Bin 0 -> 2636 bytes .../ImportAzureKeyVaultCertificate.cs | 59 +++++----------- .../Models/IKeyVaultDataServiceClient.cs | 2 +- .../Models/KeyVaultDataServiceClient.cs | 2 +- .../KeyVault/Models/PSKeyVaultCertificate.cs | 2 + .../Models/PSKeyVaultCertificatePolicy.cs | 65 +++++++++++++++++- .../Track2KeyVaultDataServiceClient.cs | 6 +- 9 files changed, 164 insertions(+), 47 deletions(-) create mode 100644 src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 create mode 100644 src/KeyVault/KeyVault.Test/Resources/certPolicy.json create mode 100644 src/KeyVault/KeyVault.Test/Resources/importCert00.pfx diff --git a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 new file mode 100644 index 000000000000..50b131f0a918 --- /dev/null +++ b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 @@ -0,0 +1,35 @@ +BeforeAll { + $vaultName = 'nori-kv765' + . "..\Scripts\Common.ps1" +} + +Describe "Import Certificate with policy" { + It "ImportCertificateFromFileParameterSet" { + $certName = Get-CertificateName + $certFilePath = "..\Resources\importCert00.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 "ImportWithPrivateKeyFromStringParameterSet" { + $certName = Get-CertificateName + $certFilePath = "..\Resources\importCert00.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 "ImportWithPrivateKeyFromCollectionParameterSet" { + $certName = Get-CertificateName + $certFilePath = "..\Resources\importCert00.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" + } +} + diff --git a/src/KeyVault/KeyVault.Test/Resources/certPolicy.json b/src/KeyVault/KeyVault.Test/Resources/certPolicy.json new file mode 100644 index 000000000000..88af4f552bc7 --- /dev/null +++ b/src/KeyVault/KeyVault.Test/Resources/certPolicy.json @@ -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 + } +} \ No newline at end of file diff --git a/src/KeyVault/KeyVault.Test/Resources/importCert00.pfx b/src/KeyVault/KeyVault.Test/Resources/importCert00.pfx new file mode 100644 index 0000000000000000000000000000000000000000..f89f48815d0e3b49e6cff9d92aa23cf74970aff2 GIT binary patch literal 2636 zcmZWq2{hDe8~@K@#*jhg+9GQi`5Qug)Q zMahySTZ9m~|2p4y@A=Mm-t)ZA^Zb6#?>X;#&b#2qH0n?Y3`eGcBTz!&>fzt$AV^3q z8MOx|qqcEm)CP`>VE!KpfrOJ0pn~cYwC@Po|ESK;K%lu~_zxTzeuU#d(EJw$o1@{> z^7UwPCUq(k6p9>zli`ztNh2N0i=?=rem_mQ<@hXxXrr(|OkDnym_D7ec|h`yFZx&S z3E}s3(gxp5hBvmJ?$QMFbraXhJm1E@oyD3=$Z6sTSM0|3uz{l4+hHOxOwUl)`_!Xc zKew7mM)|N`{|Gbr{nO1?Ojn)e_2M#PTDWmur%+f9g0FKaw|j8T*lWnEo8*}VA*8uA zwXg|HuF!cV(eC?1erB+5Xgoo9FZmEudHaJ%ru`wGZ1>r+^p>Rrd?QjIJu?APaT8*>pn?^rFr zZE24}v{#QxUYySo*Bai5lCmD_H~1-Z=$I*yN9)6-=r!lMJH#i7lU)65GqIl>VsVT5JP*>uA%+^GA}g-y{$5l9!u$(->V;B8P{k?zC6v zvm2AvhggvWvGIcGqo8K@NgL)umpe(58?u&R(+8WPvnH2Vh#A_K$ooY!2#n{5QHX^4 zZ*fd7G;8`fd{4^K(QE=A1&ho$(mv;3(_Q3ZZ(cISrx5luZ(ER5^9G6wLtc%PomqFZ zXEPq-%*J6)h|FocWXlyZZa z7*F4zSXX#v=0?awg&1-Rd!&ENIkmXCUPartEBmTZiZ$N=TKK(Yu)})Y@tPmTH_%>W z|6$-ecSHF7#=(LFxjF|qWqucfMNBjSKx@e>cH#hv!|>TLgeil{(E8Ns{dO_kZ7q4* zZvF+1h51PF2Kzm_lv`ZAjpOCB&F?Q_MF`5LktX63TV46GpPPrRCfd(svF@n3dk2~+ z52ei@*!XWWLTevzzRdIHD<4l}mBDZ;wnRHso{=V(bMxm=(v`fv&C>~;1oM;{C<#os zrja9|-_xrciRX<;u}+578ydn#&TkV51_$)=>y<3J{#DyNtG_!CB$)2->GV+t)(S|g zR|l5~s@nG(ms_wExZSg~7f>;%)$-R3JdkwHP@u9Ei<^1qXH+j+@S(!?$d_t`xcFkg zgGw%2v<+eVAOu?+ffJjW-DVv#2+q2t4YzzyKa&(<)c*U)JufC8m>~jrME4Aj} zyw&sW^9Jjkg~R3Q9zCcQ`y%a zNBTA=imo*~?qZzG5);H;woE1;YAiNk#hW&THBirDYc8(&e!(5JCeTs-jCCEDMg_SN z>DHQUgapTvpZIl#2s&Mx|3O{1+MHV+sX|<^dwwp-KArb!RcDOkCW^|UNYGSzp65tn zYnwAGinE^P3WDaAqCkf0@VgP2%(502rwwY4{k0nrEJzKVd@ql%I`;0Gp#0=v9oRsqqgv^JEdWDrslFM4#yxHv_TS9<`XU!IG^hqTACZ{v=24 z&$Qo)7j=pQ9?_m0X?dRBoeM5)Ii!6ok;}JUT^`eHt``PF_YHsPglQNFiSBQqXWpAmDHa0020{e?g3d=6Q9%7LWvRfE_p!fh(Yi0+#_PFt_>@ z;Q?uo5e2M4MCKPu1Q{D}*n$WVRN}z_A~;_JN*DowL&#E$5Q%%|od9jX5iD~DTmcWz zh=3=+4X6SxfCE_W0y1`B3vS>Rpa&>{{wMY`R|0GFK(7KSeStqfl`CKm##UhL@T(&? zp!=(<7nuEAggcH58~kTiNJK6f)(S$+PzV6J+J70y|9=NhKIq{Pz-l(pGkmKrB%WEU1+@iHl92Mo2QMOizKtwZRxpw(PvboFa2drd8# zA=Frw_Ce|1%dz6d6FVNMe^mRQ!8@J>(FO5XYYiczrt9=fK;Fx}h={+n#uZlkwN z<_)!}HVaa(&ni7-??r18MyR?D;-AIX&fA&ue73XjJ^3R`3st^G6xdY~1r9=IsAFz5J9E9Rb72C2GRiqm$wwU$xq z(RQy$s^{^2d`@Lrm0I3;phT>D0u&KF>(T>*(y_kovYT--w&yyV#$hyL7pQBT9=jY|Yj=YbG#;LHwOKrnJ4pil3cTX`!g zHYhsFXntzGk++Jt^eC(W8S>}TlfH2&I;XWDH)1&(A=E<7Q{y^g?q6`?M4M%5>M?PG z+>DI0qF`Oo*&T|uHS8Jj18q#k{*t*nW7lVZD+Y{Pw0DuMtKMv zG@$J`x;?~3xe{VHl>&wpIHzShtRcx&@0wJkS+r*Zw?^Xh+5N7%3)FuwdI=Q_hgxNX z?i(G~Z)E9wkEwiQ$o*oTRXUzj*!&|$!8h=6qWjE#Svj>0t2iT573nVfW&%&})4-bF z3q<&-0=l@Z19sNTk}ftHBK<$53a6^{#&p%CpJeVxa`EL>Xinjm& literal 0 HcmV?d00001 diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 3c617e0f507d..a034d3714dca 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -111,6 +111,9 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase [Parameter(Mandatory = false, ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")] + [Parameter(Mandatory = false, + ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet, + HelpMessage = "Specifies the password for the certificate collection and private key to import.")] public SecureString Password { get; set; } /// @@ -119,6 +122,12 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase [Parameter(Mandatory = false, ParameterSetName = ImportCertificateFromFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + [Parameter(Mandatory = false, + ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, + HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + [Parameter(Mandatory = false, + ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet, + HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] public string PolicyPath { get; set; } /// @@ -166,28 +175,22 @@ public override void ExecuteCmdlet() ValidateParameters(); PSKeyVaultCertificate certBundle = null; + PSKeyVaultCertificatePolicy policy = null; + if (!string.IsNullOrEmpty(PolicyPath)) + { + policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); + } switch (ParameterSetName) { case ImportCertificateFromFileParameterSet: - PSKeyVaultCertificatePolicy policy = null; - if (!string.IsNullOrEmpty(PolicyPath)) - { - policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); - } + // 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); - if (policy != null) - { - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, policy); - } - else - { - 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 { @@ -220,12 +223,12 @@ public override void ExecuteCmdlet() break; case ImportWithPrivateKeyFromCollectionParameterSet: - certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateCollection, Tag?.ConvertToDictionary()); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: policy); break; case ImportWithPrivateKeyFromStringParameterSet: - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: policy); break; } @@ -253,31 +256,5 @@ internal X509Certificate2Collection InitializeCertificateCollection() return certificateCollection; } - /* - private PSKeyVaultCertificatePolicy GetPolicyFromFile(string filePath) - { - PSKeyVaultCertificatePolicy policy; - /* new CertificatePolicy() - { - ContentType = contentType - } - - if (".json".Equals(Path.GetExtension(filePath), StringComparison.OrdinalIgnoreCase)) - { - using (StreamReader r = new StreamReader(filePath)) - { - string jsonContent = r.ReadToEnd(); - // dynamic array = JsonConvert.DeserializeObject(jsonContent); - var policyJson = JsonConvert.DeserializeObject(jsonContent); //(PSKeyVaultCertificatePolicy) - } - } - else - { - throw new AzPSArgumentException(string.Format(Resources.UnsupportedFileFormat, this.PolicyPath), nameof(PolicyPath)); - } - return policy; - }*/ - - } } diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs index f8ec3f0451c9..6e13e202d3c3 100644 --- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs @@ -168,7 +168,7 @@ public interface IKeyVaultDataServiceClient PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null); PSDeletedKeyVaultCertificate DeleteCertificate(string vaultName, string certName); diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs index d6530c509b2e..77fa0982078c 100644 --- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs @@ -855,7 +855,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return new PSKeyVaultCertificate(certBundle); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { if (string.IsNullOrEmpty(vaultName)) throw new ArgumentNullException(nameof(vaultName)); diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs index 467570c99782..8a2f495572d0 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs @@ -30,6 +30,7 @@ public class PSKeyVaultCertificate : PSKeyVaultCertificateIdentityItem public string SecretId { get; internal set; } public string Thumbprint { get; set; } + public PSKeyVaultCertificatePolicy Policy { get; set; } public string RecoveryLevel { get; private set; } internal PSKeyVaultCertificate(CertificateBundle certificateBundle, VaultUriHelper vaultUriHelper) @@ -156,6 +157,7 @@ internal PSKeyVaultCertificate(KeyVaultCertificateWithPolicy keyVaultCertificate KeyId = keyVaultCertificate.KeyId?.ToString(); SecretId = keyVaultCertificate.SecretId?.ToString(); + Policy = PSKeyVaultCertificatePolicy.FromTrack2CertificatePolicy(keyVaultCertificate.Policy); if (keyVaultCertificate.Properties != null) { diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 7ef063cc0936..c9be0171e0f4 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -12,7 +12,6 @@ // limitations under the License. // ---------------------------------------------------------------------------------- -// using Azure.Security.KeyVault.Keys; using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; @@ -336,7 +335,6 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); subjectAlternativeNames = SetSubjectAlternativeNames(subjectAlternativeNames); certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, subjectAlternativeNames); - // SetSubjectAlternativeNames(certificatePolicy); } else certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); @@ -459,6 +457,39 @@ internal static PSKeyVaultCertificatePolicy FromCertificatePolicy(CertificatePol Updated = certificatePolicy.Attributes == null ? null : certificatePolicy.Attributes.Updated, }; } + + internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2Certificate.CertificatePolicy certificatePolicy) + { + return new PSKeyVaultCertificatePolicy + { + SecretContentType = certificatePolicy.ContentType.Equals(null) ? null : certificatePolicy.ContentType.ToString(), + Kty = certificatePolicy.KeyType == null ? null : certificatePolicy.KeyType.ToString(), + KeySize = certificatePolicy.KeySize == null ? null : certificatePolicy.KeySize, + Curve = certificatePolicy.KeyCurveName == null ? null : certificatePolicy.KeyCurveName.ToString(), + Exportable = certificatePolicy.Exportable == null ? null : certificatePolicy.Exportable, + ReuseKeyOnRenewal = certificatePolicy.ReuseKey == null ? null : certificatePolicy.ReuseKey, + SubjectName = certificatePolicy.Subject == null ? null : certificatePolicy.Subject, + DnsNames = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.DnsNames == null ? + null : new List(certificatePolicy.SubjectAlternativeNames.DnsNames), + Emails = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.Emails == null ? + null : new List(certificatePolicy.SubjectAlternativeNames.Emails), + UserPrincipalNames = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.UserPrincipalNames == null ? + null : new List(certificatePolicy.SubjectAlternativeNames.UserPrincipalNames), + KeyUsage = certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage.Select(keyUsage => keyUsage.ToString()).ToList(), + Ekus = certificatePolicy.EnhancedKeyUsage == null ? null : new List(certificatePolicy.EnhancedKeyUsage), + ValidityInMonths = certificatePolicy.ValidityInMonths == null ? null : certificatePolicy.ValidityInMonths, + CertificateTransparency = certificatePolicy.CertificateTransparency == null ? null : certificatePolicy.CertificateTransparency, + IssuerName = certificatePolicy.IssuerName == null ? null : certificatePolicy.IssuerName, + CertificateType = certificatePolicy.CertificateType == null ? null : certificatePolicy.CertificateType, + RenewAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions), + RenewAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions), + EmailAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions), + EmailAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions), + Enabled = certificatePolicy.Enabled == null ? null : certificatePolicy.Enabled, + Created = certificatePolicy.CreatedOn.HasValue ? certificatePolicy.CreatedOn.Value.DateTime : (DateTime?)null, + Updated = certificatePolicy.UpdatedOn.HasValue ? certificatePolicy.UpdatedOn.Value.DateTime : (DateTime?)null, + }; + } private void ReadKeyProperties(JsonElement json) { foreach (JsonProperty item in json.EnumerateObject()) @@ -694,6 +725,21 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) return intValueGetter(lifetimeAction.Trigger); } + private static int? FindIntValueForAutoRenewAction(IList lifetimeActions) + { + var lifetimeAction = + lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.AutoRenew.ToString(), true) + && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); + if (lifetimeAction == null) + return null; + if (lifetimeAction.DaysBeforeExpiry.HasValue) + return lifetimeAction.DaysBeforeExpiry; + if (lifetimeAction.LifetimePercentage.HasValue) + return lifetimeAction.LifetimePercentage; + else + return null; + } + private static int? FindIntValueForEmailAction(IEnumerable lifetimeActions, Func intValueGetter) { var lifetimeAction = lifetimeActions.FirstOrDefault(x => x.Action.ActionType.HasValue && 0 == string.Compare(x.Action.ActionType.Value.ToString(), ActionType.EmailContacts.ToString(), true) && intValueGetter(x.Trigger).HasValue); @@ -706,6 +752,21 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) return intValueGetter(lifetimeAction.Trigger); } + private static int? FindIntValueForEmailAction(IList lifetimeActions) + { + var lifetimeAction = + lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.EmailContacts.ToString(), true) + && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); + if (lifetimeAction == null) + return null; + if (lifetimeAction.DaysBeforeExpiry.HasValue) + return lifetimeAction.DaysBeforeExpiry; + if (lifetimeAction.LifetimePercentage.HasValue) + return lifetimeAction.LifetimePercentage; + else + return null; + } + private void ValidateInternal( IList dnsNames, IList ekus, diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index f864247f3fa0..c53de59fb9f4 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -2,6 +2,7 @@ using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Commands.KeyVault.Models; using Microsoft.Azure.KeyVault.Models; +using Org.BouncyCastle.Asn1.X509; using System; using System.Collections; using System.Collections.Generic; @@ -250,9 +251,10 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType, certPolicy); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - throw new NotImplementedException(); + byte[] certificate = certificateCollection.Export(X509ContentType.Pfx, certPassword?.ConvertToString()); + return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType, certPolicy); } public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, X509Certificate2Collection certs, IDictionary tags) From fbf60aaea5da430cfac3f4bbcac8708d098b2b88 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sat, 8 Apr 2023 00:55:55 +0800 Subject: [PATCH 08/21] Update ChangeLog --- src/KeyVault/KeyVault/ChangeLog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/KeyVault/KeyVault/ChangeLog.md b/src/KeyVault/KeyVault/ChangeLog.md index 00d175567d07..4d7496686091 100644 --- a/src/KeyVault/KeyVault/ChangeLog.md +++ b/src/KeyVault/KeyVault/ChangeLog.md @@ -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. From 794efa52daca509feb3caa01219bc6b3cab83227 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sat, 8 Apr 2023 01:01:57 +0800 Subject: [PATCH 09/21] Remove unnecessary lines --- .../Commands/Certificate/ImportAzureKeyVaultCertificate.cs | 4 +--- src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs | 6 ------ .../Track2Models/Track2KeyVaultDataServiceClient.cs | 2 -- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index a034d3714dca..ee5a6fb2867a 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -55,8 +55,6 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// /// VaultName - /// - /// /// [Parameter(Mandatory = true, Position = 0, @@ -159,7 +157,7 @@ protected override void BeginProcessing() private void ValidateParameters() { // Verify the FileNotFound whether exists - if (this.IsParameterBound(c => c.FilePath) ) + if (this.IsParameterBound(c => c.FilePath)) { if (!File.Exists(FilePath)) { diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index c9be0171e0f4..5ea253a5327a 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -15,21 +15,15 @@ using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; -using Newtonsoft.Json; -using Org.BouncyCastle.Bcpg; using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Management.Automation; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.Json; using Track2Certificate = Azure.Security.KeyVault.Certificates; -// Track2Certificate.CertificatePolicy - namespace Microsoft.Azure.Commands.KeyVault.Models { public class PSKeyVaultCertificatePolicy diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index c53de59fb9f4..e2490cf7c2c5 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -2,7 +2,6 @@ using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Commands.KeyVault.Models; using Microsoft.Azure.KeyVault.Models; -using Org.BouncyCastle.Asn1.X509; using System; using System.Collections; using System.Collections.Generic; @@ -11,7 +10,6 @@ using System.Text; using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties; -//using CertificatePolicy = Azure.Security.KeyVault.Certificates.CertificatePolicy; namespace Microsoft.Azure.Commands.KeyVault.Track2Models { From 3416bcd24f953cd0e52a5655f9a93c32009e11c0 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Wed, 19 Apr 2023 15:03:09 +0800 Subject: [PATCH 10/21] Resolve comments --- .../PesterTests/Certificate.Tests.ps1 | 14 ++- .../ImportAzureKeyVaultCertificate.cs | 89 +++++++++++++++---- .../Models/PSKeyVaultCertificatePolicy.cs | 42 +++++---- .../Track2Models/Track2VaultClient.cs | 15 ++-- 4 files changed, 113 insertions(+), 47 deletions(-) diff --git a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 index 50b131f0a918..f45785d5b93a 100644 --- a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 +++ b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 @@ -4,7 +4,15 @@ BeforeAll { } Describe "Import Certificate with policy" { - It "ImportCertificateFromFileParameterSet" { + It "ImportCertificateFromFileWithPolicyParameterSet" { + $certName = Get-CertificateName + $certFilePath = "..\Resources\importCert00.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\importCert00.pfx" $policyPath = "..\Resources\certPolicy.json" @@ -12,7 +20,7 @@ Describe "Import Certificate with policy" { $cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -FilePath $certFilePath -PolicyPath $policyPath $cert.Policy.SecretContentType | Should -Be "application/x-pkcs12" } - It "ImportWithPrivateKeyFromStringParameterSet" { + It "ImportWithPrivateKeyFromStringWithPolicyFileParameterSet" { $certName = Get-CertificateName $certFilePath = "..\Resources\importCert00.pfx" $policyPath = "..\Resources\certPolicy.json" @@ -21,7 +29,7 @@ Describe "Import Certificate with policy" { $cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -CertificateString $Base64StringCertificate -PolicyPath $policyPath $cert.Policy.SecretContentType | Should -Be "application/x-pkcs12" } - It "ImportWithPrivateKeyFromCollectionParameterSet" { + It "ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet" { $certName = Get-CertificateName $certFilePath = "..\Resources\importCert00.pfx" $policyPath = "..\Resources\certPolicy.json" diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index ee5a6fb2867a..5e9e35f7ae66 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -39,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). /// - [Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileParameterSet)] + [Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileWithPolicyParameterSet)] [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"; + 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 @@ -79,7 +82,10 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [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; } @@ -87,7 +93,10 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Base64 encoded representation of the certificate object to import /// [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; } @@ -95,7 +104,10 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Specifies type of the certificate to be imported. /// [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; @@ -104,13 +116,22 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Password /// [Parameter(Mandatory = false, - ParameterSetName = ImportCertificateFromFileParameterSet, + ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet, HelpMessage = "Specifies the password for the certificate and private key file to import.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, + ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet, HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet, + 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 = 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.")] public SecureString Password { get; set; } @@ -118,23 +139,42 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [Parameter(Mandatory = false, - ParameterSetName = ImportCertificateFromFileParameterSet, + ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, + ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet, + ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] public string PolicyPath { get; set; } + /// + /// File Path + /// + [Parameter(Mandatory = false, + ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet, + HelpMessage = "Specifies the certificate policy to import to key vault.")] + [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; } + /// /// Certificate Collection /// [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; } @@ -151,6 +191,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase protected override void BeginProcessing() { FilePath = this.TryResolvePath(FilePath); + PolicyPath = this.TryResolvePath(PolicyPath); base.BeginProcessing(); } @@ -164,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)) + { + throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.PolicyPath), nameof(PolicyPath)); + } + } } public override void ExecuteCmdlet() @@ -178,10 +226,15 @@ public override void ExecuteCmdlet() { policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); } + 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 @@ -220,12 +273,14 @@ public override void ExecuteCmdlet() } break; - case ImportWithPrivateKeyFromCollectionParameterSet: + case ImportWithPrivateKeyFromCollectionWithPolicyParameterSet: + case ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet: certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: policy); break; - case ImportWithPrivateKeyFromStringParameterSet: + case ImportWithPrivateKeyFromStringWithPolicyParameterSet: + case ImportWithPrivateKeyFromStringWithPolicyFileParameterSet: certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: policy); break; diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 5ea253a5327a..bc7ace84e29d 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -304,7 +304,6 @@ internal Track2Certificate.SubjectAlternativeNames SetSubjectAlternativeNames(Tr if (UserPrincipalNames != null && UserPrincipalNames.Any()) foreach (var principalName in UserPrincipalNames) SubjectAlternativeNames.UserPrincipalNames.Add(principalName); - // SubjectAlternativeNames.UserPrincipalNames.Add(principalName); return SubjectAlternativeNames; } @@ -364,8 +363,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() certificatePolicy.Enabled = Enabled; if (ValidityInMonths.HasValue) - if (ValidityInMonths.HasValue) - certificatePolicy.ValidityInMonths = ValidityInMonths.Value; + certificatePolicy.ValidityInMonths = ValidityInMonths.Value; if (RenewAtNumberOfDaysBeforeExpiry.HasValue || RenewAtPercentageLifetime.HasValue || @@ -383,7 +381,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (RenewAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("AutoRenew")) + new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.AutoRenew) { DaysBeforeExpiry = RenewAtNumberOfDaysBeforeExpiry } @@ -393,7 +391,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (RenewAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("AutoRenew")) + new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.AutoRenew) { LifetimePercentage = RenewAtPercentageLifetime } @@ -402,7 +400,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (EmailAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("EmailContacts")) + new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.EmailContacts) { DaysBeforeExpiry = EmailAtNumberOfDaysBeforeExpiry } @@ -412,7 +410,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (EmailAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(new Track2Certificate.CertificatePolicyAction("EmailContacts")) + new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.EmailContacts) { LifetimePercentage = EmailAtPercentageLifetime } @@ -456,13 +454,13 @@ internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2Ce { return new PSKeyVaultCertificatePolicy { - SecretContentType = certificatePolicy.ContentType.Equals(null) ? null : certificatePolicy.ContentType.ToString(), - Kty = certificatePolicy.KeyType == null ? null : certificatePolicy.KeyType.ToString(), - KeySize = certificatePolicy.KeySize == null ? null : certificatePolicy.KeySize, - Curve = certificatePolicy.KeyCurveName == null ? null : certificatePolicy.KeyCurveName.ToString(), - Exportable = certificatePolicy.Exportable == null ? null : certificatePolicy.Exportable, - ReuseKeyOnRenewal = certificatePolicy.ReuseKey == null ? null : certificatePolicy.ReuseKey, - SubjectName = certificatePolicy.Subject == null ? null : certificatePolicy.Subject, + SecretContentType = certificatePolicy.ContentType?.ToString(), + Kty = certificatePolicy.KeyType?.ToString(), + KeySize = certificatePolicy.KeySize, + Curve = certificatePolicy.KeyCurveName?.ToString(), + Exportable = certificatePolicy.Exportable, + ReuseKeyOnRenewal = certificatePolicy.ReuseKey, + SubjectName = certificatePolicy.Subject, DnsNames = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.DnsNames == null ? null : new List(certificatePolicy.SubjectAlternativeNames.DnsNames), Emails = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.Emails == null ? @@ -471,15 +469,15 @@ internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2Ce null : new List(certificatePolicy.SubjectAlternativeNames.UserPrincipalNames), KeyUsage = certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage.Select(keyUsage => keyUsage.ToString()).ToList(), Ekus = certificatePolicy.EnhancedKeyUsage == null ? null : new List(certificatePolicy.EnhancedKeyUsage), - ValidityInMonths = certificatePolicy.ValidityInMonths == null ? null : certificatePolicy.ValidityInMonths, - CertificateTransparency = certificatePolicy.CertificateTransparency == null ? null : certificatePolicy.CertificateTransparency, - IssuerName = certificatePolicy.IssuerName == null ? null : certificatePolicy.IssuerName, - CertificateType = certificatePolicy.CertificateType == null ? null : certificatePolicy.CertificateType, + ValidityInMonths = certificatePolicy.ValidityInMonths, + CertificateTransparency = certificatePolicy.CertificateTransparency, + IssuerName = certificatePolicy.IssuerName, + CertificateType = certificatePolicy.CertificateType, RenewAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions), RenewAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForAutoRenewAction(certificatePolicy.LifetimeActions), EmailAtNumberOfDaysBeforeExpiry = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions), EmailAtPercentageLifetime = certificatePolicy.LifetimeActions == null ? null : FindIntValueForEmailAction(certificatePolicy.LifetimeActions), - Enabled = certificatePolicy.Enabled == null ? null : certificatePolicy.Enabled, + Enabled = certificatePolicy.Enabled, Created = certificatePolicy.CreatedOn.HasValue ? certificatePolicy.CreatedOn.Value.DateTime : (DateTime?)null, Updated = certificatePolicy.UpdatedOn.HasValue ? certificatePolicy.UpdatedOn.Value.DateTime : (DateTime?)null, }; @@ -687,14 +685,14 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) } } - if (actionType == "AutoRenew") + if (actionType == ActionType.AutoRenew.ToString()) { if (triggerType == "days_before_expiry") policy.RenewAtNumberOfDaysBeforeExpiry = triggerValue; else if (triggerType == "lifetime_percentage") policy.RenewAtPercentageLifetime = triggerValue; } - else if (actionType == "EmailContacts") + else if (actionType == ActionType.EmailContacts.ToString()) { if (triggerType == "days_before_expiry") policy.EmailAtNumberOfDaysBeforeExpiry = triggerValue; @@ -749,7 +747,7 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) private static int? FindIntValueForEmailAction(IList lifetimeActions) { var lifetimeAction = - lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.EmailContacts.ToString(), true) + lifetimeActions.FirstOrDefault(x => !string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.EmailContacts.ToString(), true) && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); if (lifetimeAction == null) return null; diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index bd09d082a3b7..04d7498071a4 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -9,6 +9,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Security; namespace Microsoft.Azure.Commands.KeyVault.Track2Models @@ -227,16 +228,20 @@ internal PSKeyVaultCertificate ImportCertificate(string vaultName, string certNa private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { CertificatePolicy certificatePolicy = null; - if (certPolicy == null) + if (certPolicy != null) { - certificatePolicy = new CertificatePolicy() + certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); + if ( certificatePolicy.ContentType != contentType ) { - ContentType = contentType - }; + throw new Exception($"{contentType} conflicts with the ContentType stated as {certificatePolicy.ContentType} in Certificate Policy."); + } } else { - certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); + certificatePolicy = new CertificatePolicy() + { + ContentType = contentType + }; } var options = new ImportCertificateOptions(certName, certificate) { From c7b2dbf12141150d627bc03b8d363d85943d135e Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Wed, 19 Apr 2023 15:34:55 +0800 Subject: [PATCH 11/21] Update pfx file name --- .../KeyVault.Test/PesterTests/Certificate.Tests.ps1 | 8 ++++---- .../{importCert00.pfx => importCertWithPolicy.pfx} | Bin 2 files changed, 4 insertions(+), 4 deletions(-) rename src/KeyVault/KeyVault.Test/Resources/{importCert00.pfx => importCertWithPolicy.pfx} (100%) diff --git a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 index f45785d5b93a..c69283932918 100644 --- a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 +++ b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 @@ -6,7 +6,7 @@ BeforeAll { Describe "Import Certificate with policy" { It "ImportCertificateFromFileWithPolicyParameterSet" { $certName = Get-CertificateName - $certFilePath = "..\Resources\importCert00.pfx" + $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 @@ -14,7 +14,7 @@ Describe "Import Certificate with policy" { } It "ImportCertificateFromFileWithPolicyFileParameterSet" { $certName = Get-CertificateName - $certFilePath = "..\Resources\importCert00.pfx" + $certFilePath = "..\Resources\importCertWithPolicy.pfx" $policyPath = "..\Resources\certPolicy.json" $cert = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -FilePath $certFilePath -PolicyPath $policyPath @@ -22,7 +22,7 @@ Describe "Import Certificate with policy" { } It "ImportWithPrivateKeyFromStringWithPolicyFileParameterSet" { $certName = Get-CertificateName - $certFilePath = "..\Resources\importCert00.pfx" + $certFilePath = "..\Resources\importCertWithPolicy.pfx" $policyPath = "..\Resources\certPolicy.json" $Base64StringCertificate = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($certFilePath)) @@ -31,7 +31,7 @@ Describe "Import Certificate with policy" { } It "ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet" { $certName = Get-CertificateName - $certFilePath = "..\Resources\importCert00.pfx" + $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) diff --git a/src/KeyVault/KeyVault.Test/Resources/importCert00.pfx b/src/KeyVault/KeyVault.Test/Resources/importCertWithPolicy.pfx similarity index 100% rename from src/KeyVault/KeyVault.Test/Resources/importCert00.pfx rename to src/KeyVault/KeyVault.Test/Resources/importCertWithPolicy.pfx From cf688e8977d4b7b931850f05b78e3de20a9f5b2c Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Fri, 21 Apr 2023 15:32:19 +0800 Subject: [PATCH 12/21] Update according to comments --- .../ImportAzureKeyVaultCertificate.cs | 61 +++++++++++-------- .../Models/PSKeyVaultCertificatePolicy.cs | 9 +-- .../Track2Models/Track2VaultClient.cs | 3 +- 3 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 5e9e35f7ae66..855b31e233d8 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -29,6 +29,7 @@ using Newtonsoft.Json; using System.Text.Json; +using Azure.Security.KeyVault.Certificates; namespace Microsoft.Azure.Commands.KeyVault { @@ -39,15 +40,15 @@ namespace Microsoft.Azure.Commands.KeyVault /// importing an existing certificate package file that contains both the /// certificate and private key (example: PFX or P12 files). /// - [Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileWithPolicyParameterSet)] + [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 ImportCertificateFromFileWithPolicyParameterSet = "ImportCertificateFromFileWithPolicy"; - private const string ImportWithPrivateKeyFromCollectionWithPolicyParameterSet = "ImportWithPrivateKeyFromCollectionWithPolicy"; - private const string ImportWithPrivateKeyFromStringWithPolicyParameterSet = "ImportWithPrivateKeyFromStringWithPolicy"; + 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"; @@ -82,7 +83,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [Parameter(Mandatory = true, - ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet, + ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate to add to key vault.")] [Parameter(Mandatory = true, ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, @@ -93,7 +94,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Base64 encoded representation of the certificate object to import /// [Parameter(Mandatory = true, - ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet, + 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, @@ -104,7 +105,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Specifies type of the certificate to be imported. /// [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet, + 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, @@ -116,13 +117,13 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Password /// [Parameter(Mandatory = false, - ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet, + ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet, HelpMessage = "Specifies the password for the certificate and private key file to import.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet, + ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet, HelpMessage = "Specifies the password for the certificate and private key base64 encoded string to import.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet, + ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet, HelpMessage = "Specifies the password for the certificate collection and private key to import.")] [Parameter(Mandatory = false, ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, @@ -140,28 +141,31 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// [Parameter(Mandatory = false, ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, - HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")] [Parameter(Mandatory = false, ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet, - HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")] [Parameter(Mandatory = false, ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet, - HelpMessage = "Specifies the path to the file that contains the certificate policy to import to key vault.")] + HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition. ")] public string PolicyPath { get; set; } /// /// File Path /// [Parameter(Mandatory = false, - ParameterSetName = ImportCertificateFromFileWithPolicyParameterSet, - HelpMessage = "Specifies the certificate policy to import to key vault.")] + ValueFromPipeline = true, + ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet, + HelpMessage = "An in-memory object to specify management policy for the certificate.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyParameterSet, - HelpMessage = "Specifies the certificate policy to import to key vault.")] + ValueFromPipeline = true, + ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet, + HelpMessage = "An in-memory object to specify management policy for the certificate.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet, - HelpMessage = "Specifies the certificate policy to import to key vault.")] - public PSKeyVaultCertificatePolicy Policy { get; set; } + ValueFromPipeline = true, + ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet, + HelpMessage = "An in-memory object to specify management policy for the certificate.")] + public PSKeyVaultCertificatePolicy PolicyObject { get; set; } /// /// Certificate Collection @@ -169,7 +173,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase [Parameter(Mandatory = true, Position = 2, ValueFromPipeline = true, - ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyParameterSet, + ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet, HelpMessage = "Specifies the certificate collection to add to key vault.")] [Parameter(Mandatory = true, Position = 2, @@ -226,14 +230,19 @@ public override void ExecuteCmdlet() { policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); } - else if ( Policy != null) + else if ( PolicyObject != null) { - policy = Policy; + policy = PolicyObject; + } + + if ( policy != null && policy.SecretContentType != ContentType) + { + throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {policy.SecretContentType} in Certificate Policy.", ContentType); } switch (ParameterSetName) { - case ImportCertificateFromFileWithPolicyParameterSet: + case ImportCertificateFromFileWithPolicyObjectParameterSet: case ImportCertificateFromFileWithPolicyFileParameterSet: // Pem file can't be handled by X509Certificate2Collection in dotnet standard @@ -273,13 +282,13 @@ public override void ExecuteCmdlet() } break; - case ImportWithPrivateKeyFromCollectionWithPolicyParameterSet: + case ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet: case ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet: certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: policy); break; - case ImportWithPrivateKeyFromStringWithPolicyParameterSet: + case ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet: case ImportWithPrivateKeyFromStringWithPolicyFileParameterSet: certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: policy); diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index bc7ace84e29d..7c040ce23e4d 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -461,12 +461,9 @@ internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2Ce Exportable = certificatePolicy.Exportable, ReuseKeyOnRenewal = certificatePolicy.ReuseKey, SubjectName = certificatePolicy.Subject, - DnsNames = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.DnsNames == null ? - null : new List(certificatePolicy.SubjectAlternativeNames.DnsNames), - Emails = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.Emails == null ? - null : new List(certificatePolicy.SubjectAlternativeNames.Emails), - UserPrincipalNames = certificatePolicy.SubjectAlternativeNames == null || certificatePolicy.SubjectAlternativeNames.UserPrincipalNames == null ? - null : new List(certificatePolicy.SubjectAlternativeNames.UserPrincipalNames), + DnsNames = certificatePolicy.SubjectAlternativeNames?.DnsNames?.ToList(), + Emails = certificatePolicy.SubjectAlternativeNames?.Emails?.ToList(), + UserPrincipalNames = certificatePolicy.SubjectAlternativeNames?.UserPrincipalNames?.ToList(), KeyUsage = certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage == null ? null : certificatePolicy.KeyUsage.Select(keyUsage => keyUsage.ToString()).ToList(), Ekus = certificatePolicy.EnhancedKeyUsage == null ? null : new List(certificatePolicy.EnhancedKeyUsage), ValidityInMonths = certificatePolicy.ValidityInMonths, diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index 04d7498071a4..370c781b90b4 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -3,6 +3,7 @@ using Azure.Security.KeyVault.Keys.Cryptography; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; +using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Models; using Microsoft.WindowsAzure.Commands.Utilities.Common; @@ -233,7 +234,7 @@ private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, st certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); if ( certificatePolicy.ContentType != contentType ) { - throw new Exception($"{contentType} conflicts with the ContentType stated as {certificatePolicy.ContentType} in Certificate Policy."); + throw new AzPSArgumentException($"{contentType} indicated by certificate file conflicts with the ContentType stated as {certificatePolicy.ContentType} in Certificate Policy.", contentType); } } else From 1b94f0d6d9af6399d0eccafcc9b0862d9e256796 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Fri, 21 Apr 2023 15:34:01 +0800 Subject: [PATCH 13/21] Remove unnecessary dependecies --- .../Commands/Certificate/ImportAzureKeyVaultCertificate.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 855b31e233d8..abf3e530b9bd 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -27,10 +27,6 @@ using Microsoft.Azure.Commands.Common.Exceptions; using Microsoft.Azure.Commands.KeyVault.Properties; -using Newtonsoft.Json; -using System.Text.Json; -using Azure.Security.KeyVault.Certificates; - namespace Microsoft.Azure.Commands.KeyVault { /// From c95e61e75316cca0cd6952587fe2c167c0580a0f Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Fri, 21 Apr 2023 16:10:37 +0800 Subject: [PATCH 14/21] Modify the actions number --- src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 7c040ce23e4d..a1530751c403 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -659,12 +659,12 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) foreach (JsonElement item2 in item.Value.EnumerateArray()) { + if (item2.EnumerateObject().Count() > 1) + throw new AzPSArgumentException(string.Format("Json file property {0} exceed expected number 1.", item.Name), nameof(item.Name)); foreach (JsonProperty item3 in item2.EnumerateObject()) { if (item3.Name == "trigger") { - if (item3.Value.EnumerateObject().Count() > 1) - throw new AzPSArgumentException(string.Format("Json file property {0} exceed expected number.", item3.Name), nameof(item.Name)); foreach (JsonProperty item4 in item3.Value.EnumerateObject()) { triggerType = item4.Name; From 734b3791368672118bee9c6011ca39b7533aa923 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sun, 23 Apr 2023 14:36:09 +0800 Subject: [PATCH 15/21] Update the contentType validation --- .../ImportAzureKeyVaultCertificate.cs | 29 +++++-------- .../Models/PSKeyVaultCertificatePolicy.cs | 42 +++++++++---------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index abf3e530b9bd..6f97fe52ce3a 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -211,6 +211,12 @@ private void ValidateParameters() { throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.PolicyPath), nameof(PolicyPath)); } + PolicyObject = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); + } + + if (PolicyObject != null && this.IsParameterBound(c => c.ContentType) && PolicyObject.SecretContentType != ContentType) + { + throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {PolicyObject.SecretContentType} in Certificate Policy.", ContentType); } } @@ -221,32 +227,17 @@ public override void ExecuteCmdlet() ValidateParameters(); PSKeyVaultCertificate certBundle = null; - PSKeyVaultCertificatePolicy policy = null; - if (!string.IsNullOrEmpty(PolicyPath)) - { - policy = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); - } - else if ( PolicyObject != null) - { - policy = PolicyObject; - } - - if ( policy != null && policy.SecretContentType != ContentType) - { - throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {policy.SecretContentType} in Certificate Policy.", ContentType); - } switch (ParameterSetName) { 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, certPolicy: policy); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType, certPolicy: PolicyObject); } else { @@ -265,7 +256,7 @@ public override void ExecuteCmdlet() { byte[] base64Bytes = userProvidedCertColl.Export(X509ContentType.Pfx, Password?.ConvertToString()); - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary(), certPolicy:policy); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary(), certPolicy: PolicyObject); } else { @@ -280,13 +271,13 @@ public override void ExecuteCmdlet() case ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet: case ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet: - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: policy); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: PolicyObject); break; case ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet: case ImportWithPrivateKeyFromStringWithPolicyFileParameterSet: - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: policy); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: PolicyObject); break; } diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index a1530751c403..be982699ab9b 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -22,7 +22,7 @@ using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text.Json; -using Track2Certificate = Azure.Security.KeyVault.Certificates; +using Track2CertificateSDK = Azure.Security.KeyVault.Certificates; namespace Microsoft.Azure.Commands.KeyVault.Models { @@ -293,7 +293,7 @@ internal bool HasSubjectAlternativeNames() return false; } - internal Track2Certificate.SubjectAlternativeNames SetSubjectAlternativeNames(Track2Certificate.SubjectAlternativeNames SubjectAlternativeNames) + internal Track2CertificateSDK.SubjectAlternativeNames SetSubjectAlternativeNames(Track2CertificateSDK.SubjectAlternativeNames SubjectAlternativeNames) { if (DnsNames != null && DnsNames.Any()) foreach (var dnsName in DnsNames) @@ -307,33 +307,33 @@ internal Track2Certificate.SubjectAlternativeNames SetSubjectAlternativeNames(Tr return SubjectAlternativeNames; } - internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() + internal Track2CertificateSDK.CertificatePolicy ToTrack2CertificatePolicy() { - Track2Certificate.CertificatePolicy certificatePolicy; + Track2CertificateSDK.CertificatePolicy certificatePolicy; if (!string.IsNullOrWhiteSpace(IssuerName) || !string.IsNullOrWhiteSpace(SubjectName) || HasSubjectAlternativeNames()) { if (!string.IsNullOrWhiteSpace(SubjectName) && HasSubjectAlternativeNames()) { - Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + Track2CertificateSDK.SubjectAlternativeNames subjectAlternativeNames = new Track2CertificateSDK.SubjectAlternativeNames(); subjectAlternativeNames = SetSubjectAlternativeNames(subjectAlternativeNames); - certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName, subjectAlternativeNames); + certificatePolicy = new Track2CertificateSDK.CertificatePolicy(IssuerName, SubjectName, subjectAlternativeNames); } else if (!string.IsNullOrWhiteSpace(SubjectName)) { - certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); + certificatePolicy = new Track2CertificateSDK.CertificatePolicy(IssuerName, SubjectName); } else if (HasSubjectAlternativeNames()) { - Track2Certificate.SubjectAlternativeNames subjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + Track2CertificateSDK.SubjectAlternativeNames subjectAlternativeNames = new Track2CertificateSDK.SubjectAlternativeNames(); subjectAlternativeNames = SetSubjectAlternativeNames(subjectAlternativeNames); - certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, subjectAlternativeNames); + certificatePolicy = new Track2CertificateSDK.CertificatePolicy(IssuerName, subjectAlternativeNames); } else - certificatePolicy = new Track2Certificate.CertificatePolicy(IssuerName, SubjectName); + certificatePolicy = new Track2CertificateSDK.CertificatePolicy(IssuerName, SubjectName); }else { - certificatePolicy = new Track2Certificate.CertificatePolicy(); + certificatePolicy = new Track2CertificateSDK.CertificatePolicy(); } if (!string.IsNullOrWhiteSpace(SecretContentType)) certificatePolicy.ContentType = SecretContentType; @@ -381,7 +381,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (RenewAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.AutoRenew) + new Track2CertificateSDK.LifetimeAction(Track2CertificateSDK.CertificatePolicyAction.AutoRenew) { DaysBeforeExpiry = RenewAtNumberOfDaysBeforeExpiry } @@ -391,7 +391,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (RenewAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.AutoRenew) + new Track2CertificateSDK.LifetimeAction(Track2CertificateSDK.CertificatePolicyAction.AutoRenew) { LifetimePercentage = RenewAtPercentageLifetime } @@ -400,7 +400,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (EmailAtNumberOfDaysBeforeExpiry.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.EmailContacts) + new Track2CertificateSDK.LifetimeAction(Track2CertificateSDK.CertificatePolicyAction.EmailContacts) { DaysBeforeExpiry = EmailAtNumberOfDaysBeforeExpiry } @@ -410,7 +410,7 @@ internal Track2Certificate.CertificatePolicy ToTrack2CertificatePolicy() if (EmailAtPercentageLifetime.HasValue) { certificatePolicy.LifetimeActions.Add( - new Track2Certificate.LifetimeAction(Track2Certificate.CertificatePolicyAction.EmailContacts) + new Track2CertificateSDK.LifetimeAction(Track2CertificateSDK.CertificatePolicyAction.EmailContacts) { LifetimePercentage = EmailAtPercentageLifetime } @@ -450,7 +450,7 @@ internal static PSKeyVaultCertificatePolicy FromCertificatePolicy(CertificatePol }; } - internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2Certificate.CertificatePolicy certificatePolicy) + internal static PSKeyVaultCertificatePolicy FromTrack2CertificatePolicy(Track2CertificateSDK.CertificatePolicy certificatePolicy) { return new PSKeyVaultCertificatePolicy { @@ -552,7 +552,7 @@ private void ReadX509CertificateProperties(JsonElement json) SubjectName = item.Value.GetString(); break; case "sans": - var SubjectAlternativeNames = new Track2Certificate.SubjectAlternativeNames(); + var SubjectAlternativeNames = new Track2CertificateSDK.SubjectAlternativeNames(); ReadSubjectAlternativeNames(item); // SubjectAlternativeNames .ReadProperties(item.Value); break; @@ -714,10 +714,10 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) return intValueGetter(lifetimeAction.Trigger); } - private static int? FindIntValueForAutoRenewAction(IList lifetimeActions) + private static int? FindIntValueForAutoRenewAction(IList lifetimeActions) { var lifetimeAction = - lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.AutoRenew.ToString(), true) + lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2CertificateSDK.CertificatePolicyAction.AutoRenew.ToString(), true) && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); if (lifetimeAction == null) return null; @@ -741,10 +741,10 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) return intValueGetter(lifetimeAction.Trigger); } - private static int? FindIntValueForEmailAction(IList lifetimeActions) + private static int? FindIntValueForEmailAction(IList lifetimeActions) { var lifetimeAction = - lifetimeActions.FirstOrDefault(x => !string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2Certificate.CertificatePolicyAction.EmailContacts.ToString(), true) + lifetimeActions.FirstOrDefault(x => !string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2CertificateSDK.CertificatePolicyAction.EmailContacts.ToString(), true) && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); if (lifetimeAction == null) return null; From d51b4b43671674ccadb8628e4f0d7ef2692ae385 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Sun, 23 Apr 2023 19:04:42 +0800 Subject: [PATCH 16/21] solve paramaterset ambigious --- .../PesterTests/Certificate.Tests.ps1 | 2 +- .../ImportAzureKeyVaultCertificate.cs | 82 ++++--------------- .../Models/PSKeyVaultCertificatePolicy.cs | 2 +- .../Track2KeyVaultDataServiceClient.cs | 4 +- 4 files changed, 22 insertions(+), 68 deletions(-) diff --git a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 index c69283932918..23332075323f 100644 --- a/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 +++ b/src/KeyVault/KeyVault.Test/PesterTests/Certificate.Tests.ps1 @@ -9,7 +9,7 @@ Describe "Import Certificate with policy" { $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 = Import-AzKeyVaultCertificate -VaultName $vaultName -Name $certName -FilePath $certFilePath -PolicyObject $policy $cert.Policy.SecretContentType | Should -Be "application/x-pkcs12" } It "ImportCertificateFromFileWithPolicyFileParameterSet" { diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 6f97fe52ce3a..6b3ee9bbe1d6 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -36,18 +36,15 @@ namespace Microsoft.Azure.Commands.KeyVault /// importing an existing certificate package file that contains both the /// certificate and private key (example: PFX or P12 files). /// - [Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet)] + [Cmdlet("Import", ResourceManager.Common.AzureRMConstants.AzurePrefix + "KeyVaultCertificate",SupportsShouldProcess = true,DefaultParameterSetName = ImportCertificateFromFileParameterSet)] [OutputType(typeof(PSKeyVaultCertificate))] public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase { #region Parameter Set Names - 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"; + private const string ImportCertificateFromFileParameterSet = "ImportCertificateFromFile"; + private const string ImportWithPrivateKeyFromCollectionParameterSet = "ImportWithPrivateKeyFromCollection"; + private const string ImportWithPrivateKeyFromStringParameterSet = "ImportWithPrivateKeyFromString"; #endregion @@ -79,10 +76,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [Parameter(Mandatory = true, - ParameterSetName = ImportCertificateFromFileWithPolicyObjectParameterSet, - HelpMessage = "Specifies the path to the file that contains the certificate to add to key vault.")] - [Parameter(Mandatory = true, - ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, + ParameterSetName = ImportCertificateFromFileParameterSet, HelpMessage = "Specifies the path to the file that contains the certificate to add to key vault.")] public string FilePath { get; set; } @@ -90,10 +84,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Base64 encoded representation of the certificate object to import /// [Parameter(Mandatory = true, - 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, + ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, HelpMessage = "Base64 encoded representation of the certificate object to import. This certificate needs to contain the private key.")] public string CertificateString { get; set; } @@ -101,10 +92,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Specifies type of the certificate to be imported. /// [Parameter(Mandatory = false, - 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, + ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, 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; @@ -113,37 +101,18 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// Password /// [Parameter(Mandatory = false, - 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, + ParameterSetName = ImportCertificateFromFileParameterSet, HelpMessage = "Specifies the password for the certificate and private key file to import.")] [Parameter(Mandatory = false, - ParameterSetName = ImportWithPrivateKeyFromStringWithPolicyFileParameterSet, + ParameterSetName = ImportWithPrivateKeyFromStringParameterSet, 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.")] public SecureString Password { get; set; } /// /// File Path /// [Parameter(Mandatory = false, - ParameterSetName = ImportCertificateFromFileWithPolicyFileParameterSet, HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")] - [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; } /// @@ -151,15 +120,6 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// [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; } @@ -169,12 +129,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase [Parameter(Mandatory = true, Position = 2, ValueFromPipeline = true, - ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet, - HelpMessage = "Specifies the certificate collection to add to key vault.")] - [Parameter(Mandatory = true, - Position = 2, - ValueFromPipeline = true, - ParameterSetName = ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet, + ParameterSetName = ImportWithPrivateKeyFromCollectionParameterSet, HelpMessage = "Specifies the certificate collection to add to key vault.")] public X509Certificate2Collection CertificateCollection { get; set; } @@ -205,6 +160,10 @@ private void ValidateParameters() throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.FilePath), nameof(FilePath)); } } + if (this.IsParameterBound(c => c.PolicyPath) && this.IsParameterBound(c => c.PolicyObject)) + { + throw new AzPSArgumentException($"Parameter {nameof(PolicyPath)} conflicts with Parameter {nameof(PolicyObject)}. Only one of these 2 parameters could be imported at once.", nameof(PolicyPath)); + } if (this.IsParameterBound(c => c.PolicyPath)) { if (!File.Exists(PolicyPath)) @@ -213,7 +172,6 @@ private void ValidateParameters() } PolicyObject = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); } - if (PolicyObject != null && this.IsParameterBound(c => c.ContentType) && PolicyObject.SecretContentType != ContentType) { throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {PolicyObject.SecretContentType} in Certificate Policy.", ContentType); @@ -230,8 +188,7 @@ public override void ExecuteCmdlet() switch (ParameterSetName) { - case ImportCertificateFromFileWithPolicyObjectParameterSet: - case ImportCertificateFromFileWithPolicyFileParameterSet: + case ImportCertificateFromFileParameterSet: // 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)) @@ -269,14 +226,11 @@ public override void ExecuteCmdlet() } break; - case ImportWithPrivateKeyFromCollectionWithPolicyObjectParameterSet: - case ImportWithPrivateKeyFromCollectionWithPolicyFileParameterSet: - certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, Password, Tag?.ConvertToDictionary(), certPolicy: PolicyObject); - + case ImportWithPrivateKeyFromCollectionParameterSet: + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateCollection, null, Tag?.ConvertToDictionary(), certPolicy: PolicyObject); break; - case ImportWithPrivateKeyFromStringWithPolicyObjectParameterSet: - case ImportWithPrivateKeyFromStringWithPolicyFileParameterSet: + case ImportWithPrivateKeyFromStringParameterSet: certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary(), ContentType, certPolicy: PolicyObject); break; diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index be982699ab9b..e6bd480abf88 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -659,7 +659,7 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) foreach (JsonElement item2 in item.Value.EnumerateArray()) { - if (item2.EnumerateObject().Count() > 1) + if (item.Value.EnumerateArray().Count() > 1) throw new AzPSArgumentException(string.Format("Json file property {0} exceed expected number 1.", item.Name), nameof(item.Name)); foreach (JsonProperty item3 in item2.EnumerateObject()) { diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index e2490cf7c2c5..cb4fe26e63d2 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -251,8 +251,8 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - byte[] certificate = certificateCollection.Export(X509ContentType.Pfx, certPassword?.ConvertToString()); - return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType, certPolicy); + // Export contentType ref: https://github.com/Azure/azure-sdk-for-net/blob/376b04164356dc9821923b75f2223163a2701669/sdk/keyvault/Microsoft.Azure.KeyVault/src/Customized/KeyVaultClientExtensions.cs#L605 + return VaultClient.ImportCertificate(vaultName, certName, certificateCollection.Export(X509ContentType.Pfx), certPassword, tags, contentType, certPolicy); } public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, X509Certificate2Collection certs, IDictionary tags) From 676e0fe500171866b5d57dc2a80a83f90b5da038 Mon Sep 17 00:00:00 2001 From: NoriZC <110961157+NoriZC@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:29:54 +0800 Subject: [PATCH 17/21] Update ChangeLog.md --- src/KeyVault/KeyVault/ChangeLog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/KeyVault/KeyVault/ChangeLog.md b/src/KeyVault/KeyVault/ChangeLog.md index 4d7496686091..d27796c6af38 100644 --- a/src/KeyVault/KeyVault/ChangeLog.md +++ b/src/KeyVault/KeyVault/ChangeLog.md @@ -18,7 +18,7 @@ - Additional information about change #1 --> ## Upcoming Release -* Added parameter `PolicyPath` in `Import-AzKeyVaultCertificate` to support custom policy [#20780] +* Added parameter `PolicyPath` and `PolicyObject` in `Import-AzKeyVaultCertificate` to support custom policy [#20780] ## Version 4.9.2 * Updated Azure.Core to 1.28.0. From 266f0bc5ec02c8de9228fa4f73c97580063af15c Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Mon, 24 Apr 2023 17:14:01 +0800 Subject: [PATCH 18/21] Merge the exceptions, update help file --- .../ImportAzureKeyVaultCertificate.cs | 14 ++- .../Models/PSKeyVaultCertificatePolicy.cs | 46 +++------ .../Track2Models/Track2VaultClient.cs | 4 - .../help/Import-AzKeyVaultCertificate.md | 94 +++++++++++++++++-- 4 files changed, 111 insertions(+), 47 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 6b3ee9bbe1d6..cf074e579afa 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -159,6 +159,18 @@ private void ValidateParameters() { throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.FilePath), nameof(FilePath)); } + if (IsPemFile(FilePath)) + { + ContentType = Constants.PemContentType; + } + else + { + ContentType = Constants.Pkcs12ContentType; + } + } + if (this.IsParameterBound(c => c.CertificateCollection)) + { + ContentType = Constants.Pkcs12ContentType; } if (this.IsParameterBound(c => c.PolicyPath) && this.IsParameterBound(c => c.PolicyObject)) { @@ -172,7 +184,7 @@ private void ValidateParameters() } PolicyObject = PSKeyVaultCertificatePolicy.FromJsonFile(PolicyPath); } - if (PolicyObject != null && this.IsParameterBound(c => c.ContentType) && PolicyObject.SecretContentType != ContentType) + if (PolicyObject != null && PolicyObject.SecretContentType != ContentType) { throw new AzPSArgumentException($"User input {ContentType} conflicts with the ContentType stated as {PolicyObject.SecretContentType} in Certificate Policy.", ContentType); } diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index e6bd480abf88..23dfd79de9f1 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -331,39 +331,24 @@ internal Track2CertificateSDK.CertificatePolicy ToTrack2CertificatePolicy() } else certificatePolicy = new Track2CertificateSDK.CertificatePolicy(IssuerName, SubjectName); - }else + } + else { certificatePolicy = new Track2CertificateSDK.CertificatePolicy(); } - if (!string.IsNullOrWhiteSpace(SecretContentType)) - certificatePolicy.ContentType = SecretContentType; - - if (!string.IsNullOrWhiteSpace(Kty)) + certificatePolicy.ContentType = SecretContentType; + if ( !string.IsNullOrEmpty(Kty) ) certificatePolicy.KeyType = Kty; - - if (KeySize.HasValue) - certificatePolicy.KeySize = KeySize; - - if (!string.IsNullOrWhiteSpace(Curve)) + certificatePolicy.KeySize = KeySize; + if (!string.IsNullOrEmpty(Curve)) certificatePolicy.KeyCurveName = Curve; - - if (ReuseKeyOnRenewal.HasValue) - certificatePolicy.ReuseKey = ReuseKeyOnRenewal; - - if (Exportable.HasValue) - certificatePolicy.Exportable = Exportable; - - if (CertificateTransparency.HasValue) - certificatePolicy.CertificateTransparency = CertificateTransparency; - + certificatePolicy.ReuseKey = ReuseKeyOnRenewal; + certificatePolicy.Exportable = Exportable; + certificatePolicy.CertificateTransparency = CertificateTransparency; if (!string.IsNullOrWhiteSpace(CertificateType)) certificatePolicy.CertificateType = CertificateType; - - if (Enabled.HasValue) - certificatePolicy.Enabled = Enabled; - - if (ValidityInMonths.HasValue) - certificatePolicy.ValidityInMonths = ValidityInMonths.Value; + certificatePolicy.Enabled = Enabled; + certificatePolicy.ValidityInMonths = ValidityInMonths; if (RenewAtNumberOfDaysBeforeExpiry.HasValue || RenewAtPercentageLifetime.HasValue || @@ -746,14 +731,7 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) var lifetimeAction = lifetimeActions.FirstOrDefault(x => !string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2CertificateSDK.CertificatePolicyAction.EmailContacts.ToString(), true) && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); - if (lifetimeAction == null) - return null; - if (lifetimeAction.DaysBeforeExpiry.HasValue) - return lifetimeAction.DaysBeforeExpiry; - if (lifetimeAction.LifetimePercentage.HasValue) - return lifetimeAction.LifetimePercentage; - else - return null; + return lifetimeAction == null ? null : (lifetimeAction.DaysBeforeExpiry ?? lifetimeAction.LifetimePercentage); } private void ValidateInternal( diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index 370c781b90b4..8fa93a204641 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -232,10 +232,6 @@ private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, st if (certPolicy != null) { certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); - if ( certificatePolicy.ContentType != contentType ) - { - throw new AzPSArgumentException($"{contentType} indicated by certificate file conflicts with the ContentType stated as {certificatePolicy.ContentType} in Certificate Policy.", contentType); - } } else { diff --git a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md index dc32a2f63008..1645e467b70e 100644 --- a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md +++ b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md @@ -16,22 +16,23 @@ Imports a certificate to a key vault. ### ImportCertificateFromFile (Default) ``` Import-AzKeyVaultCertificate [-VaultName] [-Name] -FilePath - [-Password ] [-Tag ] [-DefaultProfile ] [-WhatIf] [-Confirm] - [] + [-Password ] [-PolicyPath ] [-PolicyObject ] + [-Tag ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` ### ImportWithPrivateKeyFromString ``` Import-AzKeyVaultCertificate [-VaultName] [-Name] -CertificateString - [-ContentType ] [-Password ] [-Tag ] - [-DefaultProfile ] [-WhatIf] [-Confirm] [] + [-ContentType ] [-Password ] [-PolicyPath ] + [-PolicyObject ] [-Tag ] [-DefaultProfile ] + [-WhatIf] [-Confirm] [] ``` ### ImportWithPrivateKeyFromCollection ``` -Import-AzKeyVaultCertificate [-VaultName] [-Name] - [-CertificateCollection] [-Tag ] - [-DefaultProfile ] [-WhatIf] [-Confirm] [] +Import-AzKeyVaultCertificate [-VaultName] [-Name] [-PolicyPath ] + [-PolicyObject ] [-CertificateCollection] + [-Tag ] [-DefaultProfile ] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION @@ -84,7 +85,6 @@ The second command imports the certificate named ImportCert01 into the CosotosoK $Password = ConvertTo-SecureString -String "123" -AsPlainText -Force $Base64String = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes("import.pfx")) Import-AzKeyVaultCertificate -VaultName "ContosoKV01" -Name "ImportCert01" -CertificateString $Base64String -Password $Password - ``` ```output @@ -119,6 +119,54 @@ stores it in the $Password variable. The second command reads a certificate as a Base64 encoded representation. The third command imports the certificate named ImportCert01 into the CosotosoKV01 key vault. +### Example 3: Import a key vault certificate with PolicyFile +```powershell +$Password = ConvertTo-SecureString -String "123" -AsPlainText -Force +Import-AzKeyVaultCertificate -VaultName "ContosoKV01" -Name "ImportCert01" -FilePath "C:\Users\contosoUser\Desktop\import.pfx" -Password $Password -PolicyPath "C:\Users\contosoUser\Desktop\policy.json" +``` + +```output +Name : importCert01 +Certificate : [Subject] + CN=contoso.com + + [Issuer] + CN=contoso.com + + [Serial Number] + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + + [Not Before] + 2/8/2016 3:11:45 PM + + [Not After] + 8/8/2016 4:21:45 PM + + [Thumbprint] + XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +KeyId : https://ContosoKV01.vault.azure.net/keys/ImportCert01/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +SecretId : https://ContosoKV01.vault.azure.net/secrets/ImportCert01/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Thumbprint : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Policy : Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificatePolicy +RecoveryLevel : Recoverable+Purgeable +Enabled : True +Expires : 6/9/2023 6:20:26 AM +NotBefore : 3/11/2023 6:20:26 AM +Created : 4/24/2023 9:05:51 AM +Updated : 4/24/2023 9:05:51 AM +Tags : {} +VaultName : ContosoKV01 +Name : ImportCert01 +Version : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +Id : https://ContosoKV01.vault.azure.net/certificates/ImportCert01/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +``` + +The first command uses the ConvertTo-SecureString cmdlet to create a secure password, and then +stores it in the $Password variable. +The second command imports the certificate named ImportCert01 into the CosotosoKV01 key vault with +a policy defined by file. + ## PARAMETERS ### -CertificateCollection @@ -227,6 +275,36 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -PolicyObject +An in-memory object to specify management policy for the certificate. + +```yaml +Type: Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificatePolicy +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByValue) +Accept wildcard characters: False +``` + +### -PolicyPath +A file path to specify management policy for the certificate that contains JSON encoded policy definition. + +```yaml +Type: System.String +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -Tag Key-value pairs in the form of a hash table. For example: @{key0="value0";key1=$null;key2="value2"} From d95d512261981bc42a85b5f18e0c64e8f7b213c8 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Mon, 24 Apr 2023 17:17:11 +0800 Subject: [PATCH 19/21] Merge the exceptions, update help file --- .../KeyVault/Models/PSKeyVaultCertificatePolicy.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 23dfd79de9f1..1e69e211ee9f 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -704,14 +704,7 @@ internal static PSKeyVaultCertificatePolicy FromJsonFile(string filePath) var lifetimeAction = lifetimeActions.FirstOrDefault(x => string.IsNullOrEmpty(x.Action.ToString()) && 0 == string.Compare(x.Action.ToString(), Track2CertificateSDK.CertificatePolicyAction.AutoRenew.ToString(), true) && (x.DaysBeforeExpiry.HasValue || x.LifetimePercentage.HasValue)); - if (lifetimeAction == null) - return null; - if (lifetimeAction.DaysBeforeExpiry.HasValue) - return lifetimeAction.DaysBeforeExpiry; - if (lifetimeAction.LifetimePercentage.HasValue) - return lifetimeAction.LifetimePercentage; - else - return null; + return lifetimeAction == null ? null : (lifetimeAction.DaysBeforeExpiry ?? lifetimeAction.LifetimePercentage); } private static int? FindIntValueForEmailAction(IEnumerable lifetimeActions, Func intValueGetter) From 9580fa38a9509224112224d23bc6e0a03b3292c1 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Thu, 27 Apr 2023 17:55:59 +0800 Subject: [PATCH 20/21] Resolve the comments 4/27 --- .../Certificate/ImportAzureKeyVaultCertificate.cs | 4 ++-- .../Models/PSKeyVaultCertificatePolicy.cs | 15 +++++++++++++++ .../KeyVault/Track2Models/Track2VaultClient.cs | 14 +++----------- .../KeyVault/help/Import-AzKeyVaultCertificate.md | 7 ++++++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index cf074e579afa..0e1a244fb85d 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -112,7 +112,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [Parameter(Mandatory = false, - HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition.")] + HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition. Mutual-exclusive to PolicyFile.")] public string PolicyPath { get; set; } /// @@ -120,7 +120,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// [Parameter(Mandatory = false, ValueFromPipeline = true, - HelpMessage = "An in-memory object to specify management policy for the certificate.")] + HelpMessage = "An in-memory object to specify management policy for the certificate. Mutual-exclusive to PolicyPath.")] public PSKeyVaultCertificatePolicy PolicyObject { get; set; } /// diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs index 1e69e211ee9f..793d233e8e8b 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificatePolicy.cs @@ -21,6 +21,7 @@ using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; +using System.Text; using System.Text.Json; using Track2CertificateSDK = Azure.Security.KeyVault.Certificates; @@ -943,5 +944,19 @@ private void ValidateSubjectName(string subjectName) throw new ArgumentException("The subject name provided is not a valid X500 name.", e); } } + + public override string ToString() + { + if (this == null) return string.Empty; + + var sb = new StringBuilder(); + sb.AppendLine(); + sb.AppendFormat("{0, -15}: {1}{2}", "Secret Content Type", SecretContentType, Environment.NewLine); + sb.AppendFormat("{0, -15}: {1}{2}", "Issuer Name", IssuerName, Environment.NewLine); + sb.AppendFormat("{0, -15}: {1}{2}", "Created On", Created, Environment.NewLine); + sb.AppendFormat("{0, -15}: {1}{2}", "Updated On", Updated, Environment.NewLine); + sb.AppendLine("..."); + return sb.ToString(); + } } } diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index 8fa93a204641..c9eae3ec046a 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -228,18 +228,10 @@ internal PSKeyVaultCertificate ImportCertificate(string vaultName, string certNa private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType, PSKeyVaultCertificatePolicy certPolicy = null) { - CertificatePolicy certificatePolicy = null; - if (certPolicy != null) + CertificatePolicy certificatePolicy = certPolicy?.ToTrack2CertificatePolicy() ?? new CertificatePolicy() { - certificatePolicy = certPolicy.ToTrack2CertificatePolicy(); - } - else - { - certificatePolicy = new CertificatePolicy() - { - ContentType = contentType - }; - } + ContentType = contentType + }; var options = new ImportCertificateOptions(certName, certificate) { Policy = certificatePolicy, diff --git a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md index 1645e467b70e..6309a37dd1dd 100644 --- a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md +++ b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md @@ -148,7 +148,12 @@ Certificate : [Subject] KeyId : https://ContosoKV01.vault.azure.net/keys/ImportCert01/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX SecretId : https://ContosoKV01.vault.azure.net/secrets/ImportCert01/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Thumbprint : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -Policy : Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificatePolicy +Policy : + Secret Content Type: application/x-pkcs12 + Issuer Name : Unknown + Created On : 3/22/2023 6:00:52 AM + Updated On : 4/27/2023 9:52:53 AM + ... RecoveryLevel : Recoverable+Purgeable Enabled : True Expires : 6/9/2023 6:20:26 AM From 88b0f1cedfb2d1e98c27c991e03029e1cb2d45f7 Mon Sep 17 00:00:00 2001 From: Nori Zhang Date: Thu, 27 Apr 2023 18:06:33 +0800 Subject: [PATCH 21/21] Update descriptions of policyobject and policypath --- .../Commands/Certificate/ImportAzureKeyVaultCertificate.cs | 2 +- src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 0e1a244fb85d..8015ccf4a18a 100644 --- a/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -112,7 +112,7 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase /// File Path /// [Parameter(Mandatory = false, - HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition. Mutual-exclusive to PolicyFile.")] + HelpMessage = "A file path to specify management policy for the certificate that contains JSON encoded policy definition. Mutual-exclusive to PolicyObject.")] public string PolicyPath { get; set; } /// diff --git a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md index 6309a37dd1dd..db1d990f609a 100644 --- a/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md +++ b/src/KeyVault/KeyVault/help/Import-AzKeyVaultCertificate.md @@ -281,7 +281,7 @@ Accept wildcard characters: False ``` ### -PolicyObject -An in-memory object to specify management policy for the certificate. +An in-memory object to specify management policy for the certificate. Mutual-exclusive to PolicyPath. ```yaml Type: Microsoft.Azure.Commands.KeyVault.Models.PSKeyVaultCertificatePolicy @@ -296,7 +296,7 @@ Accept wildcard characters: False ``` ### -PolicyPath -A file path to specify management policy for the certificate that contains JSON encoded policy definition. +A file path to specify management policy for the certificate that contains JSON encoded policy definition. Mutual-exclusive to PolicyObject. ```yaml Type: System.String