From 19156834efdad2ada5b4e833c8822037d5009bbd Mon Sep 17 00:00:00 2001 From: Tim Heuer Date: Fri, 3 May 2019 12:10:56 -0700 Subject: [PATCH 1/2] Fixes #12 by using NuGet LocalFolderUtility to parse out wildcard inputs and loop through each file to process. Verified with *.nukpg, foo*.nupkg, etc. --- .../NuGetKeyVaultSignTool.Core.csproj | 1 + NuGetKeyVaultSignTool.Core/SignCommand.cs | 45 ++++++++-------- NuGetKeyVaultSignTool.Core/VerifyCommand.cs | 51 +++++++++++-------- NuGetKeyVaultSignTool/Program.cs | 6 --- 4 files changed, 56 insertions(+), 47 deletions(-) diff --git a/NuGetKeyVaultSignTool.Core/NuGetKeyVaultSignTool.Core.csproj b/NuGetKeyVaultSignTool.Core/NuGetKeyVaultSignTool.Core.csproj index 74ee5e8..1921a24 100644 --- a/NuGetKeyVaultSignTool.Core/NuGetKeyVaultSignTool.Core.csproj +++ b/NuGetKeyVaultSignTool.Core/NuGetKeyVaultSignTool.Core.csproj @@ -9,6 +9,7 @@ + diff --git a/NuGetKeyVaultSignTool.Core/SignCommand.cs b/NuGetKeyVaultSignTool.Core/SignCommand.cs index e4a5a81..3f1a0bc 100644 --- a/NuGetKeyVaultSignTool.Core/SignCommand.cs +++ b/NuGetKeyVaultSignTool.Core/SignCommand.cs @@ -10,6 +10,7 @@ using NuGet.Common; using NuGet.Packaging.Signing; using ILogger = Microsoft.Extensions.Logging.ILogger; +using NuGet.Protocol; namespace NuGetKeyVaultSignTool { @@ -76,38 +77,42 @@ async Task Authenticate(string authority, string resource, string scope) public async Task SignAsync(string packagePath, string outputPath, string timestampUrl, HashAlgorithmName signatureHashAlgorithm, HashAlgorithmName timestampHashAlgorithm, bool overwrite, X509Certificate2 publicCertificate, System.Security.Cryptography.RSA rsa) { - var fileName = Path.GetFileName(packagePath); - logger.LogInformation($"{nameof(SignAsync)} [{fileName}]: Begin Signing {packagePath}"); + var packagesToSign = LocalFolderUtility.ResolvePackageFromPath(packagePath); + var signatureProvider = new KeyVaultSignatureProvider(rsa, new Rfc3161TimestampProvider(new Uri(timestampUrl))); var request = new AuthorSignPackageRequest(publicCertificate, signatureHashAlgorithm, timestampHashAlgorithm); string originalPackageCopyPath = null; - try + foreach (var package in packagesToSign) { - originalPackageCopyPath = CopyPackage(packagePath); - - using (var options = SigningOptions.CreateFromFilePaths(originalPackageCopyPath, outputPath, overwrite, signatureProvider, new NuGetLogger(logger, fileName))) + logger.LogInformation($"{nameof(SignAsync)} [{package}]: Begin Signing {Path.GetFileName(package)}"); + try { - await SigningUtility.SignAsync(options, request, CancellationToken.None); + originalPackageCopyPath = CopyPackage(package); + + using (var options = SigningOptions.CreateFromFilePaths(originalPackageCopyPath, package, overwrite, signatureProvider, new NuGetLogger(logger, package))) + { + await SigningUtility.SignAsync(options, request, CancellationToken.None); + } } - } - catch (Exception e) - { - logger.LogError(e, e.Message); - return false; - } - finally - { - try + catch (Exception e) { - FileUtility.Delete(originalPackageCopyPath); + logger.LogError(e, e.Message); + return false; } - catch + finally { + try + { + FileUtility.Delete(originalPackageCopyPath); + } + catch + { + } + + logger.LogInformation($"{nameof(SignAsync)} [{package}]: End Signing {Path.GetFileName(package)}"); } - - logger.LogInformation($"{nameof(SignAsync)} [{fileName}]: End Signing {packagePath}"); } return true; diff --git a/NuGetKeyVaultSignTool.Core/VerifyCommand.cs b/NuGetKeyVaultSignTool.Core/VerifyCommand.cs index 958a3a0..2a61525 100644 --- a/NuGetKeyVaultSignTool.Core/VerifyCommand.cs +++ b/NuGetKeyVaultSignTool.Core/VerifyCommand.cs @@ -13,13 +13,14 @@ using NuGet.Packaging.Signing; using Microsoft.Extensions.Logging; using ILogger = Microsoft.Extensions.Logging.ILogger; +using NuGet.Protocol; namespace NuGetKeyVaultSignTool { public class VerifyCommand { readonly ILogger logger; - + public VerifyCommand(ILogger logger) { this.logger = logger; @@ -37,44 +38,50 @@ public async Task VerifyAsync(string file, StringBuilder buffer) throw new ArgumentNullException(nameof(buffer)); } - + var trustProviders = new ISignatureVerificationProvider[] { new IntegrityVerificationProvider(), new SignatureTrustAndValidityVerificationProvider() }; var verifier = new PackageSignatureVerifier(trustProviders); + + var allPackagesVerified = true; + try { var result = 0; - using (var package = new PackageArchiveReader(file)) - { - var verificationResult = await verifier.VerifySignaturesAsync(package, SignedPackageVerifierSettings.GetVerifyCommandDefaultPolicy(), CancellationToken.None); - + var packagesToVerify = LocalFolderUtility.ResolvePackageFromPath(file); - if (verificationResult.IsValid) - { - return verificationResult.IsValid; - } - else + foreach (var packageFile in packagesToVerify) + { + using (var package = new PackageArchiveReader(packageFile)) { - var logMessages = verificationResult.Results.SelectMany(p => p.Issues).Select(p => p .AsRestoreLogMessage()).ToList(); - foreach (var msg in logMessages) + var verificationResult = await verifier.VerifySignaturesAsync(package, SignedPackageVerifierSettings.GetVerifyCommandDefaultPolicy(), CancellationToken.None); + + if (verificationResult.IsValid) { - buffer.AppendLine(msg.Message); + allPackagesVerified = true; } - if (logMessages.Any(m => m.Level >= NuGet.Common.LogLevel.Warning)) + else { - var errors = logMessages.Where(m => m.Level == NuGet.Common.LogLevel.Error).Count(); - var warnings = logMessages.Where(m => m.Level == NuGet.Common.LogLevel.Warning).Count(); + var logMessages = verificationResult.Results.SelectMany(p => p.Issues).Select(p => p.AsRestoreLogMessage()).ToList(); + foreach (var msg in logMessages) + { + buffer.AppendLine(msg.Message); + } + if (logMessages.Any(m => m.Level >= NuGet.Common.LogLevel.Warning)) + { + var errors = logMessages.Where(m => m.Level == NuGet.Common.LogLevel.Error).Count(); + var warnings = logMessages.Where(m => m.Level == NuGet.Common.LogLevel.Warning).Count(); - buffer.AppendLine($"Finished with {errors} errors and {warnings} warnings."); + buffer.AppendLine($"Finished with {errors} errors and {warnings} warnings."); - result = errors; + result = errors; + } + allPackagesVerified = false; } - return false; } - } } catch (Exception e) @@ -82,6 +89,8 @@ public async Task VerifyAsync(string file, StringBuilder buffer) logger.LogError(e, e.Message); return false; } + + return allPackagesVerified; } } } diff --git a/NuGetKeyVaultSignTool/Program.cs b/NuGetKeyVaultSignTool/Program.cs index f57ee19..96ff20f 100644 --- a/NuGetKeyVaultSignTool/Program.cs +++ b/NuGetKeyVaultSignTool/Program.cs @@ -122,12 +122,6 @@ internal static int Main(string[] args) 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); From b949917789dad849c04a0477cd73e6cc74898b3e Mon Sep 17 00:00:00 2001 From: Tim Heuer Date: Fri, 3 May 2019 12:20:50 -0700 Subject: [PATCH 2/2] Fixes #11 - Smart defaults are already there for what makes sense, just added comments what they are in the --help output. --- NuGetKeyVaultSignTool/Program.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NuGetKeyVaultSignTool/Program.cs b/NuGetKeyVaultSignTool/Program.cs index 96ff20f..55bd847 100644 --- a/NuGetKeyVaultSignTool/Program.cs +++ b/NuGetKeyVaultSignTool/Program.cs @@ -32,9 +32,9 @@ internal static int Main(string[] args) 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 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.", 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 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); @@ -46,7 +46,7 @@ internal static int Main(string[] args) { if (string.IsNullOrWhiteSpace(packagePath.Value)) { - logger.LogError("All arguments are required"); + logger.LogError("Path to file(s) to sign are requried"); return -1; }