-
Notifications
You must be signed in to change notification settings - Fork 702
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding support for NuGet.exe sign command with fake signing #1755
Merged
mishra14
merged 15 commits into
dev-feature-signing
from
dev-anmishr-signcommand-minimal
Oct 20, 2017
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
b67c912
Adding sign command, runner and args
3bf7351
Cleaning up I/O params and adding some cert look up code
c83087c
checking in non-building code as a checkpoint
9b81d75
Cleaning up code and getting it to build
e637e9e
cleaning up the runner
8c5511a
Adding plumbing to generate test signing
87c1d71
fixing broken tests
b7a102d
fixing build break
784938f
Removing changes from signing libraries
e6c1b4b
Adjusting to fake sign
9a4510d
Adding unit tests for arg parsing
636d398
cleaning up UX and interning strings
471f234
addressing pr feedback
d1c16fc
addressing pr feedback and fixing tests
0edac8f
Addressing pr feedback
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
218 changes: 218 additions & 0 deletions
218
src/NuGet.Clients/NuGet.CommandLine/Commands/SignCommand.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,218 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Globalization; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Security; | ||
using System.Security.Cryptography; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using NuGet.Commands; | ||
using NuGet.Packaging.Signing; | ||
using NuGet.Shared; | ||
|
||
namespace NuGet.CommandLine | ||
{ | ||
[Command(typeof(NuGetCommand), "sign", "SignCommandDescription", | ||
MinArgs = 1, | ||
MaxArgs = 1, | ||
UsageSummaryResourceName = "SignCommandUsageSummary", | ||
UsageExampleResourceName = "SignCommandUsageExamples", | ||
UsageDescriptionResourceName = "SignCommandUsageDescription")] | ||
public class SignCommand : Command | ||
{ | ||
// Default constructor used only for testing, since the Command Default Constructor is protected | ||
public SignCommand() : base() | ||
{ | ||
} | ||
|
||
// List of possible values - https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/security/cryptography/HashAlgorithmName.cs | ||
private static string[] _acceptedHashAlgorithms = { "SHA256", "SHA384", "SHA512" }; | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandOutputDirectoryDescription")] | ||
public string OutputDirectory { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificatePathDescription")] | ||
public string CertificatePath { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificateStoreNameDescription")] | ||
public string CertificateStoreName { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificateStoreLocationDescription")] | ||
public string CertificateStoreLocation { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificateSubjectNameDescription")] | ||
public string CertificateSubjectName { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificateFingerprintDescription")] | ||
public string CertificateFingerprint { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCertificatePasswordDescription")] | ||
public string CertificatePassword { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandCryptographicServiceProviderDescription")] | ||
public string CryptographicServiceProvider { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandKeyContainerDescription")] | ||
public string KeyContainer { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandHashAlgorithmDescription")] | ||
public string HashAlgorithm { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandTimestamperDescription")] | ||
public string Timestamper { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandTimestampHashAlgorithmDescription")] | ||
public string TimestampHashAlgorithm { get; set; } | ||
|
||
[Option(typeof(NuGetCommand), "SignCommandOverwriteDescription")] | ||
public bool Overwrite { get; set; } | ||
|
||
public override Task ExecuteCommandAsync() | ||
{ | ||
var signArgs = GetSignArgs(); | ||
var signCommandRunner = new SignCommandRunner(); | ||
var result = signCommandRunner.ExecuteCommand(signArgs); | ||
|
||
return Task.FromResult(result); | ||
} | ||
|
||
public SignArgs GetSignArgs() | ||
{ | ||
ValidatePackagePath(); | ||
ValidateTimestamper(); | ||
ValidateCertificateInputs(); | ||
ValidateOutputDirectory(); | ||
|
||
var storeLocation = ValidateAndParseStoreLocation(); | ||
var storeName = ValidateAndParseStoreName(); | ||
var hashAlgorithm = ValidateAndParseHashAlgorithm(HashAlgorithm, nameof(HashAlgorithm)); | ||
var timestampHashAlgorithm = ValidateAndParseHashAlgorithm(TimestampHashAlgorithm, nameof(TimestampHashAlgorithm)); | ||
|
||
return new SignArgs() | ||
{ | ||
PackagePath = Arguments[0], | ||
OutputDirectory = OutputDirectory, | ||
CertificatePath = CertificatePath, | ||
CertificateStoreName = storeName, | ||
CertificateStoreLocation = storeLocation, | ||
CertificateSubjectName = CertificateSubjectName, | ||
CertificateFingerprint = CertificateFingerprint, | ||
CertificatePassword = CertificatePassword, | ||
CryptographicServiceProvider = CryptographicServiceProvider, | ||
KeyContainer = KeyContainer, | ||
HashingAlgorithm = hashAlgorithm, | ||
Logger = Console, | ||
Overwrite = Overwrite, | ||
NonInteractive = NonInteractive, | ||
Timestamper = Timestamper, | ||
TimestampHashAlgorithm = timestampHashAlgorithm | ||
}; | ||
} | ||
|
||
private HashAlgorithmName ValidateAndParseHashAlgorithm(string value, string name) | ||
{ | ||
var hashAlgorithm = HashAlgorithmName.SHA256; | ||
|
||
if (!string.IsNullOrEmpty(value)) | ||
{ | ||
if (!_acceptedHashAlgorithms.Contains(value, StringComparer.InvariantCultureIgnoreCase) || | ||
!Enum.TryParse(value, ignoreCase: true, result: out hashAlgorithm)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
NuGetCommand.SignCommandInvalidArgumentException, | ||
name)); | ||
} | ||
} | ||
|
||
return hashAlgorithm; | ||
} | ||
|
||
private StoreName ValidateAndParseStoreName() | ||
{ | ||
var storeName = StoreName.My; | ||
|
||
if (!string.IsNullOrEmpty(CertificateStoreName) && | ||
!Enum.TryParse(CertificateStoreName, ignoreCase: true, result: out storeName)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
NuGetCommand.SignCommandInvalidArgumentException, | ||
nameof(CertificateStoreName))); | ||
} | ||
|
||
return storeName; | ||
} | ||
|
||
private StoreLocation ValidateAndParseStoreLocation() | ||
{ | ||
var storeLocation = StoreLocation.CurrentUser; | ||
|
||
if (!string.IsNullOrEmpty(CertificateStoreLocation) && | ||
!Enum.TryParse(CertificateStoreLocation, ignoreCase: true, result: out storeLocation)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
NuGetCommand.SignCommandInvalidArgumentException, | ||
nameof(CertificateStoreLocation))); | ||
} | ||
|
||
return storeLocation; | ||
} | ||
|
||
private void ValidatePackagePath() | ||
{ | ||
// Assert mandatory argument | ||
if (Arguments.Count < 1 || | ||
string.IsNullOrEmpty(Arguments[0])) | ||
{ | ||
throw new ArgumentException(NuGetCommand.SignCommandNoPackageException); | ||
} | ||
} | ||
|
||
private void ValidateTimestamper() | ||
{ | ||
if (string.IsNullOrEmpty(Timestamper)) | ||
{ | ||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, | ||
NuGetCommand.SignCommandNoArgumentException, | ||
nameof(Timestamper))); | ||
} | ||
} | ||
|
||
private void ValidateOutputDirectory() | ||
{ | ||
if (!string.IsNullOrEmpty(OutputDirectory) && | ||
!Directory.Exists(OutputDirectory)) | ||
{ | ||
Directory.CreateDirectory(OutputDirectory); | ||
} | ||
} | ||
|
||
private void ValidateCertificateInputs() | ||
{ | ||
if (string.IsNullOrEmpty(CertificatePath) && | ||
string.IsNullOrEmpty(CertificateFingerprint) && | ||
string.IsNullOrEmpty(CertificateSubjectName)) | ||
{ | ||
// THrow if user gave no certificate input | ||
throw new ArgumentException(NuGetCommand.SignCommandNoCertificateException); | ||
} | ||
else if (!string.IsNullOrEmpty(CertificatePath) && | ||
((!string.IsNullOrEmpty(CertificateFingerprint) || | ||
!string.IsNullOrEmpty(CertificateSubjectName)) || | ||
!string.IsNullOrEmpty(CertificateStoreLocation) || | ||
!string.IsNullOrEmpty(CertificateStoreName))) | ||
{ | ||
// Thow if the user provided a path and any one of the other options | ||
throw new ArgumentException(NuGetCommand.SignCommandMultipleCertificateException); | ||
} | ||
else if (!string.IsNullOrEmpty(CertificateFingerprint) && !string.IsNullOrEmpty(CertificateSubjectName)) | ||
{ | ||
// Thow if the user provided a fingerprint and a subject | ||
throw new ArgumentException(NuGetCommand.SignCommandMultipleCertificateException); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it an error to specify one but not both of
CryptographicServiceProvider
andKeyContainer
?