From 4153007d631ad11e13b21a8ae40a40d795947701 Mon Sep 17 00:00:00 2001 From: Guflly <145608489+Guflly@users.noreply.github.com> Date: Mon, 27 Jul 2026 14:28:14 -0700 Subject: [PATCH] Add Azure Key Vault certificate version option --- src/Sign.Cli/AzureKeyVaultCommand.cs | 13 ++++- .../AzureKeyVaultResources.Designer.cs | 6 +++ src/Sign.Cli/AzureKeyVaultResources.resx | 3 ++ .../xlf/AzureKeyVaultResources.cs.xlf | 5 ++ .../xlf/AzureKeyVaultResources.de.xlf | 5 ++ .../xlf/AzureKeyVaultResources.es.xlf | 5 ++ .../xlf/AzureKeyVaultResources.fr.xlf | 5 ++ .../xlf/AzureKeyVaultResources.it.xlf | 5 ++ .../xlf/AzureKeyVaultResources.ja.xlf | 5 ++ .../xlf/AzureKeyVaultResources.ko.xlf | 5 ++ .../xlf/AzureKeyVaultResources.pl.xlf | 5 ++ .../xlf/AzureKeyVaultResources.pt-BR.xlf | 5 ++ .../xlf/AzureKeyVaultResources.ru.xlf | 5 ++ .../xlf/AzureKeyVaultResources.tr.xlf | 5 ++ .../xlf/AzureKeyVaultResources.zh-Hans.xlf | 5 ++ .../xlf/AzureKeyVaultResources.zh-Hant.xlf | 5 ++ .../KeyVaultService.cs | 18 ++++++- .../AzureKeyVaultCommandTests.cs | 25 ++++++++++ .../KeyVaultServiceProviderTests.cs | 4 +- .../KeyVaultServiceTests.cs | 47 ++++++++++++++++--- 20 files changed, 169 insertions(+), 12 deletions(-) diff --git a/src/Sign.Cli/AzureKeyVaultCommand.cs b/src/Sign.Cli/AzureKeyVaultCommand.cs index 25e9ee82..198aca68 100644 --- a/src/Sign.Cli/AzureKeyVaultCommand.cs +++ b/src/Sign.Cli/AzureKeyVaultCommand.cs @@ -18,6 +18,7 @@ namespace Sign.Cli internal sealed class AzureKeyVaultCommand : Command { internal Option UrlOption { get; } + internal Option CertificateVersionOption { get; } internal Option CertificateOption { get; } internal AzureCredentialOptions AzureCredentialOptions { get; } = new(); @@ -40,6 +41,10 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s Description = AzureKeyVaultResources.CertificateOptionDescription, Required = true }; + CertificateVersionOption = new Option("--azure-key-vault-certificate-version", "-kvcv") + { + Description = AzureKeyVaultResources.CertificateVersionOptionDescription + }; FilesArgument = new Argument?>("file(s)") { Description = Resources.FilesArgumentDescription, @@ -48,6 +53,7 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s Options.Add(UrlOption); Options.Add(CertificateOption); + Options.Add(CertificateVersionOption); AzureCredentialOptions.AddOptionsToCommand(this); Arguments.Add(FilesArgument); @@ -82,9 +88,11 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s // the null-forgiving operator (!) to simplify the code. Uri url = parseResult.GetValue(UrlOption)!; string certificateId = parseResult.GetValue(CertificateOption)!; + string? certificateVersion = parseResult.GetValue(CertificateVersionOption); + string certificateVersionPath = string.IsNullOrEmpty(certificateVersion) ? string.Empty : $"/{certificateVersion}"; // Construct the URI for the certificate and the key from user parameters. We'll validate those with the SDK - var certUri = new Uri($"{url.Scheme}://{url.Authority}/certificates/{certificateId}"); + var certUri = new Uri($"{url.Scheme}://{url.Authority}/certificates/{certificateId}{certificateVersionPath}"); if (!KeyVaultCertificateIdentifier.TryCreate(certUri, out var certId)) { @@ -94,7 +102,7 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s } // The key uri is similar and the key name matches the certificate name - var keyUri = new Uri($"{url.Scheme}://{url.Authority}/keys/{certificateId}"); + var keyUri = new Uri($"{url.Scheme}://{url.Authority}/keys/{certificateId}{certificateVersionPath}"); serviceProviderFactory.AddServices(services => { @@ -112,6 +120,7 @@ internal AzureKeyVaultCommand(CodeCommand codeCommand, IServiceProviderFactory s serviceProvider.GetRequiredService(), serviceProvider.GetRequiredService(), certId.Name, + certId.Version, serviceProvider.GetRequiredService>()); }); }); diff --git a/src/Sign.Cli/AzureKeyVaultResources.Designer.cs b/src/Sign.Cli/AzureKeyVaultResources.Designer.cs index 531aa76e..bf029174 100644 --- a/src/Sign.Cli/AzureKeyVaultResources.Designer.cs +++ b/src/Sign.Cli/AzureKeyVaultResources.Designer.cs @@ -69,6 +69,12 @@ internal static string CertificateOptionDescription { } } + internal static string CertificateVersionOptionDescription { + get { + return ResourceManager.GetString("CertificateVersionOptionDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation.. /// diff --git a/src/Sign.Cli/AzureKeyVaultResources.resx b/src/Sign.Cli/AzureKeyVaultResources.resx index 6f08ccd8..4f90d119 100644 --- a/src/Sign.Cli/AzureKeyVaultResources.resx +++ b/src/Sign.Cli/AzureKeyVaultResources.resx @@ -120,6 +120,9 @@ Name of the certificate in Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.cs.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.cs.xlf index a70d7d2c..800260ab 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.cs.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.cs.xlf @@ -7,6 +7,11 @@ Název certifikátu v Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Podepisování ClickOnce prostřednictvím starší verze alternativního řešení .clickonce ZIP se už nepodporuje. Projděte si dokumentaci. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.de.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.de.xlf index 7d343694..77702148 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.de.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.de.xlf @@ -7,6 +7,11 @@ Name des Zertifikats in Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Die ClickOnce-Signierung über die Legacy-.clickonce-ZIP-Problemumgehung wird nicht mehr unterstützt. Siehe Dokumentation. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.es.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.es.xlf index 3c9608ec..ae6a84b4 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.es.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.es.xlf @@ -7,6 +7,11 @@ Nombre del certificado en Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Ya no se admite la firma ClickOnce a través de la solución zip heredada .clickonce. Consulte la documentación. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.fr.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.fr.xlf index e121c215..64476fd9 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.fr.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.fr.xlf @@ -7,6 +7,11 @@ Nom du certificat dans Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. La signature ClickOnce via la solution de contournement zip .clickonce héritée n’est plus prise en charge. Consulter la documentation. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.it.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.it.xlf index 789b87dc..aeb44f7a 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.it.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.it.xlf @@ -7,6 +7,11 @@ Nome del certificato in Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. La firma ClickOnce tramite la soluzione alternativa legacy .clickonce ZIP non è più supportata. Vedere la documentazione. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.ja.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.ja.xlf index eedd4e97..68c6e898 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.ja.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.ja.xlf @@ -7,6 +7,11 @@ Azure Key Vault 内の証明書の名前。 + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. 従来の .clickonce ZIP 回避策による ClickOnce 署名はサポートされなくなりました。ドキュメントを参照してください。 diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.ko.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.ko.xlf index 98ad3687..917fab84 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.ko.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.ko.xlf @@ -7,6 +7,11 @@ Azure Key Vault의 인증서 이름입니다. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. 레거시 .clickonce ZIP 해결 방법을 통한 ClickOnce 서명은 더 이상 지원되지 않습니다. 설명서를 참조하세요. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.pl.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.pl.xlf index 77bdf2d8..2a0390a2 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.pl.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.pl.xlf @@ -7,6 +7,11 @@ Nazwa certyfikatu w usłudze Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Podpisywanie clickOnce za pośrednictwem starszego obejścia .clickonce ZIP nie jest już obsługiwane. Zobacz dokumentację. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.pt-BR.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.pt-BR.xlf index db0e2ffc..a32f441e 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.pt-BR.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.pt-BR.xlf @@ -7,6 +7,11 @@ Nome do certificado no Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Não há mais suporte para a assinatura do ClickOnce por meio da solução alternativa .clickonce ZIP herdada. Consulte a documentação. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.ru.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.ru.xlf index 9268dbfd..bd1338f1 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.ru.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.ru.xlf @@ -7,6 +7,11 @@ Имя сертификата в Azure Key Vault. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Подписание ClickOnce с помощью устаревшего обходного пути .clickonce ZIP больше не поддерживается. См. документацию. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.tr.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.tr.xlf index a97f35d3..2b0f0dec 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.tr.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.tr.xlf @@ -7,6 +7,11 @@ Azure Key Vault’taki sertifikanın adı. + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. Eski .clickonce ZIP geçici çözümü aracılığıyla ClickOnce imzalama işlemi artık desteklenmiyor. Belgelere bakın. diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hans.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hans.xlf index fb048ffd..3abdb1d4 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hans.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hans.xlf @@ -7,6 +7,11 @@ Azure Key Vault 中的证书名称。 + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. 不再支持通过旧版 .clickonce ZIP 解决方法进行 ClickOnce 签名。参阅文档。 diff --git a/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hant.xlf b/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hant.xlf index 223505aa..ea5ed19e 100644 --- a/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hant.xlf +++ b/src/Sign.Cli/xlf/AzureKeyVaultResources.zh-Hant.xlf @@ -7,6 +7,11 @@ Azure Key Vault 中的憑證名稱。 + + Optional version of the Azure Key Vault certificate. + Optional version of the Azure Key Vault certificate. + + ClickOnce signing via the legacy .clickonce ZIP workaround is no longer supported. See documentation. 已不再支援透過舊版 .clickonce ZIP 因應措施進行 ClickOnce 簽署。請參閱文件。 diff --git a/src/Sign.SignatureProviders.KeyVault/KeyVaultService.cs b/src/Sign.SignatureProviders.KeyVault/KeyVaultService.cs index 6e418c31..0393e142 100644 --- a/src/Sign.SignatureProviders.KeyVault/KeyVaultService.cs +++ b/src/Sign.SignatureProviders.KeyVault/KeyVaultService.cs @@ -17,6 +17,7 @@ internal sealed class KeyVaultService : ISignatureAlgorithmProvider, ICertificat { private readonly CertificateClient _certificateClient; private readonly CryptographyClient _cryptographyClient; + private readonly string? _certificateVersion; private readonly string _certificateName; private readonly ILogger _logger; private readonly SemaphoreSlim _mutex = new(1); @@ -26,6 +27,7 @@ internal KeyVaultService( CertificateClient certificateClient, CryptographyClient cryptographyClient, string certificateName, + string? certificateVersion, ILogger logger) { ArgumentNullException.ThrowIfNull(certificateClient, nameof(certificateClient)); @@ -34,6 +36,7 @@ internal KeyVaultService( ArgumentNullException.ThrowIfNull(logger, nameof(logger)); _certificateName = certificateName; + _certificateVersion = certificateVersion; _certificateClient = certificateClient; _cryptographyClient = cryptographyClient; _logger = logger; @@ -63,11 +66,22 @@ public async Task GetCertificateAsync(CancellationToken cancel _logger.LogTrace(Resources.FetchingCertificate); - Response response = await _certificateClient.GetCertificateAsync(_certificateName, cancellationToken); + byte[] certificateBytes; + if (string.IsNullOrEmpty(_certificateVersion)) + { + Response response = await _certificateClient.GetCertificateAsync(_certificateName, cancellationToken); + certificateBytes = response.Value.Cer; + } + else + { + Response response = + await _certificateClient.GetCertificateVersionAsync(_certificateName, _certificateVersion, cancellationToken); + certificateBytes = response.Value.Cer; + } _logger.LogTrace(Resources.FetchedCertificate, stopwatch.Elapsed.TotalMilliseconds); - _certificate = new X509Certificate2(response.Value.Cer); + _certificate = new X509Certificate2(certificateBytes); //print the certificate info _logger.LogTrace($"{Resources.CertificateDetails}{Environment.NewLine}{_certificate.ToString(verbose: true)}"); diff --git a/test/Sign.Cli.Test/AzureKeyVaultCommandTests.cs b/test/Sign.Cli.Test/AzureKeyVaultCommandTests.cs index c7de1274..5f15bafe 100644 --- a/test/Sign.Cli.Test/AzureKeyVaultCommandTests.cs +++ b/test/Sign.Cli.Test/AzureKeyVaultCommandTests.cs @@ -42,6 +42,18 @@ public void CertificateOption_Always_IsRequired() Assert.True(_command.CertificateOption.Required); } + [Fact] + public void CertificateVersionOption_Always_HasArityOfExactlyOne() + { + Assert.Equal(ArgumentArity.ExactlyOne, _command.CertificateVersionOption.Arity); + } + + [Fact] + public void CertificateVersionOption_Always_IsOptional() + { + Assert.False(_command.CertificateVersionOption.Required); + } + [Fact] public void UrlOption_Always_HasArityOfExactlyOne() { @@ -92,6 +104,8 @@ public void Command_WhenRequiredArgumentOrOptionsAreMissing_HasError(string comm [Theory] [InlineData("code azure-key-vault -kvu https://keyvault.test -kvc a b")] [InlineData("code azure-key-vault -kvu https://keyvault.test -kvc a -kvt b -kvi c -kvs d e")] + [InlineData("code azure-key-vault -kvu https://keyvault.test -kvc a -kvcv b c")] + [InlineData("code azure-key-vault -kvu https://keyvault.test -kvc a --azure-key-vault-certificate-version b c")] public void Command_WhenRequiredArgumentsArePresent_HasNoError(string command) { ParseResult result = _rootCommand.Parse(command); @@ -99,6 +113,17 @@ public void Command_WhenRequiredArgumentsArePresent_HasNoError(string command) Assert.Empty(result.Errors); } + [Theory] + [InlineData("-kvcv")] + [InlineData("--azure-key-vault-certificate-version")] + public void Command_WhenCertificateVersionIsSpecified_ParsesCorrectly(string option) + { + ParseResult result = _rootCommand.Parse($"code azure-key-vault -kvu https://keyvault.test -kvc a {option} b c"); + + Assert.Empty(result.Errors); + Assert.Equal("b", result.GetValue(_command.CertificateVersionOption)); + } + [Theory] [InlineData("code azure-key-vault -kvu \"\" -kvc a b")] [InlineData("code azure-key-vault -kvu //keyvault.test -kvc a b")] diff --git a/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceProviderTests.cs b/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceProviderTests.cs index d79a855d..c44ca343 100644 --- a/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceProviderTests.cs +++ b/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceProviderTests.cs @@ -25,7 +25,9 @@ public KeyVaultServiceProviderTests() return new KeyVaultService( Mock.Of(), Mock.Of(), - "a", sp.GetRequiredService>()); + "a", + null, + sp.GetRequiredService>()); }); serviceProvider = services.BuildServiceProvider(); } diff --git a/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceTests.cs b/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceTests.cs index b7ba2015..951ddae5 100644 --- a/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceTests.cs +++ b/test/Sign.SignatureProviders.KeyVault.Test/KeyVaultServiceTests.cs @@ -16,6 +16,7 @@ namespace Sign.SignatureProviders.KeyVault.Test public class KeyVaultServiceTests { private const string CertificateName = "a"; + private const string CertificateVersion = "b"; private static readonly ILogger Logger = Mock.Of>(); private readonly Mock _certificateClient = new(); @@ -25,7 +26,7 @@ public class KeyVaultServiceTests public void Constructor_WhenCertificateClientIsNull_Throws() { ArgumentNullException exception = Assert.Throws( - () => new KeyVaultService(certificateClient: null!, _cryptographyClient.Object, CertificateName, Logger)); + () => new KeyVaultService(certificateClient: null!, _cryptographyClient.Object, CertificateName, null, Logger)); Assert.Equal("certificateClient", exception.ParamName); } @@ -34,7 +35,7 @@ public void Constructor_WhenCertificateClientIsNull_Throws() public void Constructor_WhenCryptographyClientIsNull_Throws() { ArgumentNullException exception = Assert.Throws( - () => new KeyVaultService(_certificateClient.Object, cryptographyClient: null!, CertificateName, Logger)); + () => new KeyVaultService(_certificateClient.Object, cryptographyClient: null!, CertificateName, null, Logger)); Assert.Equal("cryptographyClient", exception.ParamName); } @@ -43,7 +44,7 @@ public void Constructor_WhenCryptographyClientIsNull_Throws() public void Constructor_WhenCertificateNameIsNull_Throws() { ArgumentNullException exception = Assert.Throws( - () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, certificateName: null!, Logger)); + () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, certificateName: null!, null, Logger)); Assert.Equal("certificateName", exception.ParamName); } @@ -52,7 +53,7 @@ public void Constructor_WhenCertificateNameIsNull_Throws() public void Constructor_WhenCertificateNameIsEmpty_Throws() { ArgumentException exception = Assert.Throws( - () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, certificateName: string.Empty, Logger)); + () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, certificateName: string.Empty, null, Logger)); Assert.Equal("certificateName", exception.ParamName); } @@ -61,7 +62,7 @@ public void Constructor_WhenCertificateNameIsEmpty_Throws() public void Constructor_WhenLoggerIsNull_Throws() { ArgumentNullException exception = Assert.Throws( - () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, CertificateName, logger: null!)); + () => new KeyVaultService(_certificateClient.Object, _cryptographyClient.Object, CertificateName, null, logger: null!)); Assert.Equal("logger", exception.ParamName); } @@ -81,7 +82,7 @@ public async Task GetCertificateAsync_CalledTwice_CertificateRetrievedOnce() .Setup(_ => _.GetCertificateAsync(CertificateName, cancellationToken)) .ReturnsAsync(response.Object); - using KeyVaultService service = new(_certificateClient.Object, _cryptographyClient.Object, CertificateName, Logger); + using KeyVaultService service = new(_certificateClient.Object, _cryptographyClient.Object, CertificateName, null, Logger); using X509Certificate2 certificate1 = await service.GetCertificateAsync(cancellationToken); using X509Certificate2 certificate2 = await service.GetCertificateAsync(cancellationToken); @@ -89,6 +90,38 @@ public async Task GetCertificateAsync_CalledTwice_CertificateRetrievedOnce() _certificateClient.Verify(_ => _.GetCertificateAsync(CertificateName, cancellationToken), Times.Once); } + [Fact] + public async Task GetCertificateAsync_WhenCertificateVersionIsSpecified_RetrievesVersion() + { + CancellationToken cancellationToken = CancellationToken.None; + byte[] publicKey = SelfIssuedCertificateCreator.CreateCertificate().Export(X509ContentType.Cert); + KeyVaultCertificate certificate = CertificateModelFactory.KeyVaultCertificate( + new CertificateProperties(CertificateName), + cer: publicKey); + Mock> response = new(); + + response + .Setup(_ => _.Value) + .Returns(certificate); + + _certificateClient + .Setup(_ => _.GetCertificateVersionAsync(CertificateName, CertificateVersion, cancellationToken)) + .ReturnsAsync(response.Object); + + using KeyVaultService service = new( + _certificateClient.Object, + _cryptographyClient.Object, + CertificateName, + CertificateVersion, + Logger); + + using X509Certificate2 result = await service.GetCertificateAsync(cancellationToken); + + _certificateClient.Verify( + _ => _.GetCertificateVersionAsync(CertificateName, CertificateVersion, cancellationToken), + Times.Once); + } + [Fact] public async Task GetRsaAsync_ReturnsRSAKeyVaultWrapper() { @@ -109,7 +142,7 @@ public async Task GetRsaAsync_ReturnsRSAKeyVaultWrapper() .Setup(_ => _.CreateRSAAsync(cancellationToken)) .ReturnsAsync(rsaKeyVault.Object); - using KeyVaultService service = new(_certificateClient.Object, _cryptographyClient.Object, CertificateName, Logger); + using KeyVaultService service = new(_certificateClient.Object, _cryptographyClient.Object, CertificateName, null, Logger); using RSA rsa = await service.GetRsaAsync(cancellationToken);