public string GetSigningCertificateFingerprint(HashAlgorithmName algorithm) { if (!Enum.IsDefined(typeof(HashAlgorithmName), algorithm)) { throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, Strings.UnrecognizedEnumValue, algorithm), nameof(algorithm)); } var certificate = SignerInfo.Certificate; if (certificate == null) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings.Verify_ErrorNoCertificate, FriendlyName)); } if (_signingCertificateFingerprintLookup == null) { _signingCertificateFingerprintLookup = new Dictionary <HashAlgorithmName, string>(); } if (_signingCertificateFingerprintLookup.TryGetValue(algorithm, out var fingerprint)) { return(fingerprint); } fingerprint = CertificateUtility.GetHashString(certificate, algorithm); _signingCertificateFingerprintLookup.Add(algorithm, fingerprint); return(fingerprint); }
/// <summary> /// Get a valid timestamp from the unsigned attributes if present /// </summary> /// <param name="settings">Specify what is allowed in the validation for timestamp</param> /// <param name="fingerprintAlgorithm">fingerprint algorithm for displaying timestamp's certificate information</param> /// <param name="issues">List of log messages.</param> /// <param name="verificationFlags">Flags that specify the status of the verification</param> /// <param name="validTimestamp">TTimestamp found in the signature that passes validation with the given <see cref="settings"/></param> /// <remarks>If <see cref="SignedPackageVerifierSettings.AllowNoTimestamp" /> is set to true this method return true with a <see cref="validTimestamp" /> set to null.</remarks> /// <returns>true if a valid timestamp was found</returns> internal bool TryGetValidTimestamp( SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, List <SignatureLog> issues, out SignatureVerificationStatusFlags verificationFlags, out Timestamp validTimestamp) { if (issues == null) { throw new ArgumentNullException(nameof(issues)); } if (settings == null) { throw new ArgumentNullException(nameof(settings)); } verificationFlags = SignatureVerificationStatusFlags.NoErrors; validTimestamp = null; var timestamps = Timestamps; if (timestamps.Count == 0) { issues.Add(SignatureLog.Issue(!settings.AllowNoTimestamp, NuGetLogCode.NU3027, Strings.ErrorNoTimestamp)); if (!settings.AllowNoTimestamp) { verificationFlags |= SignatureVerificationStatusFlags.NoValidTimestamp; return(false); } } if (timestamps.Count > 1 && !settings.AllowMultipleTimestamps) { issues.Add(SignatureLog.Error(NuGetLogCode.NU3000, Strings.ErrorMultipleTimestamps)); verificationFlags |= SignatureVerificationStatusFlags.MultipleTimestamps; return(false); } var timestamp = timestamps.FirstOrDefault(); if (timestamp != null) { verificationFlags |= timestamp.Verify(this, settings, fingerprintAlgorithm, issues); if (verificationFlags != SignatureVerificationStatusFlags.NoErrors && verificationFlags != SignatureVerificationStatusFlags.UnknownRevocation) { return(false); } validTimestamp = timestamp; } return(true); }
public static X509CertificateWithKeyInfo GenerateCertificateWithKeyInfo( string subjectName, Action <TestCertificateGenerator> modifyGenerator, NuGet.Common.HashAlgorithmName hashAlgorithm = NuGet.Common.HashAlgorithmName.SHA256, RSASignaturePaddingMode paddingMode = RSASignaturePaddingMode.Pkcs1, int publicKeyLength = 2048, ChainCertificateRequest chainCertificateRequest = null) { var rsa = RSA.Create(publicKeyLength); var cert = GenerateCertificate(subjectName, modifyGenerator, rsa, hashAlgorithm, paddingMode, chainCertificateRequest); return(new X509CertificateWithKeyInfo(cert, rsa)); }
static string HashAlgorithmToBouncyCastle(NuGet.Common.HashAlgorithmName algorithmName) { switch (algorithmName) { case NuGet.Common.HashAlgorithmName.SHA256: return("SHA256WITHRSA"); case NuGet.Common.HashAlgorithmName.SHA384: return("SHA384WITHRSA"); case NuGet.Common.HashAlgorithmName.SHA512: return("SHA512WITHRSA"); default: throw new ArgumentOutOfRangeException(nameof(algorithmName)); } }
/// <summary> /// Create a self signed certificate with bouncy castle. /// </summary> public static X509Certificate2 GenerateCertificate( string subjectName, Action <TestCertificateGenerator> modifyGenerator, NuGet.Common.HashAlgorithmName hashAlgorithm = NuGet.Common.HashAlgorithmName.SHA256, RSASignaturePaddingMode paddingMode = RSASignaturePaddingMode.Pkcs1, int publicKeyLength = 2048, ChainCertificateRequest chainCertificateRequest = null) { chainCertificateRequest = chainCertificateRequest ?? new ChainCertificateRequest() { IsCA = true }; using (var rsa = RSA.Create(publicKeyLength)) { return(GenerateCertificate(subjectName, modifyGenerator, rsa, hashAlgorithm, paddingMode, chainCertificateRequest)); } }
public static byte[] GetHash(X509Certificate2 certificate, NuGet.Common.HashAlgorithmName hashAlgorithm) { return(hashAlgorithm.ComputeHash(certificate.RawData)); }
private static X509Certificate2 GenerateCertificate( string subjectName, Action <TestCertificateGenerator> modifyGenerator, RSA rsa, NuGet.Common.HashAlgorithmName hashAlgorithm, RSASignaturePaddingMode paddingMode, ChainCertificateRequest chainCertificateRequest) { if (string.IsNullOrEmpty(subjectName)) { subjectName = "NuGetTest"; } // Create cert var subjectDN = $"CN={subjectName}"; var certGen = new TestCertificateGenerator(); var isSelfSigned = true; X509Certificate2 issuer = null; DateTimeOffset? notAfter = null; var keyUsage = X509KeyUsageFlags.DigitalSignature; if (chainCertificateRequest == null) { // Self-signed certificates should have this flag set. keyUsage |= X509KeyUsageFlags.KeyCertSign; } else { if (chainCertificateRequest.Issuer != null) { isSelfSigned = false; // for a certificate with an issuer assign Authority Key Identifier issuer = chainCertificateRequest?.Issuer; notAfter = issuer.NotAfter.Subtract(TimeSpan.FromMinutes(5)); var publicKey = DotNetUtilities.GetRsaPublicKey(issuer.GetRSAPublicKey()); certGen.Extensions.Add( new X509Extension( Oids.AuthorityKeyIdentifier, new AuthorityKeyIdentifierStructure(publicKey).GetEncoded(), critical: false)); } if (chainCertificateRequest.ConfigureCrl) { // for a certificate in a chain create CRL distribution point extension var issuerDN = chainCertificateRequest?.Issuer?.Subject ?? subjectDN; var crlServerUri = $"{chainCertificateRequest.CrlServerBaseUri}{issuerDN}.crl"; var generalName = new Org.BouncyCastle.Asn1.X509.GeneralName(Org.BouncyCastle.Asn1.X509.GeneralName.UniformResourceIdentifier, new DerIA5String(crlServerUri)); var distPointName = new DistributionPointName(new GeneralNames(generalName)); var distPoint = new DistributionPoint(distPointName, null, null); certGen.Extensions.Add( new X509Extension( TestOids.CrlDistributionPoints, new DerSequence(distPoint).GetDerEncoded(), critical: false)); } if (chainCertificateRequest.IsCA) { // update key usage with CA cert sign and crl sign attributes keyUsage |= X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign; } } var padding = paddingMode.ToPadding(); var request = new CertificateRequest(subjectDN, rsa, hashAlgorithm.ConvertToSystemSecurityHashAlgorithmName(), padding); bool isCa = isSelfSigned ? true : (chainCertificateRequest?.IsCA ?? false); certGen.NotAfter = notAfter ?? DateTime.UtcNow.Add(TimeSpan.FromMinutes(30)); certGen.NotBefore = DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(30)); var random = new Random(); var serial = random.Next(); var serialNumber = BitConverter.GetBytes(serial); Array.Reverse(serialNumber); certGen.SetSerialNumber(serialNumber); certGen.Extensions.Add( new X509SubjectKeyIdentifierExtension(request.PublicKey, critical: false)); certGen.Extensions.Add( new X509KeyUsageExtension(keyUsage, critical: false)); certGen.Extensions.Add( new X509BasicConstraintsExtension(certificateAuthority: isCa, hasPathLengthConstraint: false, pathLengthConstraint: 0, critical: true)); // Allow changes modifyGenerator?.Invoke(certGen); foreach (var extension in certGen.Extensions) { request.CertificateExtensions.Add(extension); } X509Certificate2 certResult; if (isSelfSigned) { certResult = request.CreateSelfSigned(certGen.NotBefore, certGen.NotAfter); } else { using (var temp = request.Create(issuer, certGen.NotBefore, certGen.NotAfter, certGen.SerialNumber)) { certResult = temp.CopyWithPrivateKey(rsa); } } return(new X509Certificate2(certResult.Export(X509ContentType.Pkcs12), password: (string)null, keyStorageFlags: X509KeyStorageFlags.Exportable)); }
/// <summary> /// Verify if the signature object meets the specification trust and validity requirements. /// </summary> /// <param name="timestamp">Timestamp for this signature, if signature is not timestamped it can be null.</param> /// <param name="allowUntrusted">Setting that tells if a signature that does not meet any soft failure requirements can still be allowed. Used to know if warnings or errors should be logged for an issue.</param> /// <param name="allowUnknownRevocation">Setting that tells if unkown revocation is valid when building the chain.</param> /// <param name="allowUntrustedSelfSignedCertificate">Setting that tells if an untrusted self-signed certificate should be allowed as the signing certificate.</param> /// <param name="fingerprintAlgorithm">Algorithm used to calculate and display the certificate's fingerprint.</param> /// <param name="certificateExtraStore">Collection of certificates to help the chain building engine as an extra store.</param> /// <param name="issues">List of log messages.</param> /// <remarks>This is only public for ease of testing</remarks> /// <returns>Status of trust for signature.</returns> public virtual SignatureVerificationSummary Verify( Timestamp timestamp, SignatureVerifySettings settings, HashAlgorithmName fingerprintAlgorithm, X509Certificate2Collection certificateExtraStore) { settings = settings ?? SignatureVerifySettings.Default; var flags = SignatureVerificationStatusFlags.NoErrors; var issues = new List <SignatureLog>(); SignatureVerificationStatus status; var certificate = SignerInfo.Certificate; if (certificate == null) { issues.Add(SignatureLog.Issue(!settings.AllowIllegal, NuGetLogCode.NU3010, string.Format(CultureInfo.CurrentCulture, Strings.Verify_ErrorNoCertificate, FriendlyName))); flags |= SignatureVerificationStatusFlags.NoCertificate; status = settings.AllowIllegal ? SignatureVerificationStatus.Valid : SignatureVerificationStatus.Disallowed; return(new SignatureVerificationSummary(Type, status, flags, issues)); } issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationCertDisplay, FriendlyName, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(certificate, fingerprintAlgorithm)}"))); try { SignerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(!settings.AllowIllegal, NuGetLogCode.NU3012, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_SignatureVerificationFailed, FriendlyName))); issues.Add(SignatureLog.DebugLog(e.ToString())); flags |= SignatureVerificationStatusFlags.SignatureCheckFailed; status = settings.AllowIllegal ? SignatureVerificationStatus.Valid : SignatureVerificationStatus.Disallowed; return(new SignatureVerificationSummary(Type, status, flags, issues)); } DateTimeOffset?expirationTime = null; var certificateFlags = VerificationUtility.ValidateSigningCertificate(certificate, !settings.AllowIllegal, FriendlyName, issues); if (certificateFlags != SignatureVerificationStatusFlags.NoErrors) { flags |= certificateFlags; } else { timestamp = timestamp ?? new Timestamp(); using (var chainHolder = new X509ChainHolder()) { var chain = chainHolder.Chain; // This flag should only be set for verification scenarios, not signing. chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid; CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, timestamp.UpperLimit.LocalDateTime, CertificateType.Signature); if (settings.RevocationMode == RevocationMode.Offline) { chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline; } else { chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; } var chainBuildingSucceeded = CertificateChainUtility.BuildCertificateChain(chain, certificate, out var chainStatuses); var x509ChainString = CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm); if (!string.IsNullOrWhiteSpace(x509ChainString)) { issues.Add(SignatureLog.DetailedLog(x509ChainString)); } var chainBuildingHasIssues = false; if (!chainBuildingSucceeded) { var statusFlags = CertificateChainUtility.DefaultObservedStatusFlags; IEnumerable <string> messages; if (CertificateChainUtility.TryGetStatusAndMessage(chainStatuses, statusFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(!settings.AllowIllegal, NuGetLogCode.NU3012, string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, message))); } chainBuildingHasIssues = true; flags |= SignatureVerificationStatusFlags.ChainBuildingFailure; } // For all the special cases, chain status list only has unique elements for each chain status flag present // therefore if we are checking for one specific chain status we can use the first of the returned list // if we are combining checks for more than one, then we have to use the whole list. if (CertificateChainUtility.TryGetStatusAndMessage(chainStatuses, X509ChainStatusFlags.Revoked, out messages)) { issues.Add(SignatureLog.Error(NuGetLogCode.NU3012, string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, messages.First()))); flags |= SignatureVerificationStatusFlags.CertificateRevoked; return(new SignatureVerificationSummary(Type, SignatureVerificationStatus.Suspect, flags, timestamp, issues)); } if (CertificateChainUtility.TryGetStatusAndMessage(chainStatuses, X509ChainStatusFlags.UntrustedRoot, out messages)) { if (settings.ReportUntrustedRoot) { issues.Add(SignatureLog.Issue(!settings.AllowUntrusted, NuGetLogCode.NU3018, string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, messages.First()))); } if (!settings.AllowUntrusted) { chainBuildingHasIssues = true; flags |= SignatureVerificationStatusFlags.UntrustedRoot; } } var offlineRevocationErrors = CertificateChainUtility.TryGetStatusAndMessage(chainStatuses, X509ChainStatusFlags.OfflineRevocation, out var _); var unknownRevocationErrors = CertificateChainUtility.TryGetStatusAndMessage(chainStatuses, X509ChainStatusFlags.RevocationStatusUnknown, out var unknownRevocationStatusMessages); if (offlineRevocationErrors || unknownRevocationErrors) { if (settings.ReportUnknownRevocation) { string unknownRevocationMessage = null; if (unknownRevocationErrors) { unknownRevocationMessage = string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, unknownRevocationStatusMessages.First()); } if (settings.RevocationMode == RevocationMode.Offline) { if (offlineRevocationErrors) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, Strings.VerifyCertTrustOfflineWhileRevocationModeOffline))); } if (unknownRevocationMessage != null) { issues.Add(SignatureLog.InformationLog(unknownRevocationMessage)); } } else { if (offlineRevocationErrors) { issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3018, string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue, FriendlyName, Strings.VerifyCertTrustOfflineWhileRevocationModeOnline))); } if (unknownRevocationMessage != null) { issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3018, unknownRevocationMessage)); } } } if (!settings.AllowUnknownRevocation) { chainBuildingHasIssues = true; flags |= SignatureVerificationStatusFlags.UnknownRevocation; } } // Debug log any errors issues.Add(SignatureLog.DebugLog( string.Format( CultureInfo.CurrentCulture, Strings.VerifyError_InvalidCertificateChain, FriendlyName, string.Join(", ", chainStatuses.Select(x => x.Status.ToString()))))); } var isSignatureTimeValid = Rfc3161TimestampVerificationUtility.ValidateSignerCertificateAgainstTimestamp(certificate, timestamp); if (isSignatureTimeValid && !chainBuildingHasIssues) { return(new SignatureVerificationSummary(Type, SignatureVerificationStatus.Valid, flags, timestamp, issues)); } else if (!isSignatureTimeValid) { issues.Add( SignatureLog.Issue( !settings.AllowUntrusted, NuGetLogCode.NU3037, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_SignatureNotTimeValid, FriendlyName))); if (!settings.AllowUntrusted) { flags |= SignatureVerificationStatusFlags.CertificateExpired; } expirationTime = DateTime.SpecifyKind(certificate.NotAfter, DateTimeKind.Local); } } } status = VerificationUtility.GetSignatureVerificationStatus(flags); return(new SignatureVerificationSummary(Type, status, flags, timestamp, expirationTime, issues)); }