/// <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)); } verificationFlags = SignatureVerificationStatusFlags.NoErrors; validTimestamp = null; var timestamps = Timestamps; settings = settings ?? SignedPackageVerifierSettings.GetDefault(); 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); }
internal override SignatureVerificationStatus Verify( Timestamp timestamp, SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, X509Certificate2Collection certificateExtraStore, List <SignatureLog> issues) { if (issues == null) { throw new ArgumentNullException(nameof(issues)); } settings = settings ?? SignedPackageVerifierSettings.GetDefault(); issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.SignatureType, Type.ToString()))); return(base.Verify(timestamp, settings, fingerprintAlgorithm, certificateExtraStore, issues)); }
/// <summary> /// /// </summary> /// <param name="allowMultipleTimestamps"></param> /// <param name="allowIgnoreTimestamp"></param> /// <param name="allowNoTimestamp"></param> /// <param name="allowUnknownRevocation"></param> /// <param name="issues"></param> /// <returns></returns> internal Timestamp GetValidTimestamp( SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, List <SignatureLog> issues) { if (issues == null) { throw new ArgumentNullException(nameof(issues)); } var timestamps = Timestamps; settings = settings ?? SignedPackageVerifierSettings.GetDefault(); if (timestamps.Count == 0) { issues.Add(SignatureLog.Issue(!settings.AllowNoTimestamp, NuGetLogCode.NU3027, Strings.ErrorNoTimestamp)); if (!settings.AllowNoTimestamp) { throw new TimestampException(Strings.TimestampInvalid); } } if (timestamps.Count > 1 && !settings.AllowMultipleTimestamps) { issues.Add(SignatureLog.Issue(true, NuGetLogCode.NU3000, Strings.ErrorMultipleTimestamps)); throw new TimestampException(Strings.TimestampInvalid); } var timestamp = timestamps.FirstOrDefault(); if (timestamp != null && !timestamp.Verify(this, settings, fingerprintAlgorithm, issues) && !settings.AllowIgnoreTimestamp) { throw new TimestampException(Strings.TimestampInvalid); } return(timestamp); }
/// <summary> /// Verify if the timestamp object meets the specification requirements. /// </summary> /// <param name="signature">Signature which this timestamp is for.</param> /// <param name="allowIgnoreTimestamp">Setting that tells if a timestamp can be ignored if it doesn't meet the requirements. 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="issues">List of log messages.</param> /// <returns>true if the timestamp meets the requierements, false otherwise.</returns> internal SignatureVerificationStatusFlags Verify( Signature signature, SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, List <SignatureLog> issues) { settings = settings ?? SignedPackageVerifierSettings.GetDefault(); var flags = SignatureVerificationStatusFlags.NoErrors; if (signature == null) { throw new ArgumentNullException(nameof(signature)); } if (issues == null) { throw new ArgumentNullException(nameof(issues)); } var treatIssueAsError = !settings.AllowIgnoreTimestamp; var timestamperCertificate = SignerInfo.Certificate; if (timestamperCertificate == null) { flags |= SignatureVerificationStatusFlags.NoCertificate; issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3020, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampNoCertificate, signature.FriendlyName))); return(flags); } flags |= VerificationUtility.ValidateTimestamp(this, signature, treatIssueAsError, issues, SigningSpecifications.V1); if (flags == SignatureVerificationStatusFlags.NoErrors) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, GeneralizedTime.LocalDateTime.ToString()) + Environment.NewLine)); issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationTimestamperCertDisplay, signature.FriendlyName, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate, fingerprintAlgorithm)}"))); var certificateExtraStore = SignedCms.Certificates; 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, DateTime.Now, CertificateType.Timestamp); if (settings.RevocationMode == RevocationMode.Offline) { chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline; } else { chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; } var chainBuildSucceed = CertificateChainUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList); var x509ChainString = CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm); if (!string.IsNullOrWhiteSpace(x509ChainString)) { issues.Add(SignatureLog.DetailedLog(x509ChainString)); } if (chainBuildSucceed) { return(flags); } var chainBuildingHasIssues = false; IEnumerable <string> messages; var timestampInvalidCertificateFlags = CertificateChainUtility.DefaultObservedStatusFlags; if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, timestampInvalidCertificateFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, message))); } flags |= SignatureVerificationStatusFlags.ChainBuildingFailure; 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. if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, X509ChainStatusFlags.UntrustedRoot, out messages)) { issues.Add(SignatureLog.Error(NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, messages.First()))); flags |= SignatureVerificationStatusFlags.UntrustedRoot; chainBuildingHasIssues = true; } if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, X509ChainStatusFlags.Revoked, out messages)) { issues.Add(SignatureLog.Error(NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, messages.First()))); flags |= SignatureVerificationStatusFlags.CertificateRevoked; return(flags); } var offlineRevocationErrors = CertificateChainUtility.TryGetStatusMessage(chainStatusList, X509ChainStatusFlags.OfflineRevocation, out var _); var unknownRevocationErrors = CertificateChainUtility.TryGetStatusMessage(chainStatusList, X509ChainStatusFlags.RevocationStatusUnknown, out var unknownRevocationStatusMessages); if (offlineRevocationErrors || unknownRevocationErrors) { if (treatIssueAsError) { string unknownRevocationMessage = null; if (unknownRevocationErrors) { unknownRevocationMessage = string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, unknownRevocationStatusMessages.First()); } if (settings.RevocationMode == RevocationMode.Offline) { if (offlineRevocationErrors) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampVerifyChainBuildingIssue, signature.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.VerifyError_TimestampVerifyChainBuildingIssue, signature.FriendlyName, Strings.VerifyCertTrustOfflineWhileRevocationModeOnline))); } if (unknownRevocationMessage != null) { issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3018, unknownRevocationMessage)); } } } if (!chainBuildingHasIssues && (settings.AllowIgnoreTimestamp || settings.AllowUnknownRevocation)) { return(flags); } flags |= SignatureVerificationStatusFlags.UnknownRevocation; chainBuildingHasIssues = true; } // Debug log any errors issues.Add( SignatureLog.DebugLog( string.Format( CultureInfo.CurrentCulture, $"{signature.FriendlyName}'s timestamp", Strings.VerifyError_InvalidCertificateChain, string.Join(", ", chainStatusList.Select(x => x.Status.ToString()))))); } } return(flags); }
/// <summary> /// Verify if the timestamp object meets the specification requirements. /// </summary> /// <param name="signature">Signature which this timestamp is for.</param> /// <param name="allowIgnoreTimestamp">Setting that tells if a timestamp can be ignored if it doesn't meet the requirements. 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="issues">List of log messages.</param> /// <returns>true if the timestamp meets the requierements, false otherwise.</returns> internal bool Verify( Signature signature, SignedPackageVerifierSettings settings, HashAlgorithmName fingerprintAlgorithm, List <SignatureLog> issues) { settings = settings ?? SignedPackageVerifierSettings.GetDefault(); if (signature == null) { throw new ArgumentNullException(nameof(signature)); } if (issues == null) { throw new ArgumentNullException(nameof(issues)); } var treatIssueAsError = !settings.AllowIgnoreTimestamp; var timestamperCertificate = SignerInfo.Certificate; if (timestamperCertificate == null) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); return(false); } if (VerificationUtility.IsTimestampValid(this, signature, treatIssueAsError, issues, SigningSpecifications.V1)) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, GeneralizedTime.LocalDateTime.ToString()) + Environment.NewLine)); issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationTimestamperCertDisplay, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate, fingerprintAlgorithm)}"))); var certificateExtraStore = SignedCms.Certificates; using (var chainHolder = new X509ChainHolder()) { var chain = chainHolder.Chain; // This flags should only be set for verification scenarios, not signing chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; CertificateChainUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, DateTime.Now, CertificateType.Timestamp); var chainBuildSucceed = CertificateChainUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList); issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain, fingerprintAlgorithm))); if (chainBuildSucceed) { return(true); } var chainBuildingHasIssues = false; IEnumerable <string> messages; var timestampInvalidCertificateFlags = CertificateChainUtility.DefaultObservedStatusFlags | (X509ChainStatusFlags.Revoked) | (X509ChainStatusFlags.NotTimeValid) | (X509ChainStatusFlags.CtlNotTimeValid); if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, timestampInvalidCertificateFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3028, 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. const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; if (CertificateChainUtility.TryGetStatusMessage(chainStatusList, RevocationStatusFlags, out messages)) { if (treatIssueAsError) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(!settings.AllowUnknownRevocation, NuGetLogCode.NU3028, message)); } } if (!chainBuildingHasIssues && (settings.AllowIgnoreTimestamp || settings.AllowUnknownRevocation)) { return(true); } chainBuildingHasIssues = true; } // Debug log any errors issues.Add( SignatureLog.DebugLog( string.Format( CultureInfo.CurrentCulture, Strings.ErrorInvalidCertificateChain, string.Join(", ", chainStatusList.Select(x => x.Status.ToString()))))); } } return(false); }
/// <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.GetDefault(); var treatIssueAsError = !settings.AllowIllegal; var certificate = SignerInfo.Certificate; if (certificate == null) { issues.Add(SignatureLog.Issue(treatIssueAsError, NuGetLogCode.NU3010, Strings.ErrorNoCertificate)); return(SignatureVerificationStatus.Illegal); } 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.Illegal); } 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.Valid); } 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.Suspect); } 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.Valid); } } 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.Valid); } 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.Illegal); }