Example #1
0
        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));
        }
Example #3
0
        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(),