public static void Verify(SignPackageRequest request, ILogger logger) { if (request == null) { throw new ArgumentNullException(nameof(request)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } if (!CertificateUtility.IsSignatureAlgorithmSupported(request.Certificate)) { throw new SignatureException(NuGetLogCode.NU3013, Strings.SigningCertificateHasUnsupportedSignatureAlgorithm); } if (!CertificateUtility.IsCertificatePublicKeyValid(request.Certificate)) { throw new SignatureException(NuGetLogCode.NU3014, Strings.SigningCertificateFailsPublicKeyLengthRequirement); } if (CertificateUtility.HasExtendedKeyUsage(request.Certificate, Oids.LifetimeSigningEku)) { throw new SignatureException(NuGetLogCode.NU3015, Strings.ErrorCertificateHasLifetimeSigningEKU); } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(request.Certificate)) { throw new SignatureException(NuGetLogCode.NU3017, Strings.SignatureNotYetValid); } request.BuildSigningCertificateChainOnce(logger); }
internal static bool IsSigningCertificateValid(X509Certificate2 certificate, bool treatIssuesAsErrors, List <SignatureLog> issues) { var isValid = true; if (!CertificateUtility.IsSignatureAlgorithmSupported(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3013, Strings.SigningCertificateHasUnsupportedSignatureAlgorithm)); isValid = false; } if (!CertificateUtility.IsCertificatePublicKeyValid(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3014, Strings.SigningCertificateFailsPublicKeyLengthRequirement)); isValid = false; } if (CertificateUtility.HasExtendedKeyUsage(certificate, Oids.LifetimeSigningEku)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3015, Strings.ErrorCertificateHasLifetimeSigningEKU)); isValid = false; } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3017, Strings.SignatureNotYetValid)); isValid = false; } return(isValid); }
private static bool IsMatch( X509Certificate2 certificate, EssCertIdV2 essCertIdV2, Errors errors, bool isIssuerSerialRequired) { if (isIssuerSerialRequired) { if (essCertIdV2.IssuerSerial == null || essCertIdV2.IssuerSerial.GeneralNames.Count == 0) { throw new SignatureException(errors.InvalidSignature, errors.InvalidSignatureString); } } if (essCertIdV2.IssuerSerial != null) { if (!AreSerialNumbersEqual(essCertIdV2.IssuerSerial, certificate)) { return(false); } if (!AreGeneralNamesEqual(essCertIdV2.IssuerSerial, certificate)) { return(false); } } var hashAlgorithmName = CryptoHashUtility.OidToHashAlgorithmName(essCertIdV2.HashAlgorithm.Algorithm.Value); var actualHash = CertificateUtility.GetHash(certificate, hashAlgorithmName); return(essCertIdV2.CertificateHash.SequenceEqual(actualHash)); }
private Signature CreateSignature(SignPackageRequest request, SignatureContent signatureContent) { var cmsSigner = CreateCmsSigner(request); if (request.PrivateKey != null) { return(CreateSignature(cmsSigner, signatureContent, request.PrivateKey)); } var contentInfo = new ContentInfo(signatureContent.GetBytes()); var cms = new SignedCms(contentInfo); try { cms.ComputeSignature(cmsSigner); } catch (CryptographicException ex) when(ex.HResult == INVALID_PROVIDER_TYPE_HRESULT) { var exceptionBuilder = new StringBuilder(); exceptionBuilder.AppendLine(Strings.SignFailureCertificateInvalidProviderType); exceptionBuilder.AppendLine(CertificateUtility.X509Certificate2ToString(request.Certificate)); throw new SignatureException(NuGetLogCode.NU3016, exceptionBuilder.ToString()); } return(Signature.Load(cms)); }
private static void ValidateTimestampCms(SigningSpecifications spec, SignedCms timestampCms) { var signerInfo = timestampCms.SignerInfos[0]; try { signerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { throw new TimestampException(NuGetLogCode.NU3021, Strings.TimestampSignatureValidationFailed, e); } if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate)) { throw new TimestampException(NuGetLogCode.NU3022, Strings.TimestampUnsupportedSignatureAlgorithm); } if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate)) { throw new TimestampException(NuGetLogCode.NU3023, Strings.TimestampCertificateFailsPublicKeyLengthRequirement); } if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value)) { throw new TimestampException(NuGetLogCode.NU3024, Strings.TimestampUnsupportedSignatureAlgorithm); } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { throw new TimestampException(NuGetLogCode.NU3025, Strings.TimestampNotYetValid); } }
private static PrimarySignature CreatePrimarySignature(CmsSigner cmsSigner, SignPackageRequest request, byte[] signingData) { var contentInfo = new ContentInfo(signingData); var cms = new SignedCms(contentInfo); try { #if IS_DESKTOP cms.ComputeSignature(cmsSigner); #else // In .NET Framework, this parameter is not used and a PIN prompt is always shown. In .NET Core, the silent flag needs to be set to false to show a PIN prompt. cms.ComputeSignature(cmsSigner, silent: false); #endif } catch (CryptographicException ex) when(ex.HResult == INVALID_PROVIDER_TYPE_HRESULT) { var exceptionBuilder = new StringBuilder(); exceptionBuilder.AppendLine(Strings.SignFailureCertificateInvalidProviderType); exceptionBuilder.AppendLine(CertificateUtility.X509Certificate2ToString(request.Certificate, Common.HashAlgorithmName.SHA256)); throw new SignatureException(NuGetLogCode.NU3001, exceptionBuilder.ToString()); } return(PrimarySignature.Load(cms)); }
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); }
internal static bool IsTimestampValid(Timestamp timestamp, byte[] data, bool treatIssuesAsErrors, List <SignatureLog> issues, SigningSpecifications spec) { var isValid = true; var signerInfo = timestamp.SignerInfo; if (!timestamp.TstInfo.HasMessageHash(data)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3019, Strings.TimestampIntegrityCheckFailed)); isValid = false; } if (timestamp.SignerInfo.Certificate != null) { try { signerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3021, Strings.TimestampSignatureValidationFailed)); issues.Add(SignatureLog.DebugLog(e.ToString())); isValid = false; } if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3022, Strings.TimestampUnsupportedSignatureAlgorithm)); isValid = false; } if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3023, Strings.TimestampCertificateFailsPublicKeyLengthRequirement)); isValid = false; } if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3024, Strings.TimestampUnsupportedSignatureAlgorithm)); isValid = false; } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3025, Strings.TimestampNotYetValid)); isValid = false; } } else { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); isValid = false; } return(isValid); }
public static EssCertIdV2 Create(X509Certificate2 certificate, Common.HashAlgorithmName hashAlgorithmName) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } var algorithm = new AlgorithmIdentifier(hashAlgorithmName.ConvertToOid()); var hash = CertificateUtility.GetHash(certificate, hashAlgorithmName); var issuerSerial = IssuerSerial.Create(certificate); return(new EssCertIdV2(algorithm, hash, issuerSerial)); }
private static string GetCertificateFingerprint( Signature signature, HashAlgorithmName fingerprintAlgorithm, IDictionary <HashAlgorithmName, string> CertificateFingerprintLookUp) { if (!CertificateFingerprintLookUp.TryGetValue(fingerprintAlgorithm, out var fingerprintString)) { fingerprintString = CertificateUtility.GetHashString(signature.SignerInfo.Certificate, fingerprintAlgorithm); CertificateFingerprintLookUp[fingerprintAlgorithm] = fingerprintString; } return(fingerprintString); }
private static string GetCertificateFingerprint( Signature signature, HashAlgorithmName fingerprintAlgorithm, IDictionary <HashAlgorithmName, string> CertificateFingerprintLookUp) { if (!CertificateFingerprintLookUp.TryGetValue(fingerprintAlgorithm, out var fingerprintString)) { var primarySignatureCertificateFingerprint = CertificateUtility.GetHash(signature.SignerInfo.Certificate, fingerprintAlgorithm); fingerprintString = BitConverter.ToString(primarySignatureCertificateFingerprint).Replace("-", ""); CertificateFingerprintLookUp[fingerprintAlgorithm] = fingerprintString; } return(fingerprintString); }
internal unsafe void AddCountersignature(CmsSigner cmsSigner, CngKey privateKey) { using (var hb = new HeapBlockRetainer()) { var signerInfo = NativeUtility.CreateSignerInfo(cmsSigner, privateKey, hb); NativeUtility.ThrowIfFailed(NativeMethods.CryptMsgCountersign( _handle, dwIndex: 0, cCountersigners: 1, rgCountersigners: signerInfo)); AddCertificates(CertificateUtility.GetRawDataForCollection(cmsSigner.Certificates)); } }
internal static bool BuildCertificateChain(X509Chain chain, X509Certificate2 certificate, out X509ChainStatus[] status) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } var buildSuccess = chain.Build(certificate); status = new X509ChainStatus[chain.ChainStatus.Length]; chain.ChainStatus.CopyTo(status, 0); // Check if time is not in the future return(buildSuccess && !CertificateUtility.IsCertificateValidityPeriodInTheFuture(certificate)); }
/// <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; }
private static PrimarySignature CreatePrimarySignature(CmsSigner cmsSigner, SignPackageRequest request, byte[] signingData) { var contentInfo = new ContentInfo(signingData); var cms = new SignedCms(contentInfo); try { cms.ComputeSignature(cmsSigner); } catch (CryptographicException ex) when(ex.HResult == INVALID_PROVIDER_TYPE_HRESULT) { var exceptionBuilder = new StringBuilder(); exceptionBuilder.AppendLine(Strings.SignFailureCertificateInvalidProviderType); exceptionBuilder.AppendLine(CertificateUtility.X509Certificate2ToString(request.Certificate, Common.HashAlgorithmName.SHA256)); throw new SignatureException(NuGetLogCode.NU3001, exceptionBuilder.ToString()); } return(PrimarySignature.Load(cms)); }
internal static SignatureVerificationStatusFlags ValidateSigningCertificate(X509Certificate2 certificate, bool treatIssuesAsErrors, string signatureFriendlyName, List <SignatureLog> issues) { if (certificate == null) { throw new ArgumentNullException(nameof(certificate)); } if (issues == null) { throw new ArgumentNullException(nameof(issues)); } var validationFlags = SignatureVerificationStatusFlags.NoErrors; if (!CertificateUtility.IsSignatureAlgorithmSupported(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3013, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_CertificateHasUnsupportedSignatureAlgorithm, signatureFriendlyName))); validationFlags |= SignatureVerificationStatusFlags.SignatureAlgorithmUnsupported; } if (!CertificateUtility.IsCertificatePublicKeyValid(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3014, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_CertificateFailsPublicKeyLengthRequirement, signatureFriendlyName))); validationFlags |= SignatureVerificationStatusFlags.CertificatePublicKeyInvalid; } if (CertificateUtility.HasExtendedKeyUsage(certificate, Oids.LifetimeSigningEku)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3015, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_CertificateHasLifetimeSigningEKU, signatureFriendlyName))); validationFlags |= SignatureVerificationStatusFlags.HasLifetimeSigningEku; } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3017, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_CertificateNotYetValid, signatureFriendlyName))); validationFlags |= SignatureVerificationStatusFlags.CertificateValidityInTheFuture; } return(validationFlags); }
private bool IsSignatureAllowed(Signature signature) { // Get information needed for allow list verification var primarySignatureCertificateFingerprint = CertificateUtility.GetHash(signature.SignerInfo.Certificate, _fingerprintAlgorithm); var primarySignatureCertificateFingerprintString = BitConverter.ToString(primarySignatureCertificateFingerprint).Replace("-", ""); foreach (var allowedEntry in _allowList) { // Verify the certificate hash allow list objects var certificateHashEntry = allowedEntry as CertificateHashAllowListEntry; if (certificateHashEntry != null) { if (certificateHashEntry.VerificationTarget.HasFlag(VerificationTarget.Primary) && StringComparer.OrdinalIgnoreCase.Equals(certificateHashEntry.CertificateFingerprint, primarySignatureCertificateFingerprintString)) { return(true); } } } return(false); }
private static PrimarySignature CreateRepositoryCountersignature(CmsSigner cmsSigner, SignPackageRequest request, PrimarySignature primarySignature) { var cms = new SignedCms(); cms.Decode(primarySignature.GetBytes()); try { cms.SignerInfos[0].ComputeCounterSignature(cmsSigner); } catch (CryptographicException ex) when(ex.HResult == INVALID_PROVIDER_TYPE_HRESULT) { var exceptionBuilder = new StringBuilder(); exceptionBuilder.AppendLine(Strings.SignFailureCertificateInvalidProviderType); exceptionBuilder.AppendLine(CertificateUtility.X509Certificate2ToString(request.Certificate, Common.HashAlgorithmName.SHA256)); throw new SignatureException(NuGetLogCode.NU3001, exceptionBuilder.ToString()); } return(PrimarySignature.Load(cms)); }
/// <summary> /// Timestamps data present in the TimestampRequest. /// </summary> public byte[] TimestampData(TimestampRequest request, ILogger logger, CancellationToken token) { token.ThrowIfCancellationRequested(); if (request == null) { throw new ArgumentNullException(nameof(request)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } // Get the signatureValue from the signerInfo object using (var signatureNativeCms = NativeCms.Decode(request.SignatureValue, detached: false)) { var signatureValueHashByteArray = NativeCms.GetSignatureValueHash( request.TimestampHashAlgorithm, signatureNativeCms); // Allows us to track the request. var nonce = GenerateNonce(); var rfc3161TimestampRequest = new Rfc3161TimestampRequest( signatureValueHashByteArray, request.TimestampHashAlgorithm.ConvertToSystemSecurityHashAlgorithmName(), nonce: nonce, requestSignerCertificates: true); // Request a timestamp // The response status need not be checked here as lower level api will throw if the response is invalid var timestampToken = rfc3161TimestampRequest.SubmitRequest( _timestamperUrl, TimeSpan.FromSeconds(_rfc3161RequestTimeoutSeconds)); // ensure response is for this request ValidateTimestampResponseNonce(nonce, timestampToken); var timestampCms = timestampToken.AsSignedCms(); byte[] timestampByteArray; using (var timestampNativeCms = NativeCms.Decode(timestampCms.Encode(), detached: false)) using (var timestampCertChain = new X509Chain()) { var policy = timestampCertChain.ChainPolicy; policy.ApplicationPolicy.Add(new Oid(Oids.TimeStampingEkuOid)); policy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid; policy.ExtraStore.AddRange(timestampCms.Certificates); policy.RevocationFlag = X509RevocationFlag.ExcludeRoot; policy.RevocationMode = X509RevocationMode.Online; var timestampSignerCertificate = GetTimestampSignerCertificate(timestampCms); if (DateTime.Now < timestampSignerCertificate.NotBefore) { throw new TimestampException(LogMessage.CreateError( NuGetLogCode.NU3025, string.Format(CultureInfo.CurrentCulture, Strings.TimestampCertificateInvalid, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestampSignerCertificate)}"))); } if (!timestampCertChain.Build(timestampSignerCertificate)) { throw new TimestampException(LogMessage.CreateError( NuGetLogCode.NU3028, string.Format(CultureInfo.CurrentCulture, Strings.TimestampCertificateChainBuildFailure, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestampSignerCertificate)}"))); } // Insert all the certificates into timestampCms InsertTimestampCertChainIntoTimestampCms(timestampCms, timestampCertChain, timestampNativeCms); timestampByteArray = timestampCms.Encode(); } signatureNativeCms.AddTimestamp(timestampByteArray); return(signatureNativeCms.Encode()); } }
/// <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.TryGetStatusMessage(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.TryGetStatusMessage(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.TryGetStatusMessage(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.TryGetStatusMessage(chainStatuses, X509ChainStatusFlags.OfflineRevocation, out var _); var unknownRevocationErrors = CertificateChainUtility.TryGetStatusMessage(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)); }
/// <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); }
private SignatureVerificationStatus VerifySignature(Signature signature, Timestamp timestamp, bool failuresAreFatal, List <SignatureLog> issues) { var certificate = signature.SignerInfo.Certificate; if (certificate != null) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationAuthorCertDisplay, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(certificate)}"))); try { signature.SignerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3012, Strings.ErrorSignatureVerificationFailed)); issues.Add(SignatureLog.DebugLog(e.ToString())); return(SignatureVerificationStatus.Invalid); } if (!SigningUtility.IsCertificateValidityPeriodInTheFuture(certificate)) { timestamp = timestamp ?? new Timestamp(); if (Rfc3161TimestampVerificationUtility.ValidateSignerCertificateAgainstTimestamp(certificate, timestamp)) { // Read signed attribute containing the original cert hashes // var signingCertificateAttribute = signature.SignerInfo.SignedAttributes.GetAttributeOrDefault(Oids.SigningCertificateV2); // TODO: how are we going to use the signingCertificateAttribute? var certificateExtraStore = signature.SignedCms.Certificates; using (var chain = new X509Chain()) { // This flags should only be set for verification scenarios, not signing chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; SigningUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, timestamp.UpperLimit.LocalDateTime, NuGetVerificationCertificateType.Signature); var chainBuildingSucceed = SigningUtility.BuildCertificateChain(chain, certificate, out var chainStatusList); issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain))); if (chainBuildingSucceed) { return(SignatureVerificationStatus.Trusted); } var chainBuildingHasIssues = false; IReadOnlyList <string> messages; if (SigningUtility.TryGetStatusMessage(chainStatusList, SigningUtility.NotIgnoredCertificateFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3018, 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. IReadOnlyList <X509ChainStatus> chainStatus = null; if (SigningUtility.ChainStatusListIncludesStatus(chainStatusList, X509ChainStatusFlags.Revoked, out chainStatus)) { var status = chainStatus.First(); issues.Add(SignatureLog.Issue(true, NuGetLogCode.NU3018, status.StatusInformation)); return(SignatureVerificationStatus.Invalid); } const X509ChainStatusFlags RevocationStatusFlags = X509ChainStatusFlags.RevocationStatusUnknown | X509ChainStatusFlags.OfflineRevocation; if (SigningUtility.TryGetStatusMessage(chainStatusList, RevocationStatusFlags, out messages)) { if (failuresAreFatal) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3018, message)); } } else if (!chainBuildingHasIssues) { return(SignatureVerificationStatus.Trusted); } chainBuildingHasIssues = true; } // Debug log any errors issues.Add(SignatureLog.DebugLog(string.Format(CultureInfo.CurrentCulture, Strings.ErrorInvalidCertificateChain, string.Join(", ", chainStatusList.Select(x => x.ToString()))))); } } else { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3012, Strings.ErrorSignatureVerificationFailed)); } } else { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3017, Strings.SignatureNotYetValid)); } } else { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3010, Strings.ErrorNoCertificate)); } return(SignatureVerificationStatus.Untrusted); }
/// <summary> /// Validates a SignedCms object containing a timestamp response. /// </summary> /// <param name="timestampCms">SignedCms response from the timestamp authority.</param> /// <param name="messageHash">Message hash that was signed and timestamped.</param> private bool IsTimestampValid(Timestamp timestamp, byte[] messageHash, bool failuresAreFatal, List <SignatureLog> issues) { var timestamperCertificate = timestamp.SignerInfo.Certificate; if (timestamperCertificate == null) { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); return(false); } if (SigningUtility.IsTimestampValid(timestamp, messageHash, failuresAreFatal, issues, _specification)) { issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.TimestampValue, timestamp.GeneralizedTime.LocalDateTime.ToString()) + Environment.NewLine)); issues.Add(SignatureLog.InformationLog(string.Format(CultureInfo.CurrentCulture, Strings.VerificationTimestamperCertDisplay, $"{Environment.NewLine}{CertificateUtility.X509Certificate2ToString(timestamperCertificate)}"))); //var signingCertificateAttribute = timestampSignerInfo.SignedAttributes.GetAttributeOrDefault(Oids.SigningCertificate); //if (signingCertificateAttribute == null) //{ // signingCertificateAttribute = timestampSignerInfo.SignedAttributes.GetAttributeOrDefault(Oids.SigningCertificateV2); //} // TODO: how are we going to use the signingCertificateAttribute? var certificateExtraStore = timestamp.SignedCms.Certificates; using (var chain = new X509Chain()) { // This flags should only be set for verification scenarios, not signing chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreNotTimeValid | X509VerificationFlags.IgnoreCtlNotTimeValid; SigningUtility.SetCertBuildChainPolicy(chain.ChainPolicy, certificateExtraStore, DateTime.Now, NuGetVerificationCertificateType.Timestamp); var chainBuildSucceed = SigningUtility.BuildCertificateChain(chain, timestamperCertificate, out var chainStatusList); issues.Add(SignatureLog.DetailedLog(CertificateUtility.X509ChainToString(chain))); if (chainBuildSucceed) { return(true); } var chainBuildingHasIssues = false; IReadOnlyList <string> messages; var timestampInvalidCertificateFlags = SigningUtility.NotIgnoredCertificateFlags | (X509ChainStatusFlags.Revoked) | (X509ChainStatusFlags.NotTimeValid) | (X509ChainStatusFlags.CtlNotTimeValid); if (SigningUtility.TryGetStatusMessage(chainStatusList, timestampInvalidCertificateFlags, out messages)) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(failuresAreFatal, 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 (SigningUtility.TryGetStatusMessage(chainStatusList, RevocationStatusFlags, out messages)) { if (failuresAreFatal) { foreach (var message in messages) { issues.Add(SignatureLog.Issue(failuresAreFatal, NuGetLogCode.NU3028, message)); } } else if (!chainBuildingHasIssues) { return(true); } chainBuildingHasIssues = true; } // Debug log any errors issues.Add(SignatureLog.DebugLog(string.Format(CultureInfo.CurrentCulture, Strings.ErrorInvalidCertificateChain, string.Join(", ", chainStatusList.Select(x => x.ToString()))))); } } return(false); }
internal static SignatureVerificationStatusFlags ValidateTimestamp(Timestamp timestamp, Signature signature, bool treatIssuesAsErrors, List <SignatureLog> issues, SigningSpecifications spec) { if (timestamp == null) { throw new ArgumentNullException(nameof(timestamp)); } if (signature == null) { throw new ArgumentNullException(nameof(signature)); } if (issues == null) { throw new ArgumentNullException(nameof(issues)); } // Default to specification v1 spec = spec ?? SigningSpecifications.V1; var validationFlags = SignatureVerificationStatusFlags.NoErrors; var signerInfo = timestamp.SignerInfo; if (timestamp.SignerInfo.Certificate != null) { try { signerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3021, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampSignatureValidationFailed, signature.FriendlyName))); issues.Add(SignatureLog.DebugLog(e.ToString())); validationFlags |= SignatureVerificationStatusFlags.SignatureCheckFailed; } if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3022, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampUnsupportedSignatureAlgorithm, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.SignatureAlgorithmUnsupported; } if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3023, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampCertificateFailsPublicKeyLengthRequirement, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.CertificatePublicKeyInvalid; } if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3024, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampUnsupportedSignatureAlgorithm, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.HashAlgorithmUnsupported; } try { var hashAlgorithm = CryptoHashUtility.OidToHashAlgorithmName(timestamp.TstInfo.HashAlgorithmId.Value); var signatureValue = signature.GetSignatureValue(); var messageHash = hashAlgorithm.ComputeHash(signatureValue); if (!timestamp.TstInfo.HasMessageHash(messageHash)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3019, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampIntegrityCheckFailed, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.IntegrityCheckFailed; } } catch { // If the hash algorithm is not supported OidToHashAlgorithmName will throw issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3030, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampMessageImprintUnsupportedHashAlgorithm, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.MessageImprintUnsupportedAlgorithm; } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3025, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampNotYetValid, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.CertificateValidityInTheFuture; } if (!CertificateUtility.IsDateInsideValidityPeriod(signerInfo.Certificate, timestamp.GeneralizedTime)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3036, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampGeneralizedTimeInvalid, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.GeneralizedTimeOutsideValidity; } } else { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3020, string.Format(CultureInfo.CurrentCulture, Strings.VerifyError_TimestampNoCertificate, signature.FriendlyName))); validationFlags |= SignatureVerificationStatusFlags.NoCertificate; } return(validationFlags); }
internal static bool IsTimestampValid(Timestamp timestamp, Signature signature, bool treatIssuesAsErrors, List <SignatureLog> issues, SigningSpecifications spec) { if (timestamp == null) { throw new ArgumentNullException(nameof(timestamp)); } if (signature == null) { throw new ArgumentNullException(nameof(signature)); } if (issues == null) { throw new ArgumentNullException(nameof(issues)); } // Default to specification v1 spec = spec ?? SigningSpecifications.V1; var isValid = true; var signerInfo = timestamp.SignerInfo; if (timestamp.SignerInfo.Certificate != null) { try { signerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3021, Strings.TimestampSignatureValidationFailed)); issues.Add(SignatureLog.DebugLog(e.ToString())); isValid = false; } if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3022, Strings.TimestampUnsupportedSignatureAlgorithm)); isValid = false; } if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3023, Strings.TimestampCertificateFailsPublicKeyLengthRequirement)); isValid = false; } if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3024, Strings.TimestampUnsupportedSignatureAlgorithm)); isValid = false; } try { var hashAlgorithm = CryptoHashUtility.OidToHashAlgorithmName(timestamp.TstInfo.HashAlgorithmId.Value); var signatureValue = signature.GetSignatureValue(); var messageHash = hashAlgorithm.ComputeHash(signatureValue); if (!timestamp.TstInfo.HasMessageHash(messageHash)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3019, Strings.TimestampIntegrityCheckFailed)); isValid = false; } } catch { // If the hash algorithm is not supported OidToHashAlgorithmName will throw issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3030, Strings.TimestampMessageImprintUnsupportedHashAlgorithm)); isValid = false; } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3025, Strings.TimestampNotYetValid)); isValid = false; } } else { issues.Add(SignatureLog.Issue(treatIssuesAsErrors, NuGetLogCode.NU3020, Strings.TimestampNoCertificate)); isValid = false; } return(isValid); }
/// <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); }
private static void ValidateTimestampCms(SigningSpecifications spec, SignedCms timestampCms, IRfc3161TimestampToken timestampToken) { var signerInfo = timestampCms.SignerInfos[0]; try { signerInfo.CheckSignature(verifySignatureOnly: true); } catch (Exception e) { throw new TimestampException(NuGetLogCode.NU3021, Strings.SignError_TimestampSignatureValidationFailed, e); } if (signerInfo.Certificate == null) { throw new TimestampException(NuGetLogCode.NU3020, Strings.SignError_TimestampNoCertificate); } if (!CertificateUtility.IsSignatureAlgorithmSupported(signerInfo.Certificate)) { var certificateSignatureAlgorithm = GetNameOrOidString(signerInfo.Certificate.SignatureAlgorithm); var supportedSignatureAlgorithms = string.Join(", ", spec.AllowedSignatureAlgorithms); var errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.TimestampCertificateUnsupportedSignatureAlgorithm, certificateSignatureAlgorithm, supportedSignatureAlgorithms); throw new TimestampException(NuGetLogCode.NU3022, errorMessage); } if (!CertificateUtility.IsCertificatePublicKeyValid(signerInfo.Certificate)) { throw new TimestampException(NuGetLogCode.NU3023, Strings.SignError_TimestampCertificateFailsPublicKeyLengthRequirement); } if (!spec.AllowedHashAlgorithmOids.Contains(signerInfo.DigestAlgorithm.Value)) { var digestAlgorithm = GetNameOrOidString(signerInfo.DigestAlgorithm); var supportedSignatureAlgorithms = string.Join(", ", spec.AllowedHashAlgorithms); var errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.TimestampSignatureUnsupportedDigestAlgorithm, digestAlgorithm, supportedSignatureAlgorithms); throw new TimestampException(NuGetLogCode.NU3024, errorMessage); } if (CertificateUtility.IsCertificateValidityPeriodInTheFuture(signerInfo.Certificate)) { throw new TimestampException(NuGetLogCode.NU3025, Strings.SignError_TimestampNotYetValid); } if (!CertificateUtility.IsDateInsideValidityPeriod(signerInfo.Certificate, timestampToken.TokenInfo.Timestamp)) { throw new TimestampException(NuGetLogCode.NU3036, Strings.SignError_TimestampGeneralizedTimeInvalid); } }
/// <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); }