// Inspired from https://github.com/squaredup/bettersigntool/blob/master/bettersigntool/bettersigntool/SignCommand.cs async Task <bool> Sign(string file, SignArgs args) { var retry = TimeSpan.FromSeconds(5); var attempt = 1; do { if (attempt > 1) { logger.LogInformation($"Performing attempt #{attempt} of 3 attempts after {retry.TotalSeconds}s"); await Task.Delay(retry); } if (await RunSignTool(file, args)) { logger.LogInformation($"Signed successfully"); return(true); } attempt++; retry = TimeSpan.FromSeconds(Math.Pow(retry.TotalSeconds, 1.5)); } while (attempt <= 3); logger.LogError($"Failed to sign. Attempts exceeded"); throw new Exception($"Could not sign {file}"); }
async Task SubmitInternal(string name, IList <string> files) { logger.LogInformation("Signing NuGetKeyVaultSignTool job {0} with {1} files", name, files.Count()); var args = new SignArgs { HashAlgorithm = HashAlgorithmName.SHA256, TimestampUrl = keyVaultService.CertificateInfo.TimestampUrl, PublicCertificate = await keyVaultService.GetCertificateAsync(), Rsa = await keyVaultService.ToRSA() }; try { var tasks = files.Select(file => { telemetryLogger.OnSignFile(file, signToolName); return(Sign(file, args)); }); await Task.WhenAll(tasks); } finally { args.Rsa?.Dispose(); } }
async Task <bool> RunSignTool(string file, SignArgs args) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); logger.LogInformation("Signing using {fileName}", file); var success = false; try { success = await signCommand.SignAsync( file, file, args.TimestampUrl, args.HashAlgorithm, args.HashAlgorithm, true, args.PublicCertificate, args.Rsa ); } catch (Exception e) { logger.LogError(e, e.Message); } telemetryLogger.TrackSignToolDependency(signToolName, file, startTime, stopwatch.Elapsed, null, success ? 0 : -1); return(success); }
private TestContext(SignArgs args, TestDirectory directory, X509Certificate2 certificate, TestLogger logger) { Args = args; Directory = directory; Certificate = certificate; Runner = new SignCommandRunner(); Logger = logger; }
internal static Test Create(X509Certificate2 certificate) { var directory = TestDirectory.Create(); var packageFilePath = CreatePackage(directory, "package.nupkg"); var logger = new TestLogger(); var args = new SignArgs() { Logger = logger, NonInteractive = true, PackagePath = packageFilePath }; return(new Test(args, directory, certificate, logger)); }
internal static async Task <TestContext> CreateAsync(X509Certificate2 certificate) { var directory = TestDirectory.Create(); var packageFilePath = await CreatePackageAsync(directory, "package.nupkg"); var logger = new TestLogger(); var args = new SignArgs() { Logger = logger, NonInteractive = true, PackagePaths = new string[] { packageFilePath } }; return(new TestContext(args, directory, certificate, logger)); }
private void SignCommandArgs(Action <Mock <ISignCommandRunner>, CommandLineApplication, Func <LogLevel>, Func <SignArgs> > verify) { // Arrange var logLevel = LogLevel.Information; var logger = new TestCommandOutputLogger(); var testApp = new CommandLineApplication(); var mockCommandRunner = new Mock <ISignCommandRunner>(); SignArgs parsedArgs = null; mockCommandRunner .Setup(m => m.ExecuteCommandAsync(It.IsAny <SignArgs>())) .Callback <SignArgs>(x => parsedArgs = x) .Returns(Task.FromResult(0)); testApp.Name = "dotnet nuget_test"; SignCommand.Register(testApp, () => logger, ll => logLevel = ll, () => mockCommandRunner.Object); // Act & Assert verify(mockCommandRunner, testApp, () => logLevel, () => parsedArgs); }
internal static void Register(CommandLineApplication app, Func <ILogger> getLogger, Action <LogLevel> setLogLevel, Func <ISignCommandRunner> getCommandRunner) { app.Command(CommandName, signCmd => { CommandArgument packagePaths = signCmd.Argument( "<package-paths>", Strings.SignCommandPackagePathDescription, multipleValues: true); CommandOption outputDirectory = signCmd.Option( "-o|--output", Strings.SignCommandOutputDirectoryDescription, CommandOptionType.SingleValue); CommandOption path = signCmd.Option( "--certificate-path", Strings.SignCommandCertificatePathDescription, CommandOptionType.SingleValue); CommandOption store = signCmd.Option( "--certificate-store-name", Strings.SignCommandCertificateStoreNameDescription, CommandOptionType.SingleValue); CommandOption location = signCmd.Option( "--certificate-store-location", Strings.SignCommandCertificateStoreLocationDescription, CommandOptionType.SingleValue); CommandOption subject = signCmd.Option( "--certificate-subject-name", Strings.SignCommandCertificateSubjectNameDescription, CommandOptionType.SingleValue); CommandOption fingerprint = signCmd.Option( "--certificate-fingerprint", Strings.SignCommandCertificateFingerprintDescription, CommandOptionType.SingleValue); CommandOption password = signCmd.Option( "--certificate-password", Strings.SignCommandCertificatePasswordDescription, CommandOptionType.SingleValue); CommandOption algorithm = signCmd.Option( "--hash-algorithm", Strings.SignCommandHashAlgorithmDescription, CommandOptionType.SingleValue); CommandOption timestamper = signCmd.Option( "--timestamper", Strings.SignCommandTimestamperDescription, CommandOptionType.SingleValue); CommandOption timestamperAlgorithm = signCmd.Option( "--timestamp-hash-algorithm", Strings.SignCommandTimestampHashAlgorithmDescription, CommandOptionType.SingleValue); CommandOption overwrite = signCmd.Option( "--overwrite", Strings.SignCommandOverwriteDescription, CommandOptionType.NoValue); CommandOption verbosity = signCmd.Option( "-v|--verbosity", Strings.Verbosity_Description, CommandOptionType.SingleValue); signCmd.HelpOption(XPlatUtility.HelpOption); signCmd.Description = Strings.SignCommandDescription; signCmd.OnExecute(async() => { ILogger logger = getLogger(); ValidatePackagePaths(packagePaths); WarnIfNoTimestamper(logger, timestamper); ValidateCertificateInputs(path, fingerprint, subject, store, location); ValidateAndCreateOutputDirectory(outputDirectory); SigningSpecificationsV1 signingSpec = SigningSpecifications.V1; StoreLocation storeLocation = ValidateAndParseStoreLocation(location); StoreName storeName = ValidateAndParseStoreName(store); HashAlgorithmName hashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(algorithm.Value(), algorithm.LongName, signingSpec); HashAlgorithmName timestampHashAlgorithm = CommandLineUtility.ParseAndValidateHashAlgorithm(timestamperAlgorithm.Value(), timestamperAlgorithm.LongName, signingSpec); var args = new SignArgs() { PackagePaths = packagePaths.Values, OutputDirectory = outputDirectory.Value(), CertificatePath = path.Value(), CertificateStoreName = storeName, CertificateStoreLocation = storeLocation, CertificateSubjectName = subject.Value(), CertificateFingerprint = fingerprint.Value(), CertificatePassword = password.Value(), SignatureHashAlgorithm = hashAlgorithm, Logger = logger, Overwrite = overwrite.HasValue(), //The interactive option is not enabled at first, so the NonInteractive is always set to true. This is tracked by https://github.com/NuGet/Home/issues/10620 NonInteractive = true, Timestamper = timestamper.Value(), TimestampHashAlgorithm = timestampHashAlgorithm }; setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value())); ISignCommandRunner runner = getCommandRunner(); int result = await runner.ExecuteCommandAsync(args); return(result); }); }); }