diff --git a/src/KeyVault/KeyVault/Az.KeyVault.psd1 b/src/KeyVault/KeyVault/Az.KeyVault.psd1 index e166de69e0d8..27167be0019b 100644 --- a/src/KeyVault/KeyVault/Az.KeyVault.psd1 +++ b/src/KeyVault/KeyVault/Az.KeyVault.psd1 @@ -60,6 +60,7 @@ RequiredAssemblies = 'Microsoft.Azure.KeyVault.dll', 'Microsoft.Azure.KeyVault.WebKey.dll', 'Microsoft.Azure.Management.KeyVault.dll', 'Azure.Security.KeyVault.Keys.dll', + 'Azure.Security.KeyVault.Certificates.dll', 'Azure.Security.KeyVault.Administration.dll', 'BouncyCastle.Crypto.dll' diff --git a/src/KeyVault/KeyVault/ChangeLog.md b/src/KeyVault/KeyVault/ChangeLog.md index ebb844cfe2f9..3636c0d89265 100644 --- a/src/KeyVault/KeyVault/ChangeLog.md +++ b/src/KeyVault/KeyVault/ChangeLog.md @@ -18,6 +18,7 @@ - Additional information about change #1 --> ## Upcoming Release +* Supported importing pem certificate by `Import-AzKeyVaultCertificate` [#18494] * Supported accepting rotation policy in a JSON file * [Breaking Change] Changed parameter `ExpiresIn` in `Set-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string. It must be an ISO 8601 duration like "P30D" for 30 days. * [Breaking Change] Changed output properties `ExpiresIn`, `TimeAfterCreate` and `TimeBeforeExpiry` of `Set-AzKeyVaultKeyRotationPolicy` and `Get-AzKeyVaultKeyRotationPolicy` from TimeSpan? to string. diff --git a/src/KeyVault/KeyVault/Commands/AddAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/AddAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/AddAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/AddAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/BackupAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/BackupAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/BackupAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/BackupAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/AddAzureKeyVaultCertificateContact.cs b/src/KeyVault/KeyVault/Commands/Certificate/Contact/AddAzureKeyVaultCertificateContact.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/AddAzureKeyVaultCertificateContact.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Contact/AddAzureKeyVaultCertificateContact.cs diff --git a/src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateContact.cs b/src/KeyVault/KeyVault/Commands/Certificate/Contact/GetAzureKeyVaultCertificateContact.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateContact.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Contact/GetAzureKeyVaultCertificateContact.cs diff --git a/src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateContact.cs b/src/KeyVault/KeyVault/Commands/Certificate/Contact/RemoveAzureKeyVaultCertificateContact.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateContact.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Contact/RemoveAzureKeyVaultCertificateContact.cs diff --git a/src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/GetAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/GetAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateOperation.cs b/src/KeyVault/KeyVault/Commands/Certificate/GetAzureKeyVaultCertificateOperation.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateOperation.cs rename to src/KeyVault/KeyVault/Commands/Certificate/GetAzureKeyVaultCertificateOperation.cs diff --git a/src/KeyVault/KeyVault/Commands/ImportAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs similarity index 71% rename from src/KeyVault/KeyVault/Commands/ImportAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs index 667d4144ca9d..22ebd2f5b4e3 100644 --- a/src/KeyVault/KeyVault/Commands/ImportAzureKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Commands/Certificate/ImportAzureKeyVaultCertificate.cs @@ -23,6 +23,9 @@ using KeyVaultProperties = Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters; +using Microsoft.WindowsAzure.Commands.Utilities.Common; +using Microsoft.Azure.Commands.Common.Exceptions; +using Microsoft.Azure.Commands.KeyVault.Properties; namespace Microsoft.Azure.Commands.KeyVault { @@ -116,51 +119,78 @@ public class ImportAzureKeyVaultCertificate : KeyVaultCmdletBase #endregion + protected override void BeginProcessing() + { + FilePath = this.TryResolvePath(FilePath); + base.BeginProcessing(); + } + + private void ValidateParameters() + { + // Verify the FileNotFound whether exists + if (this.IsParameterBound(c => c.FilePath)) + { + if (!File.Exists(FilePath)) + { + throw new AzPSArgumentException(string.Format(Resources.FileNotFound, this.FilePath), nameof(FilePath)); + } + } + } + public override void ExecuteCmdlet() { if (ShouldProcess(Name, Properties.Resources.ImportCertificate)) { - List certBundleList = new List(); + ValidateParameters(); + PSKeyVaultCertificate certBundle = null; switch (ParameterSetName) { case ImportCertificateFromFileParameterSet: - - bool doImport = false; - X509Certificate2Collection userProvidedCertColl = InitializeCertificateCollection(); - - // look for at least one certificate which contains a private key - foreach (var cert in userProvidedCertColl) - { - doImport |= cert.HasPrivateKey; - if (doImport) - break; - } - - if (doImport) + // 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[] base64Bytes = userProvidedCertColl.Export(X509ContentType.Pfx, Password?.ConvertToString()); - string base64CertCollection = Convert.ToBase64String(base64Bytes); - certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, base64CertCollection, Password, Tag == null ? null : Tag.ConvertToDictionary()); + byte[] pemBytes = File.ReadAllBytes(FilePath); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, pemBytes, Password, Tag?.ConvertToDictionary(), Constants.PemContentType); } else { - certBundle = this.DataServiceClient.MergeCertificate( - VaultName, - Name, - userProvidedCertColl, - Tag == null ? null : Tag.ConvertToDictionary()); + bool doImport = false; + X509Certificate2Collection userProvidedCertColl = InitializeCertificateCollection(); + + // look for at least one certificate which contains a private key + foreach (var cert in userProvidedCertColl) + { + doImport |= cert.HasPrivateKey; + if (doImport) + break; + } + + if (doImport) + { + byte[] base64Bytes = userProvidedCertColl.Export(X509ContentType.Pfx, Password?.ConvertToString()); + certBundle = this.Track2DataClient.ImportCertificate(VaultName, Name, base64Bytes, Password, Tag?.ConvertToDictionary()); + } + else + { + certBundle = this.DataServiceClient.MergeCertificate( + VaultName, + Name, + userProvidedCertColl, + Tag == null ? null : Tag.ConvertToDictionary()); + } } break; case ImportWithPrivateKeyFromCollectionParameterSet: - certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateCollection, Tag == null ? null : Tag.ConvertToDictionary()); + certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateCollection, Tag?.ConvertToDictionary()); break; case ImportWithPrivateKeyFromStringParameterSet: - certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag == null ? null : Tag.ConvertToDictionary()); + certBundle = this.DataServiceClient.ImportCertificate(VaultName, Name, CertificateString, Password, Tag?.ConvertToDictionary()); break; } @@ -169,6 +199,11 @@ public override void ExecuteCmdlet() } } + private bool IsPemFile(string filePath) + { + return ".pem".Equals(Path.GetExtension(FilePath), StringComparison.OrdinalIgnoreCase); + } + internal X509Certificate2Collection InitializeCertificateCollection() { FileInfo certFile = new FileInfo(ResolveUserPath(this.FilePath)); diff --git a/src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateIssuer.cs b/src/KeyVault/KeyVault/Commands/Certificate/Issuer/GetAzureKeyVaultCertificateIssuer.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificateIssuer.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Issuer/GetAzureKeyVaultCertificateIssuer.cs diff --git a/src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateIssuer.cs b/src/KeyVault/KeyVault/Commands/Certificate/Issuer/RemoveAzureKeyVaultCertificateIssuer.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateIssuer.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Issuer/RemoveAzureKeyVaultCertificateIssuer.cs diff --git a/src/KeyVault/KeyVault/Commands/SetAzureKeyVaultCertificateIssuer.cs b/src/KeyVault/KeyVault/Commands/Certificate/Issuer/SetAzureKeyVaultCertificateIssuer.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/SetAzureKeyVaultCertificateIssuer.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Issuer/SetAzureKeyVaultCertificateIssuer.cs diff --git a/src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificateAdministratorDetails.cs b/src/KeyVault/KeyVault/Commands/Certificate/NewAzureKeyVaultCertificateAdministratorDetails.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificateAdministratorDetails.cs rename to src/KeyVault/KeyVault/Commands/Certificate/NewAzureKeyVaultCertificateAdministratorDetails.cs diff --git a/src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificateOrganizationDetails.cs b/src/KeyVault/KeyVault/Commands/Certificate/NewAzureKeyVaultCertificateOrganizationDetails.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificateOrganizationDetails.cs rename to src/KeyVault/KeyVault/Commands/Certificate/NewAzureKeyVaultCertificateOrganizationDetails.cs diff --git a/src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Commands/Certificate/Policy/GetAzureKeyVaultCertificatePolicy.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/GetAzureKeyVaultCertificatePolicy.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Policy/GetAzureKeyVaultCertificatePolicy.cs diff --git a/src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Commands/Certificate/Policy/NewAzureKeyVaultCertificatePolicy.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/NewAzureKeyVaultCertificatePolicy.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Policy/NewAzureKeyVaultCertificatePolicy.cs diff --git a/src/KeyVault/KeyVault/Commands/SetAzureKeyVaultCertificatePolicy.cs b/src/KeyVault/KeyVault/Commands/Certificate/Policy/SetAzureKeyVaultCertificatePolicy.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/SetAzureKeyVaultCertificatePolicy.cs rename to src/KeyVault/KeyVault/Commands/Certificate/Policy/SetAzureKeyVaultCertificatePolicy.cs diff --git a/src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/RemoveAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/RemoveAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateOperation.cs b/src/KeyVault/KeyVault/Commands/Certificate/RemoveAzureKeyVaultCertificateOperation.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/RemoveAzureKeyVaultCertificateOperation.cs rename to src/KeyVault/KeyVault/Commands/Certificate/RemoveAzureKeyVaultCertificateOperation.cs diff --git a/src/KeyVault/KeyVault/Commands/RestoreAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/RestoreAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/RestoreAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/RestoreAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/StopAzureKeyVaultCertificateOperation.cs b/src/KeyVault/KeyVault/Commands/Certificate/StopAzureKeyVaultCertificateOperation.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/StopAzureKeyVaultCertificateOperation.cs rename to src/KeyVault/KeyVault/Commands/Certificate/StopAzureKeyVaultCertificateOperation.cs diff --git a/src/KeyVault/KeyVault/Commands/UndoAzureKeyVaultCertificateRemoval.cs b/src/KeyVault/KeyVault/Commands/Certificate/UndoAzureKeyVaultCertificateRemoval.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/UndoAzureKeyVaultCertificateRemoval.cs rename to src/KeyVault/KeyVault/Commands/Certificate/UndoAzureKeyVaultCertificateRemoval.cs diff --git a/src/KeyVault/KeyVault/Commands/UpdateAzureKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Commands/Certificate/UpdateAzureKeyVaultCertificate.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/UpdateAzureKeyVaultCertificate.cs rename to src/KeyVault/KeyVault/Commands/Certificate/UpdateAzureKeyVaultCertificate.cs diff --git a/src/KeyVault/KeyVault/Commands/UndoAzureKeyVaultKeyRemoval.cs b/src/KeyVault/KeyVault/Commands/Key/UndoAzureKeyVaultKeyRemoval.cs similarity index 100% rename from src/KeyVault/KeyVault/Commands/UndoAzureKeyVaultKeyRemoval.cs rename to src/KeyVault/KeyVault/Commands/Key/UndoAzureKeyVaultKeyRemoval.cs diff --git a/src/KeyVault/KeyVault/KeyVault.csproj b/src/KeyVault/KeyVault/KeyVault.csproj index 68c1ccc250de..f77803c3bee8 100644 --- a/src/KeyVault/KeyVault/KeyVault.csproj +++ b/src/KeyVault/KeyVault/KeyVault.csproj @@ -14,6 +14,7 @@ + diff --git a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs index 52d434f266a8..a0d1584057b4 100644 --- a/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/IKeyVaultDataServiceClient.cs @@ -162,9 +162,13 @@ public interface IKeyVaultDataServiceClient PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, X509Certificate2Collection certs, IDictionary tags); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertColl, SecureString certPassword, IDictionary tags); + PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, byte[] certBytes, Dictionary tags); - PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags); + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); + + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertString, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType); + + PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType); PSDeletedKeyVaultCertificate DeleteCertificate(string vaultName, string certName); diff --git a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs index dc047b5efd29..11798d35b8c1 100644 --- a/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Models/KeyVaultDataServiceClient.cs @@ -786,6 +786,11 @@ public string BackupCertificate(string vaultName, string certificateName, string return outputBlobPath; } + public PSKeyVaultCertificate MergeCertificate(string vaultName, string name, byte[] certBytes, Dictionary tags) + { + throw new NotImplementedException(); + } + public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, X509Certificate2Collection certs, IDictionary tags) { if (string.IsNullOrEmpty(vaultName)) @@ -812,7 +817,12 @@ public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertColl, SecureString certPassword, IDictionary tags) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + { + 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) { if (string.IsNullOrEmpty(vaultName)) throw new ArgumentNullException(nameof(vaultName)); @@ -827,14 +837,13 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName var password = (certPassword == null) ? string.Empty : certPassword.ConvertToString(); - try { certBundle = this.keyVaultClient.ImportCertificateAsync(vaultAddress, certName, base64CertColl, password, new CertificatePolicy { SecretProperties = new SecretProperties { - ContentType = "application/x-pkcs12" + ContentType = contentType } }, null, tags).GetAwaiter().GetResult(); } @@ -846,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) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType) { if (string.IsNullOrEmpty(vaultName)) throw new ArgumentNullException(nameof(vaultName)); @@ -864,7 +873,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName { SecretProperties = new SecretProperties { - ContentType = "application/x-pkcs12" + ContentType = contentType } }, null, tags).GetAwaiter().GetResult(); } @@ -875,6 +884,7 @@ public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName return new PSKeyVaultCertificate(certBundle); } + public IEnumerable GetCertificateContacts(string vaultName) { if (string.IsNullOrEmpty(vaultName)) diff --git a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs index aba04bd9ba05..467570c99782 100644 --- a/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs +++ b/src/KeyVault/KeyVault/Models/PSKeyVaultCertificate.cs @@ -11,6 +11,8 @@ // limitations under the License. // ---------------------------------------------------------------------------------- +using Azure.Security.KeyVault.Certificates; + using Microsoft.Azure.Commands.KeyVault.Properties; using Microsoft.Azure.KeyVault.Models; using System; @@ -42,9 +44,8 @@ internal PSKeyVaultCertificate(CertificateBundle certificateBundle, VaultUriHelp SetObjectIdentifier(vaultUriHelper, certificateBundle.CertificateIdentifier); - // VaultName formatted incorrect in certificateBundle - var vaultUri = new Uri(certificateBundle.CertificateIdentifier.Vault); - VaultName = vaultUri.Host.Split('.').First(); + // Vault formatted as "https://{vaultName}.vault.azure.net:443" in certificateBundle + VaultName = new Uri(certificateBundle.CertificateIdentifier.Vault).Host.Split('.').First(); if ( certificateBundle.Cer != null ) { @@ -88,14 +89,12 @@ internal PSKeyVaultCertificate(CertificateBundle certificateBundle) if (certificateBundle.CertificateIdentifier == null) throw new ArgumentException(Resources.InvalidKeyIdentifier); - var vaultUri = new Uri(certificateBundle.CertificateIdentifier.Vault); - SetObjectIdentifier(new ObjectIdentifier { Id = certificateBundle.CertificateIdentifier.Identifier, Name = certificateBundle.CertificateIdentifier.Name, - // VaultName formatted incorrect in certificateBundle - VaultName = vaultUri.Host.Split('.').First(), + // Vault formatted as "https://{vaultName}.vault.azure.net:443" in certificateBundle + VaultName = new Uri(certificateBundle.CertificateIdentifier.Vault).Host.Split('.').First(), Version = certificateBundle.CertificateIdentifier.Version }); @@ -131,6 +130,45 @@ internal PSKeyVaultCertificate(CertificateBundle certificateBundle) } } + internal PSKeyVaultCertificate(KeyVaultCertificateWithPolicy keyVaultCertificate) + { + if (keyVaultCertificate == null) + { + throw new ArgumentNullException(nameof(keyVaultCertificate)); + } + if (keyVaultCertificate.Id == null) + throw new ArgumentException(Resources.InvalidKeyIdentifier); + + SetObjectIdentifier(new ObjectIdentifier + { + Id = keyVaultCertificate.Id.ToString(), + Name = keyVaultCertificate.Name, + // Extract VaultName from VaultUri + VaultName = keyVaultCertificate.Properties?.VaultUri.Host.Split('.').First(), + Version = keyVaultCertificate.Properties?.Version + }); + + if (keyVaultCertificate.Cer != null) + { + Certificate = new X509Certificate2(keyVaultCertificate.Cer); + Thumbprint = Certificate.Thumbprint; + } + + KeyId = keyVaultCertificate.KeyId?.ToString(); + SecretId = keyVaultCertificate.SecretId?.ToString(); + + if (keyVaultCertificate.Properties != null) + { + Created = keyVaultCertificate.Properties.CreatedOn?.DateTime; + Expires = keyVaultCertificate.Properties.ExpiresOn?.DateTime; + NotBefore = keyVaultCertificate.Properties.NotBefore?.DateTime; + Enabled = keyVaultCertificate.Properties.Enabled; + Updated = keyVaultCertificate.Properties.UpdatedOn?.DateTime; + RecoveryLevel = keyVaultCertificate.Properties.RecoveryLevel; + Tags = keyVaultCertificate.Properties.Tags?.ConvertToHashtable(); + } + } + internal static PSKeyVaultCertificate FromCertificateBundle(CertificateBundle certificateBundle) { if ( certificateBundle == null ) diff --git a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs index f65561b88ba6..68ba1bbce626 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2KeyVaultDataServiceClient.cs @@ -237,12 +237,17 @@ public IEnumerable GetDeletedCertifica throw new NotImplementedException(); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string base64CertColl, SecureString certPassword, IDictionary tags) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, string certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) { throw new NotImplementedException(); } - public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags) + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, byte[] certificate, SecureString certPassword, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + { + return VaultClient.ImportCertificate(vaultName, certName, certificate, certPassword, tags, contentType); + } + + public PSKeyVaultCertificate ImportCertificate(string vaultName, string certName, X509Certificate2Collection certificateCollection, IDictionary tags, string contentType = Constants.Pkcs12ContentType) { throw new NotImplementedException(); } @@ -252,6 +257,11 @@ public PSKeyVaultCertificate MergeCertificate(string vaultName, string certName, throw new NotImplementedException(); } + public PSKeyVaultCertificate MergeCertificate(string vaultName, string name, byte[] certBytes, Dictionary tags) + { + return VaultClient.MergeCertifcate(vaultName, name, certBytes, tags); + } + public void PurgeCertificate(string vaultName, string certName) { throw new NotImplementedException(); diff --git a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs index 705bfa5c9f17..b86b4b876db4 100644 --- a/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs +++ b/src/KeyVault/KeyVault/Track2Models/Track2VaultClient.cs @@ -1,14 +1,15 @@ -using Azure.Security.KeyVault.Keys; +using Azure.Security.KeyVault.Certificates; +using Azure.Security.KeyVault.Keys; using Azure.Security.KeyVault.Keys.Cryptography; using Microsoft.Azure.Commands.Common.Authentication.Abstractions; using Microsoft.Azure.Commands.KeyVault.Models; -using Microsoft.Azure.KeyVault.Models; using Microsoft.WindowsAzure.Commands.Utilities.Common; using System; using System.Collections; -using System.Xml; +using System.Collections.Generic; +using System.Security; namespace Microsoft.Azure.Commands.KeyVault.Track2Models { @@ -23,6 +24,7 @@ internal class Track2VaultClient // todo: consider caching clients private KeyClient CreateKeyClient(string vaultName) => new KeyClient(_vaultUriHelper.CreateVaultUri(vaultName), _credential); private CryptographyClient CreateCryptographyClient(string keyId) => new CryptographyClient(new Uri(keyId), _credential); + private CertificateClient CreateCertificateClient(string vaultName) => new CertificateClient(_vaultUriHelper.CreateVaultUri(vaultName), _credential); public Track2VaultClient(IAuthenticationFactory authFactory, IAzureContext context) { @@ -49,7 +51,7 @@ private PSKeyVaultKey CreateKey(KeyClient client, string keyName, PSKeyVaultKeyA } else if (keyAttributes.KeyType == KeyType.Ec || keyAttributes.KeyType == KeyType.EcHsm) { - options = new CreateEcKeyOptions(keyName, isHsm) { CurveName = string.IsNullOrEmpty(curveName) ? (KeyCurveName?) null : new KeyCurveName(curveName) }; + options = new CreateEcKeyOptions(keyName, isHsm) { CurveName = string.IsNullOrEmpty(curveName) ? (KeyCurveName?)null : new KeyCurveName(curveName) }; } else { @@ -186,7 +188,7 @@ internal PSKeyRotationPolicy SetKeyRotationPolicy(PSKeyRotationPolicy psKeyRotat var policy = new KeyRotationPolicy() { ExpiresIn = psKeyRotationPolicy.ExpiresIn, - LifetimeActions = {} + LifetimeActions = { } }; psKeyRotationPolicy.LifetimeActions?.ForEach( @@ -207,5 +209,46 @@ 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) + { + if (string.IsNullOrEmpty(vaultName)) + throw new ArgumentNullException(nameof(vaultName)); + if (string.IsNullOrEmpty(certName)) + throw new ArgumentNullException(nameof(certName)); + if (null == certificate) + throw new ArgumentNullException(nameof(certificate)); + + var certClient = CreateCertificateClient(vaultName); + return ImportCertificate(certClient, certName, certificate, password, tags, contentType); + } + + private PSKeyVaultCertificate ImportCertificate(CertificateClient certClient, string certName, byte[] certificate, SecureString password, IDictionary tags, string contentType = Constants.Pkcs12ContentType) + { + var options = new ImportCertificateOptions(certName, certificate) + { + Policy = new CertificatePolicy() + { + ContentType = contentType + }, + Password = password?.ConvertToString() + }; + tags?.ForEach((entry) => + { + options.Tags.Add(entry.Key.ToString(), entry.Value.ToString()); + }); + return new PSKeyVaultCertificate(certClient.ImportCertificate(options)); + } + + public PSKeyVaultCertificate MergeCertifcate(string vaultName, string certName, byte[] certificate, IDictionary tags) + { + var certClient = CreateCertificateClient(vaultName); + var options = new MergeCertificateOptions(certName, new List { certificate }); + tags?.ForEach((entry) => { options.Tags.Add(entry.Key.ToString(), entry.Value.ToString()); }); + var cert = certClient.MergeCertificate(options); + return new PSKeyVaultCertificate(cert); + } + #endregion } }