/// <summary> /// Get error/warning chain status flags for certificate chain validation during signing. /// </summary> /// <param name="certificate">The certificate to verify.</param> /// <param name="errorStatusFlags">Error chain status flags.</param> /// <param name="warningStatusFlags">Warning chain status flags.</param> /// <exception cref="ArgumentNullException">Thrown if <paramref name="certificate" /> is <c>null</c>.</exception> public static void GetChainStatusFlagsForSigning( X509Certificate2 certificate, out X509ChainStatusFlags errorStatusFlags, out X509ChainStatusFlags warningStatusFlags) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } warningStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; if (CertificateUtility.IsSelfIssued(certificate)) { warningStatusFlags |= X509ChainStatusFlags.UntrustedRoot; } // Every status flag that isn't a warning is an error. errorStatusFlags = (~(X509ChainStatusFlags)0) & ~warningStatusFlags; }
/// <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> /// <returns>Status of trust for signature.</returns> internal virtual SignatureVerificationStatus Verify( Timestamp timestamp, SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, X509Certificate2Collection certificateExtraStore, List <SignatureLog> issues) { if (issues == null) { throw new ArgumentNullException(nameof(issues)); } settings = settings ?? SignedPackageVerifierSettings.Default; var treatIssueAsError = !settings.AllowUntrusted; var certificate = SignerInfo.Certificate; if (certificate == null) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3010, Strings.ErrorNoCertificate)); return(SignatureVerificationStatus.Invalid); } issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationAuthorCertDisplay, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(certificate, fingerprintAlgorithm)}"))); try { SignerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, Strings.ErrorSignatureVerificationFailed)); issues.Add(SignatureLog.DebugLog(e.ToString())); return(SignatureVerificationStatus.Invalid); } if (VerificationUtility.IsSigningCertificateValid(certificate, treatIssueAsError, issues)) { timestamp = timestamp ?? new Timestamp(); if (Rfc3161TimestampVerificationUtility.ValidateSignerCertificateAgainstTimestamp(certificate, timestamp)) { using (var chainHolder = new X509ChainHolder()) { var chain = chainHolder.Chain; // These flags should only be set for verification scenarios not signing chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, timestamp.UpperLimit.LocalDateTime, CertificateType.Signature); var chainBuildingSucceed = CertificateChainUtility.BuildCertificateChain(chain, certificate, out var chainStatuses); issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm))); if (chainBuildingSucceed) { return(SignatureVerificationStatus.Trusted); } var chainBuildingHasIssues = false; var statusFlags = CertificateChainUtility.DefaultObservedStatusFlags; var isSelfSignedCertificate = CertificateUtility.IsSelfIssued(certificate); if (isSelfSignedCertificate) { statusFlags &= ~X509ChainStatusFlags.UntrustedRoot; } IEnumerable <string> messages; if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, statusFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3012, message)); } chainBuildingHasIssues = true; } // 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. IEnumerable <X509ChainStatus> chainStatus = null; if (CertificateChainUtility.ChainStatusListIncludesStatus(chainStatuses, X509ChainStatusFlags.Revoked, out chainStatus)) { var status = chainStatus.First(); issues.Add(SignatureLog.Error(NuGetLogCode.NU3012, status.StatusInformation)); return(SignatureVerificationStatus.Invalid); } if (isSelfSignedCertificate && CertificateChainUtility.TryGetStatusMessage(chainStatuses, X509ChainStatusFlags.UntrustedRoot, out messages)) { issues.Add(SignatureLog.Issue(!settings.AllowUntrustedSelfIssuedCertificate, NuGetLogCode.NU3018, messages.First())); if (!chainBuildingHasIssues && settings.AllowUntrustedSelfIssuedCertificate) { return(SignatureVerificationStatus.Trusted); } } const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; if (CertificateChainUtility.TryGetStatusMessage(chainStatuses, RevocationStatusFlags, out messages)) { if (treatIssueAsError) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3018, message)); } } if (!chainBuildingHasIssues && settings.AllowUnknownRevocation) { return(SignatureVerificationStatus.Trusted); } chainBuildingHasIssues = true; } // Debug log any errors issues.Add(SignatureLog.DebugLog( string.Format( CultureInfo.CurrentCulture, Strings.ErrorInvalidCertificateChain, string.Join(", ", chainStatuses.Select(x => x.Status.ToString()))))); } } else { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3011, Strings.SignatureNotTimeValid)); } } return(SignatureVerificationStatus.Untrusted); }