in src/NuGet.Core/NuGet.Packaging/Signing/Signatures/Signature.cs [139:347]
public virtual SignatureVerificationSummary Verify(
Timestamp timestamp,
SignatureVerifySettings settings,
HashAlgorithmName fingerprintAlgorithm,
X509Certificate2Collection certificateExtraStore)
{
settings = settings ?? SignatureVerifySettings.Default;
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)));
status = settings.AllowIllegal ? SignatureVerificationStatus.Valid : SignatureVerificationStatus.Disallowed;
return new SignatureVerificationSummary(Type, status, SignatureVerificationStatusFlags.NoCertificate, issues);
}
issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture,
Strings.VerificationCertDisplay,
FriendlyName,
$"{Environment.NewLine}")));
// Debug log any errors
issues.AddRange(CertificateUtility.X509Certificate2ToLogMessages(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()));
status = settings.AllowIllegal ? SignatureVerificationStatus.Valid : SignatureVerificationStatus.Disallowed;
return new SignatureVerificationSummary(Type, status, SignatureVerificationStatusFlags.SignatureCheckFailed, issues);
}
DateTimeOffset? expirationTime = null;
var certificateFlags = VerificationUtility.ValidateSigningCertificate(certificate, !settings.AllowIllegal, FriendlyName, issues);
if (certificateFlags != SignatureVerificationStatusFlags.NoErrors)
{
status = VerificationUtility.GetSignatureVerificationStatus(certificateFlags);
return new SignatureVerificationSummary(Type, status, certificateFlags, timestamp, expirationTime, issues);
}
else
{
timestamp = timestamp ?? new Timestamp();
SignatureVerificationStatusFlags flags = SignatureVerificationStatusFlags.NoErrors;
using (X509ChainHolder chainHolder = X509ChainHolder.CreateForCodeSigning())
{
IX509Chain chain = chainHolder.Chain2;
// 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);
string x509ChainString = CertificateUtility.X509ChainToString(chain.PrivateReference, fingerprintAlgorithm);
if (!string.IsNullOrWhiteSpace(x509ChainString))
{
issues.Add(SignatureLog.DetailedLog(x509ChainString));
}
var chainBuildingHasIssues = false;
if (!chainBuildingSucceeded && chainStatuses.Length == 0)
{
return new SignatureVerificationSummary(Type, SignatureVerificationStatus.Unknown, SignatureVerificationStatusFlags.UnknownBuildStatus, timestamp, issues);
}
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)
{
SignatureUtility.LogAdditionalContext(chain, issues);
issues.Add(SignatureLog.Issue(!settings.AllowUntrusted, NuGetLogCode.NU3018, string.Format(CultureInfo.CurrentCulture, Strings.VerifyChainBuildingIssue_UntrustedRoot, FriendlyName)));
}
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;
}
}
if (!chainBuildingSucceeded)
{
// 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);
}
}