internal static int Main(string[] args) { var application = new CommandLineApplication(throwOnUnexpectedArg: false); var signCommand = application.Command("sign", throwOnUnexpectedArg: false, configuration: signConfiguration => { signConfiguration.Description = "Signs NuGet packages with the specified Key Vault certificate."; signConfiguration.HelpOption("-? | -h | --help"); var file = signConfiguration.Argument("file", "File to sign."); var fileDigestAlgorithm = signConfiguration.Option("-fd | --file-digest", "The digest algorithm to hash the file with.", CommandOptionType.SingleValue); var rfc3161TimeStamp = signConfiguration.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 = signConfiguration.Option("-td | --timestamp-digest", "Used with the -tr switch to request a digest algorithm used by the RFC 3161 timestamp server.", CommandOptionType.SingleValue); var azureKeyVaultUrl = signConfiguration.Option("-kvu | --azure-key-vault-url", "The URL to an 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); var azureKeyVaultAccessToken = signConfiguration.Option("-kva | --azure-key-vault-accesstoken", "The Access Token to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); signConfiguration.OnExecute(() => { if (string.IsNullOrWhiteSpace(file.Value)) { application.Error.WriteLine("All arguments are required"); return(Task.FromResult(-1)); } if (!azureKeyVaultUrl.HasValue()) { application.Error.WriteLine("Key Vault URL not specified"); return(Task.FromResult(-1)); } if (!azureKeyVaultCertificateName.HasValue()) { application.Error.WriteLine("Certificate name not specified"); return(Task.FromResult(-1)); } if (!rfc3161TimeStamp.HasValue()) { application.Error.WriteLine("Timestamp url not specified"); return(Task.FromResult(-1)); } var valid = (azureKeyVaultAccessToken.HasValue() || (azureKeyVaultClientId.HasValue() && azureKeyVaultClientSecret.HasValue())); if (!valid) { application.Error.WriteLine("Either access token or clientId and client secret must be specified"); return(Task.FromResult(-1)); } var sigHashAlg = GetValueFromOption(fileDigestAlgorithm, AlgorithmFromInput, HashAlgorithmName.SHA256); var timeHashAlg = GetValueFromOption(rfc3161Digest, AlgorithmFromInput, HashAlgorithmName.SHA256); var cmd = new SignCommand(application); return(cmd.SignAsync(file.Value, rfc3161TimeStamp.Value(), sigHashAlg, timeHashAlg, azureKeyVaultCertificateName.Value(), azureKeyVaultUrl.Value(), azureKeyVaultClientId.Value(), azureKeyVaultClientSecret.Value(), azureKeyVaultAccessToken.Value())); }); } ); // Verify var verifyCommand = application.Command("verify", throwOnUnexpectedArg: false, configuration: verifyConfiguration => { verifyConfiguration.Description = "Verifies NuGet packages are signed correctly"; verifyConfiguration.HelpOption("-? | -h | --help"); var file = verifyConfiguration.Argument("file", "File to sign."); verifyConfiguration.OnExecute(async() => { if (string.IsNullOrWhiteSpace(file.Value)) { application.Error.WriteLine("All arguments are required"); return(-1); } if (!File.Exists(file.Value)) { application.Error.WriteLine("File does not exist"); return(-1); } var cmd = new VerifyCommand(application); var result = await cmd.VerifyAsync(file.Value); if (result) { Console.WriteLine("Signature is valid"); } else { Console.Write("Signature is invalid"); } return(result ? 0 : -1); }); } ); application.HelpOption("-? | -h | --help"); application.VersionOption("-v | --version", typeof(Program).Assembly.GetName().Version.ToString(3)); if (args.Length == 0) { application.ShowHelp(); } return(application.Execute(args)); }
internal static int Main(string[] args) { var serviceCollection = new ServiceCollection() .AddLogging(builder => { builder.AddConsole(); }); var serviceProvider = serviceCollection.BuildServiceProvider(); var logger = serviceProvider.GetRequiredService <ILogger <Program> >(); var application = new CommandLineApplication(throwOnUnexpectedArg: false); var signCommand = application.Command("sign", throwOnUnexpectedArg: false, configuration: signConfiguration => { signConfiguration.Description = "Signs NuGet packages with the specified Key Vault certificate."; signConfiguration.HelpOption("-? | -h | --help"); var packagePath = signConfiguration.Argument("packagePath", "Package to sign."); var outputPath = signConfiguration.Option("-o | --output", "The output file. If omitted, overwrites input.", CommandOptionType.SingleValue); var force = signConfiguration.Option("-f | --force", "Overwrites a sigature if it exists.", CommandOptionType.NoValue); var fileDigestAlgorithm = signConfiguration.Option("-fd | --file-digest", "The digest algorithm to hash the file with. Default option is sha256", CommandOptionType.SingleValue); var rfc3161TimeStamp = signConfiguration.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 = signConfiguration.Option("-td | --timestamp-digest", "Used with the -tr switch to request a digest algorithm used by the RFC 3161 timestamp server. Default option is sha256", CommandOptionType.SingleValue); var signatureType = signConfiguration.Option("-st | --signature-type", "The signature type (omit for author, default. Only author is supported currently).", CommandOptionType.SingleValue); var v3ServiceIndexUrl = signConfiguration.Option("-v3si | --v3-service-index-url", "Specifies V3 Service Index Url. Required if SignatureType is Repository", CommandOptionType.SingleValue); var packageOwners = signConfiguration.Option("-own | --package-owner", "Package Owners. Required if SignatureType is Repository", CommandOptionType.MultipleValue); var azureKeyVaultUrl = signConfiguration.Option("-kvu | --azure-key-vault-url", "The URL to an 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 azureKeyVaultTenantId = signConfiguration.Option("-kvt | --azure-key-vault-tenant-id", "The Tenant Id 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); var azureKeyVaultAccessToken = signConfiguration.Option("-kva | --azure-key-vault-accesstoken", "The Access Token to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); var azureKeyVaultMsi = signConfiguration.Option("-kvm | --azure-key-vault-managed-identity", "Use a Managed Identity to access Azure Key Vault.", CommandOptionType.NoValue); signConfiguration.OnExecute(async() => { if (string.IsNullOrWhiteSpace(packagePath.Value)) { logger.LogError("Path to file(s) to sign are requried"); return(-1); } if (!azureKeyVaultUrl.HasValue()) { logger.LogError("Key Vault URL not specified"); return(-1); } if (!azureKeyVaultCertificateName.HasValue()) { logger.LogError("Certificate name not specified"); return(-1); } if (!rfc3161TimeStamp.HasValue()) { logger.LogError("Timestamp url not specified"); return(-1); } var valid = (azureKeyVaultAccessToken.HasValue() || azureKeyVaultMsi.HasValue() || (azureKeyVaultClientId.HasValue() && azureKeyVaultClientSecret.HasValue() && azureKeyVaultTenantId.HasValue())); if (!valid) { logger.LogError("Either access token or clientId, client secret, and tenant id must be specified"); return(-1); } var sigHashAlg = GetValueFromOption(fileDigestAlgorithm, AlgorithmFromInput, HashAlgorithmName.SHA256); var timeHashAlg = GetValueFromOption(rfc3161Digest, AlgorithmFromInput, HashAlgorithmName.SHA256); var sigType = GetValueFromOption(signatureType, SignatureTypeFromInput, SignatureType.Author); Uri v3ServiceIndex = null; if (sigType != SignatureType.Author) { // Check for service index and owners if (!v3ServiceIndexUrl.HasValue()) { logger.LogError("Service index url must be specified for repository signatures"); return(-1); } if (!Uri.TryCreate(v3ServiceIndexUrl.Value(), UriKind.Absolute, out v3ServiceIndex)) { logger.LogError($"Could not parse '{v3ServiceIndexUrl.Value()}' as a Uri"); return(-1); } if (!packageOwners.HasValue()) { logger.LogError("At least one package owner must be specified for repository signatures"); return(-1); } } if (!Uri.TryCreate(azureKeyVaultUrl.Value(), UriKind.Absolute, out Uri keyVaultUri)) { logger.LogError($"Could not parse '{azureKeyVaultUrl.Value()}' as a Uri"); return(-1); } var output = string.IsNullOrWhiteSpace(outputPath.Value()) ? packagePath.Value : outputPath.Value(); TokenCredential credential = null; if (azureKeyVaultMsi.HasValue()) { credential = new DefaultAzureCredential(); } else if (!string.IsNullOrWhiteSpace(azureKeyVaultAccessToken.Value())) { credential = new AccessTokenCredential(azureKeyVaultAccessToken.Value(), DateTimeOffset.UtcNow.AddHours(1)); } else { credential = new ClientSecretCredential(azureKeyVaultTenantId.Value(), azureKeyVaultClientId.Value(), azureKeyVaultClientSecret.Value()); } var cmd = new SignCommand(logger); var result = await cmd.SignAsync(packagePath.Value, output, rfc3161TimeStamp.Value(), sigHashAlg, timeHashAlg, sigType, force.HasValue(), v3ServiceIndex, packageOwners.Values, azureKeyVaultCertificateName.Value(), keyVaultUri, credential ); return(result ? 0 : -1); }); } ); // Verify var verifyCommand = application.Command("verify", throwOnUnexpectedArg: false, configuration: verifyConfiguration => { verifyConfiguration.Description = "Verifies NuGet packages are signed correctly"; verifyConfiguration.HelpOption("-? | -h | --help"); var file = verifyConfiguration.Argument("file", "File to sign."); verifyConfiguration.OnExecute(async() => { if (string.IsNullOrWhiteSpace(file.Value)) { application.Error.WriteLine("All arguments are required"); return(-1); } var cmd = new VerifyCommand(logger); var buffer = new StringBuilder(); var result = await cmd.VerifyAsync(file.Value, buffer); Console.WriteLine(buffer.ToString()); if (result) { Console.WriteLine("Signature is valid"); } else { Console.Write("Signature is invalid"); } return(result ? 0 : -1); }); } ); application.HelpOption("-? | -h | --help"); application.VersionOption("-v | --version", typeof(Program).Assembly.GetName().Version.ToString(3)); if (args.Length == 0) { application.ShowHelp(); } return(application.Execute(args)); }
internal static int Main(string[] args) { var serviceCollection = new ServiceCollection() .AddLogging(builder => { builder.AddConsole(); }); var serviceProvider = serviceCollection.BuildServiceProvider(); var logger = serviceProvider.GetRequiredService <ILogger <Program> >(); var application = new CommandLineApplication(throwOnUnexpectedArg: false); var signCommand = application.Command("sign", throwOnUnexpectedArg: false, configuration: signConfiguration => { signConfiguration.Description = "Signs NuGet packages with the specified Key Vault certificate."; signConfiguration.HelpOption("-? | -h | --help"); var packagePath = signConfiguration.Argument("packagePath", "Package to sign."); var outputPath = signConfiguration.Option("-o | --output", "The output file. If omitted, overwrites input.", CommandOptionType.SingleValue); var force = signConfiguration.Option("-f | --force", "Overwrites a sigature if it exists.", CommandOptionType.NoValue); var fileDigestAlgorithm = signConfiguration.Option("-fd | --file-digest", "The digest algorithm to hash the file with.", CommandOptionType.SingleValue); var rfc3161TimeStamp = signConfiguration.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 = signConfiguration.Option("-td | --timestamp-digest", "Used with the -tr switch to request a digest algorithm used by the RFC 3161 timestamp server.", CommandOptionType.SingleValue); var signatureType = signConfiguration.Option("-st | --signature-type", "The signature type (omit for author, default. Only author is supported currently).", CommandOptionType.SingleValue); var azureKeyVaultUrl = signConfiguration.Option("-kvu | --azure-key-vault-url", "The URL to an 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); var azureKeyVaultAccessToken = signConfiguration.Option("-kva | --azure-key-vault-accesstoken", "The Access Token to authenticate to the Azure Key Vault.", CommandOptionType.SingleValue); signConfiguration.OnExecute(async() => { if (string.IsNullOrWhiteSpace(packagePath.Value)) { logger.LogError("All arguments are required"); return(-1); } if (!azureKeyVaultUrl.HasValue()) { logger.LogError("Key Vault URL not specified"); return(-1); } if (!azureKeyVaultCertificateName.HasValue()) { logger.LogError("Certificate name not specified"); return(-1); } if (!rfc3161TimeStamp.HasValue()) { logger.LogError("Timestamp url not specified"); return(-1); } var valid = (azureKeyVaultAccessToken.HasValue() || (azureKeyVaultClientId.HasValue() && azureKeyVaultClientSecret.HasValue())); if (!valid) { logger.LogError("Either access token or clientId and client secret must be specified"); return(-1); } var sigHashAlg = GetValueFromOption(fileDigestAlgorithm, AlgorithmFromInput, HashAlgorithmName.SHA256); var timeHashAlg = GetValueFromOption(rfc3161Digest, AlgorithmFromInput, HashAlgorithmName.SHA256); var sigType = GetValueFromOption(signatureType, SignatureTypeFromInput, SignatureType.Author); if (sigType != SignatureType.Author) { logger.LogError("Only author signatures are currently supported."); return(-1); } var output = string.IsNullOrWhiteSpace(outputPath.Value()) ? packagePath.Value : outputPath.Value(); var cmd = new SignCommand(logger); var result = await cmd.SignAsync(packagePath.Value, output, rfc3161TimeStamp.Value(), sigHashAlg, timeHashAlg, sigType, force.HasValue(), azureKeyVaultCertificateName.Value(), azureKeyVaultUrl.Value(), azureKeyVaultClientId.Value(), azureKeyVaultClientSecret.Value(), azureKeyVaultAccessToken.Value()); return(result ? 0 : -1); }); } ); // Verify var verifyCommand = application.Command("verify", throwOnUnexpectedArg: false, configuration: verifyConfiguration => { verifyConfiguration.Description = "Verifies NuGet packages are signed correctly"; verifyConfiguration.HelpOption("-? | -h | --help"); var file = verifyConfiguration.Argument("file", "File to sign."); verifyConfiguration.OnExecute(async() => { if (string.IsNullOrWhiteSpace(file.Value)) { application.Error.WriteLine("All arguments are required"); return(-1); } if (!File.Exists(file.Value)) { application.Error.WriteLine("File does not exist"); return(-1); } var cmd = new VerifyCommand(logger); var buffer = new StringBuilder(); var result = await cmd.VerifyAsync(file.Value, buffer); Console.WriteLine(buffer.ToString()); if (result) { Console.WriteLine("Signature is valid"); } else { Console.Write("Signature is invalid"); } return(result ? 0 : -1); }); } ); application.HelpOption("-? | -h | --help"); application.VersionOption("-v | --version", typeof(Program).Assembly.GetName().Version.ToString(3)); if (args.Length == 0) { application.ShowHelp(); } return(application.Execute(args)); }