public async Task <ErrorOr <AzureKeyVaultMaterializedConfiguration> > Materialize(AzureKeyVaultSignConfigurationSet configuration) { var authenticationFailure = false; async Task <string> 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 { _logger.LogTrace("Acquiring access token from client id"); var result = await context.AcquireTokenAsync(resource, credential); _logger.LogTrace("Acquired access token from client id"); return(result.AccessToken); } catch (AdalServiceException e) when(e.StatusCode >= 400 && e.StatusCode < 500) { authenticationFailure = true; _logger.LogError("Failed to authenticate to Azure Key Vault. Please check credentials."); return(null); } } var vault = new KeyVaultClient(Authenticate); X509Certificate2 certificate; CertificateBundle azureCertificate; try { _logger.LogTrace($"Retrieving certificate {configuration.AzureKeyVaultCertificateName}."); azureCertificate = await vault.GetCertificateAsync(configuration.AzureKeyVaultUrl, configuration.AzureKeyVaultCertificateName); _logger.LogTrace($"Retrieved certificate {configuration.AzureKeyVaultCertificateName}."); certificate = new X509Certificate2(azureCertificate.Cer); } catch (Exception e) { if (!authenticationFailure) { _logger.LogError($"Failed to retrieve certificate {configuration.AzureKeyVaultCertificateName} from Azure Key Vault. Please verify the name of the certificate and the permissions to the certificate."); } return(e); } var keyId = azureCertificate.KeyIdentifier; return(new AzureKeyVaultMaterializedConfiguration(vault, certificate, keyId)); }
public async Task <ErrorOr <AzureKeyVaultMaterializedConfiguration> > Materialize(AzureKeyVaultSignConfigurationSet configuration) { TokenCredential credential; if (configuration.ManagedIdentity) { credential = new DefaultAzureCredential(); } else if (!string.IsNullOrWhiteSpace(configuration.AzureAccessToken)) { credential = new AccessTokenCredential(configuration.AzureAccessToken); } else { credential = new ClientSecretCredential(configuration.AzureTenantId, configuration.AzureClientId, configuration.AzureClientSecret); } X509Certificate2 certificate; KeyVaultCertificateWithPolicy azureCertificate; try { var certClient = new CertificateClient(configuration.AzureKeyVaultUrl, credential); _logger.LogTrace($"Retrieving certificate {configuration.AzureKeyVaultCertificateName}."); azureCertificate = (await certClient.GetCertificateAsync(configuration.AzureKeyVaultCertificateName).ConfigureAwait(false)).Value; _logger.LogTrace($"Retrieved certificate {configuration.AzureKeyVaultCertificateName}."); certificate = new X509Certificate2(azureCertificate.Cer); } catch (Exception e) { _logger.LogError($"Failed to retrieve certificate {configuration.AzureKeyVaultCertificateName} from Azure Key Vault. Please verify the name of the certificate and the permissions to the certificate. Error message: {e.Message}."); _logger.LogTrace(e.ToString()); return(e); } var keyId = azureCertificate.KeyId; return(new AzureKeyVaultMaterializedConfiguration(credential, certificate, keyId)); }
static int Main(string[] args) { var maxLevel = LogLevel.Information; var serviceCollection = new ServiceCollection() .AddLogging(builder => { builder .AddFilter(level => level >= maxLevel) .AddConsole(options => { options.IncludeScopes = true; }); }); var serviceProvider = serviceCollection.BuildServiceProvider(); var logger = serviceProvider.GetRequiredService <ILogger <Program> >(); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.Error.WriteLine("Azure Sign Tool is only supported on Windows."); return(E_PLATFORMNOTSUPPORTED); } var application = new CommandLineApplication(throwOnUnexpectedArg: false) { Name = "azuresigntool", FullName = "Azure Sign Tool", }; var signCommand = application.Command("sign", throwOnUnexpectedArg: false, configuration: cfg => { cfg.Description = "Signs a file."; var fileDigestAlgorithm = cfg.Option("-fd | --file-digest", "The digest algorithm to hash the file with.", CommandOptionType.SingleValue); var azureKeyVaultUrl = cfg.Option("-kvu | --azure-key-vault-url", "The URL to an Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultClientId = cfg.Option("-kvi | --azure-key-vault-client-id", "The Client ID to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultClientSecret = cfg.Option("-kvs | --azure-key-vault-client-secret", "The Client Secret to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultCertificateName = cfg.Option("-kvc | --azure-key-vault-certificate", "The name of the certificate in Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultAccessToken = cfg.Option("-kva | --azure-key-vault-accesstoken", "The Access Token to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var description = cfg.Option("-d | --description", "Provide a description of the signed content.", CommandOptionType.SingleValue); var descriptionUrl = cfg.Option("-du | --description-url", "Provide a URL with more information about the signed content.", CommandOptionType.SingleValue); var rfc3161TimeStamp = cfg.Option("-tr | --timestamp-rfc3161", "Specifies the RFC 3161 timestamp server's URL. If this option (or -t) is not specified, the signed file will not be timestamped.", CommandOptionType.SingleValue); var rfc3161Digest = cfg.Option("-td | --timestamp-digest", "Used with the -tr switch to request a digest algorithm used by the RFC 3161 timestamp server.", CommandOptionType.SingleValue); var acTimeStamp = cfg.Option("-t | --timestamp-authenticode", "Specify the timestamp server's URL. If this option is not present, the signed file will not be timestamped.", CommandOptionType.SingleValue); var additionalCertificates = cfg.Option("-ac | --additional-certificates", "Specify one or more certificates to include in the public certificate chain.", CommandOptionType.MultipleValue); var verbose = cfg.Option("-v | --verbose", "Include additional output.", CommandOptionType.NoValue); var quiet = cfg.Option("-q | --quiet", "Do not print any output to the console.", CommandOptionType.NoValue); var pageHashing = cfg.Option("-ph | --page-hashing", "Generate page hashes for executable files if supported.", CommandOptionType.NoValue); var noPageHashing = cfg.Option("-nph | --no-page-hashing", "Suppress page hashes for executable files if supported.", CommandOptionType.NoValue); var continueOnError = cfg.Option("-coe | --continue-on-error", "Continue signing multiple files if an error occurs.", CommandOptionType.NoValue); var inputFileList = cfg.Option("-ifl | --input-file-list", "A path to a file that contains a list of files, one per line, to sign.", CommandOptionType.SingleValue); var maxDegreeOfParallelism = cfg.Option("-mdop | --max-degree-of-parallelism", "The maximum number of concurrent signing operations.", CommandOptionType.SingleValue); var file = cfg.Argument("file", "The path to the file.", multipleValues: true); cfg.HelpOption("-? | -h | --help"); cfg.OnExecute(async() => { X509Certificate2Collection certificates; switch (GetAdditionalCertificates(additionalCertificates.Values, logger)) { case ErrorOr <X509Certificate2Collection> .Ok d: certificates = d.Value; break; case ErrorOr <X509Certificate2Collection> .Err err: logger.LogError(err.Error, err.Error.Message); return(E_INVALIDARG); default: logger.LogInformation("Failed to include additional certificates."); return(E_INVALIDARG); } if (!CheckMutuallyExclusive(logger, quiet, verbose)) { return(E_INVALIDARG); } if (quiet.HasValue()) { maxLevel = LogLevel.Critical; } else if (verbose.HasValue()) { maxLevel = LogLevel.Trace; } if (!CheckMutuallyExclusive(logger, acTimeStamp, rfc3161TimeStamp) | !CheckRequired(logger, azureKeyVaultUrl, azureKeyVaultCertificateName) | !CheckMutuallyExclusive(logger, pageHashing, noPageHashing)) { return(E_INVALIDARG); } if (!azureKeyVaultAccessToken.HasValue() && !CheckRequired(logger, azureKeyVaultClientId, azureKeyVaultClientSecret)) { return(E_INVALIDARG); } int?signingConcurrency = null; if (maxDegreeOfParallelism.HasValue()) { if (int.TryParse(maxDegreeOfParallelism.Value(), out var maxSigningConcurrency) && (maxSigningConcurrency > 0 || maxSigningConcurrency == -1)) { signingConcurrency = maxSigningConcurrency; } else { logger.LogInformation("Value specified for --max-degree-of-parallelism is not a valid value."); return(E_INVALIDARG); } } var listOfFilesToSign = new HashSet <string>(); listOfFilesToSign.UnionWith(file.Values); if (inputFileList.HasValue()) { if (!File.Exists(inputFileList.Value())) { logger.LogInformation($"Input file list {inputFileList.Value()} does not exist."); return(E_INVALIDARG); } listOfFilesToSign.UnionWith(File.ReadAllLines(inputFileList.Value()).Where(s => !string.IsNullOrWhiteSpace(s))); } if (listOfFilesToSign.Count == 0) { logger.LogError("File or list of files is required."); return(E_INVALIDARG); } foreach (var filePath in listOfFilesToSign) { try { if (!File.Exists(filePath)) { logger.LogInformation($"File {filePath} does not exist or does not have permission."); return(E_FILE_NOT_FOUND); } } catch { logger.LogInformation($"File {filePath} does not exist or does not have permission."); return(E_FILE_NOT_FOUND); } } var configuration = new AzureKeyVaultSignConfigurationSet { AzureKeyVaultUrl = azureKeyVaultUrl.Value(), AzureKeyVaultCertificateName = azureKeyVaultCertificateName.Value(), AzureClientId = azureKeyVaultClientId.Value(), AzureAccessToken = azureKeyVaultAccessToken.Value(), AzureClientSecret = azureKeyVaultClientSecret.Value(), }; var digestAlgorithm = GetValueFromOption(fileDigestAlgorithm, AlgorithmFromInput, HashAlgorithmName.SHA256); TimeStampConfiguration timeStampConfiguration; if (rfc3161TimeStamp.HasValue()) { timeStampConfiguration = new TimeStampConfiguration( rfc3161TimeStamp.Value(), GetValueFromOption(rfc3161Digest, AlgorithmFromInput, HashAlgorithmName.SHA256), TimeStampType.RFC3161 ); } else if (acTimeStamp.HasValue()) { timeStampConfiguration = new TimeStampConfiguration( acTimeStamp.Value(),