From ef7b588f1d432e8e308d919732199e66cf96001b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brandon=F0=9F=8C=A9=EF=B8=8FH?= Date: Tue, 11 Jun 2024 16:55:49 -0700 Subject: [PATCH 1/2] Adds the capability to pass TenantId in as part of the Client credentials Also pivots to newer RsaKeyVault and Azure Identity SDK as a result --- .../AzureKeyVaultMaterializedConfiguration.cs | 17 ++++---- .../AzureKeyVaultSignConfigurationSet.cs | 1 + .../KeyVaultConfigurationDiscoverer.cs | 40 +++++-------------- src/OpenVsixSignTool/OpenVsixSignTool.csproj | 5 ++- src/OpenVsixSignTool/Program.cs | 3 +- src/OpenVsixSignTool/SignCommand.cs | 15 +++++-- 6 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/OpenVsixSignTool/AzureKeyVaultMaterializedConfiguration.cs b/src/OpenVsixSignTool/AzureKeyVaultMaterializedConfiguration.cs index 3dcc2fc..af2f500 100644 --- a/src/OpenVsixSignTool/AzureKeyVaultMaterializedConfiguration.cs +++ b/src/OpenVsixSignTool/AzureKeyVaultMaterializedConfiguration.cs @@ -1,19 +1,22 @@ -using Microsoft.Azure.KeyVault; +using System; using System.Security.Cryptography.X509Certificates; +using Azure.Identity; +using Azure.Security.KeyVault.Keys.Cryptography; + namespace OpenVsixSignTool { public class AzureKeyVaultMaterializedConfiguration { - public AzureKeyVaultMaterializedConfiguration(KeyVaultClient client, X509Certificate2 publicCertificate, KeyIdentifier keyId) + public AzureKeyVaultMaterializedConfiguration(ClientSecretCredential creds, Uri keyId, X509Certificate2 x509Certificate) { - Client = client; - KeyId = keyId; - PublicCertificate = publicCertificate; + this.Credentials = creds; + this.KeyId = keyId; + this.PublicCertificate = x509Certificate; } public X509Certificate2 PublicCertificate { get; } - public KeyVaultClient Client { get; } - public KeyIdentifier KeyId { get; } + public ClientSecretCredential Credentials { get; } + public Uri KeyId { get; } } } diff --git a/src/OpenVsixSignTool/AzureKeyVaultSignConfigurationSet.cs b/src/OpenVsixSignTool/AzureKeyVaultSignConfigurationSet.cs index e4136d1..a81ef3f 100644 --- a/src/OpenVsixSignTool/AzureKeyVaultSignConfigurationSet.cs +++ b/src/OpenVsixSignTool/AzureKeyVaultSignConfigurationSet.cs @@ -8,5 +8,6 @@ public sealed class AzureKeyVaultSignConfigurationSet public string AzureKeyVaultUrl { get; set; } public string AzureKeyVaultCertificateName { get; set; } public string AzureAccessToken { get; set; } + public string AzureTenantId { get; set; } } } diff --git a/src/OpenVsixSignTool/KeyVaultConfigurationDiscoverer.cs b/src/OpenVsixSignTool/KeyVaultConfigurationDiscoverer.cs index fdf9ef3..8433c05 100644 --- a/src/OpenVsixSignTool/KeyVaultConfigurationDiscoverer.cs +++ b/src/OpenVsixSignTool/KeyVaultConfigurationDiscoverer.cs @@ -1,42 +1,22 @@ -using Microsoft.Azure.KeyVault; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using System.Security.Cryptography.X509Certificates; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; +using Azure.Identity; +using Azure.Security.KeyVault.Certificates; +using Azure.Security.KeyVault.Keys.Cryptography; + namespace OpenVsixSignTool { internal class KeyVaultConfigurationDiscoverer { public async Task> Materialize(AzureKeyVaultSignConfigurationSet configuration) { - async Task Authenticate(string authority, string resource, string scope) - { - if (!string.IsNullOrWhiteSpace(configuration.AzureAccessToken)) - { - return configuration.AzureAccessToken; - } - - var context = new AuthenticationContext(authority); - ClientCredential credential = new ClientCredential(configuration.AzureClientId, configuration.AzureClientSecret); - - try - { - var result = await context.AcquireTokenAsync(resource, credential); - return result.AccessToken; - } - catch (AdalServiceException e) when (e.StatusCode >= 400 && e.StatusCode < 500) - { - return null; - } - } - - var vault = new KeyVaultClient(Authenticate); - var azureCertificate = await vault.GetCertificateAsync(configuration.AzureKeyVaultUrl, configuration.AzureKeyVaultCertificateName); - - var certificate = new X509Certificate2(azureCertificate.Cer); - var keyId = azureCertificate.KeyIdentifier; - return new AzureKeyVaultMaterializedConfiguration(vault, certificate, keyId); + var creds = new ClientSecretCredential(configuration.AzureTenantId, configuration.AzureClientId, configuration.AzureClientSecret); + var certClient = new CertificateClient(new System.Uri(configuration.AzureKeyVaultUrl), creds); + KeyVaultCertificateWithPolicy azureCertificate = await certClient.GetCertificateAsync(configuration.AzureKeyVaultCertificateName); + var x509Certificate = new X509Certificate2(azureCertificate.Cer); + return new AzureKeyVaultMaterializedConfiguration(creds, azureCertificate.KeyId, x509Certificate); } } } diff --git a/src/OpenVsixSignTool/OpenVsixSignTool.csproj b/src/OpenVsixSignTool/OpenVsixSignTool.csproj index 740cbea..72fdc49 100644 --- a/src/OpenVsixSignTool/OpenVsixSignTool.csproj +++ b/src/OpenVsixSignTool/OpenVsixSignTool.csproj @@ -16,11 +16,14 @@ + + + - + diff --git a/src/OpenVsixSignTool/Program.cs b/src/OpenVsixSignTool/Program.cs index 8436c59..3123abb 100644 --- a/src/OpenVsixSignTool/Program.cs +++ b/src/OpenVsixSignTool/Program.cs @@ -21,6 +21,7 @@ internal static int Main(string[] args) var file = signConfiguration.Argument("file", "A to the VSIX file."); var azureKeyVaultUrl = signConfiguration.Option("-kvu | --azure-key-vault-url", "The URL to an Azure Key Vault.", CommandOptionType.SingleValue); + var azureKeyVaultTenantId = signConfiguration.Option("-kvt | --azure-key-vault-tenant-id", "The Tenant ID to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultClientId = signConfiguration.Option("-kvi | --azure-key-vault-client-id", "The Client ID to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultClientSecret = signConfiguration.Option("-kvs | --azure-key-vault-client-secret", "The Client Secret to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultCertificateName = signConfiguration.Option("-kvc | --azure-key-vault-certificate", "The name of the certificate in Azure Key Vault.", CommandOptionType.SingleValue); @@ -35,7 +36,7 @@ internal static int Main(string[] args) } else { - return sign.SignAzure(azureKeyVaultUrl, azureKeyVaultClientId, azureKeyVaultClientSecret, + return sign.SignAzure(azureKeyVaultUrl, azureKeyVaultTenantId, azureKeyVaultClientId, azureKeyVaultClientSecret, azureKeyVaultCertificateName, azureKeyVaultAccessToken, force, fileDigest, timestamp, timestampAlgorithm, file); } }); diff --git a/src/OpenVsixSignTool/SignCommand.cs b/src/OpenVsixSignTool/SignCommand.cs index 1843372..d9a9e26 100644 --- a/src/OpenVsixSignTool/SignCommand.cs +++ b/src/OpenVsixSignTool/SignCommand.cs @@ -1,6 +1,8 @@ using Microsoft.Azure.KeyVault; using Microsoft.Extensions.CommandLineUtils; + using OpenVsixSignTool.Core; + using System; using System.IO; using System.Linq; @@ -115,7 +117,7 @@ internal Task SignAsync certificate, GetSigningKeyFromCertificate(certificate)); } - internal async Task SignAzure(CommandOption azureKeyVaultUrl, CommandOption azureKeyVaultClientId, + internal async Task SignAzure(CommandOption azureKeyVaultUrl, CommandOption azureKeyVaultTenantId, CommandOption azureKeyVaultClientId, CommandOption azureKeyVaultClientSecret, CommandOption azureKeyVaultCertificateName, CommandOption azureKeyVaultAccessToken, CommandOption force, CommandOption fileDigest, CommandOption timestampUrl, CommandOption timestampAlgorithm, CommandArgument vsixPath) { @@ -129,6 +131,12 @@ internal async Task SignAzure(CommandOption azureKeyVaultUrl, CommandOption // we only need the client id/secret if we don't have an access token if (!azureKeyVaultAccessToken.HasValue()) { + if (!azureKeyVaultTenantId.HasValue()) + { + _signCommandApplication.Out.WriteLine("The Azure Key Vault Tenant ID or Access Token must be specified for Azure signing."); + return EXIT_CODES.INVALID_OPTIONS; + } + if (!azureKeyVaultClientId.HasValue()) { _signCommandApplication.Out.WriteLine("The Azure Key Vault Client ID or Access Token must be specified for Azure signing."); @@ -192,6 +200,7 @@ internal async Task SignAzure(CommandOption azureKeyVaultUrl, CommandOption { AzureKeyVaultUrl = azureKeyVaultUrl.Value(), AzureKeyVaultCertificateName = azureKeyVaultCertificateName.Value(), + AzureTenantId = azureKeyVaultTenantId.Value(), AzureClientId = azureKeyVaultClientId.Value(), AzureAccessToken = azureKeyVaultAccessToken.Value(), AzureClientSecret = azureKeyVaultClientSecret.Value(), @@ -209,8 +218,8 @@ internal async Task SignAzure(CommandOption azureKeyVaultUrl, CommandOption _signCommandApplication.Out.WriteLine("Failed to get configuration from Azure Key Vault."); return EXIT_CODES.FAILED; } - var context = new KeyVaultContext(materialized.Client, materialized.KeyId, materialized.PublicCertificate); - using (var keyVault = new RSAKeyVault(context)) + + using (var keyVault = new RSAKeyVaultProvider.RSAKeyVault(new RSAKeyVaultProvider.KeyVaultContext(materialized.Credentials, materialized.KeyId, materialized.PublicCertificate))) { return await PerformSignOnVsixAsync( vsixPathValue, From 2f29b81c3e168f4cad3b1bde4f43cd4ef947e1e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Brandon=F0=9F=8C=A9=EF=B8=8FH?= Date: Tue, 11 Jun 2024 17:03:21 -0700 Subject: [PATCH 2/2] fixing bad ref in solution items --- OpenVsixSignTool.sln | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenVsixSignTool.sln b/OpenVsixSignTool.sln index 726787b..bc6a6db 100644 --- a/OpenVsixSignTool.sln +++ b/OpenVsixSignTool.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26403.7 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.34909.67 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenVsixSignTool.Core", "src\OpenVsixSignTool.Core\OpenVsixSignTool.Core.csproj", "{351BD833-DE64-4042-9ADE-343825443A36}" EndProject @@ -13,8 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenVsixSignTool.Tests", "t EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{49F130E1-BD5B-43AD-98C6-4CDE1AA10B2A}" ProjectSection(SolutionItems) = preProject - azure-pipelines.yml = azure-pipelines.yml Directory.Build.props = Directory.Build.props + .github\workflows\pr.yml = .github\workflows\pr.yml EndProjectSection EndProject Global