Example #1
0
        public void ShouldSignExeWithRSASigningCertificates_Sha256FileDigest_WithTimestamps(string certificate)
        {
            var signingCert     = new X509Certificate2(certificate, "test", X509KeyStorageFlags.EphemeralKeySet);
            var timestampConfig = new TimeStampConfiguration("http://timestamp.digicert.com", HashAlgorithmName.SHA256, TimeStampType.RFC3161);
            var signer          = new AuthenticodeKeyVaultSigner(signingCert.GetRSAPrivateKey(), signingCert, HashAlgorithmName.SHA256, timestampConfig);
            var fileToSign      = GetFileToSign();
            var result          = signer.SignFile(fileToSign, null, null, null);

            Assert.Equal(0, result);
        }
Example #2
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(),