private static void AddElementStatus( Interop.Crypto.X509VerifyStatusCode errorCode, List <X509ChainStatus> elementStatus, List <X509ChainStatus> overallStatus) { X509ChainStatusFlags statusFlag = MapVerifyErrorToChainStatus(errorCode); Debug.Assert( (statusFlag & (statusFlag - 1)) == 0, "Status flag has more than one bit set", "More than one bit is set in status '{0}' for error code '{1}'", statusFlag, errorCode); foreach (X509ChainStatus currentStatus in elementStatus) { if ((currentStatus.Status & statusFlag) != 0) { return; } } X509ChainStatus chainStatus = new X509ChainStatus { Status = statusFlag, StatusInformation = Interop.Crypto.GetX509VerifyCertErrorString(errorCode), }; elementStatus.Add(chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); }
internal Interop.Crypto.X509VerifyStatusCode FindFirstChain(X509Certificate2Collection extraCerts) { SafeX509StoreCtxHandle storeCtx = _storeCtx; // While this returns true/false, at this stage we care more about the detailed error code. Interop.Crypto.X509VerifyCert(storeCtx); Interop.Crypto.X509VerifyStatusCode statusCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); if (IsCompleteChain(statusCode)) { return(statusCode); } SafeX509StackHandle untrusted = _untrustedLookup; if (extraCerts?.Count > 0) { foreach (X509Certificate2 cert in extraCerts) { AddToStackAndUpRef(((OpenSslX509CertificateReader)cert.Pal).SafeHandle, untrusted); } Interop.Crypto.X509StoreCtxRebuildChain(storeCtx); statusCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); } return(statusCode); }
internal void CrlChainFinished(Interop.Crypto.X509VerifyStatusCode code) { if (IsEnabled()) { CrlChainFinished(code.Code); } }
internal void OcspResponseFromDownload(int chainDepth, Interop.Crypto.X509VerifyStatusCode code) { if (IsEnabled()) { OcspResponseFromDownload(chainDepth, code.Code); } }
internal void FindChainViaAiaFinished(Interop.Crypto.X509VerifyStatusCode code, int downloadCount) { if (IsEnabled()) { FindChainViaAiaFinished(code.Code, downloadCount); } }
internal static bool IsCompleteChain(Interop.Crypto.X509VerifyStatusCode statusCode) { switch (statusCode) { case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: return(false); default: return(true); } }
internal void Finish(OidCollection applicationPolicy, OidCollection certificatePolicy) { WorkingChain workingChain = null; // If the chain had any errors during the previous build we need to walk it again with // the error collector running. if (Interop.Crypto.X509StoreCtxGetError(_storeCtx) != Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { Interop.Crypto.X509StoreCtxReset(_storeCtx); workingChain = new WorkingChain(); Interop.Crypto.X509StoreVerifyCallback workingCallback = workingChain.VerifyCallback; Interop.Crypto.X509StoreCtxSetVerifyCallback(_storeCtx, workingCallback); bool verify = Interop.Crypto.X509VerifyCert(_storeCtx); GC.KeepAlive(workingCallback); // Because our callback tells OpenSSL that every problem is ignorable, it should tell us that the // chain is just fine (unless it returned a negative code for an exception) Debug.Assert(verify, "verify should have returned true"); const Interop.Crypto.X509VerifyStatusCode NoCrl = Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL; ErrorCollection?errors = workingChain.LastError > 0 ? (ErrorCollection?)workingChain[0] : null; if (_revocationMode == X509RevocationMode.Online && _remainingDownloadTime > TimeSpan.Zero && errors?.HasError(NoCrl) == true) { Interop.Crypto.X509VerifyStatusCode ocspStatus = CheckOcsp(); ref ErrorCollection refErrors = ref workingChain[0]; if (ocspStatus == Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { refErrors.ClearError(NoCrl); } else if (ocspStatus != NoCrl) { refErrors.ClearError(NoCrl); refErrors.Add(ocspStatus); } } }
internal int VerifyCallback(int ok, IntPtr ctx) { if (ok != 0) { return(ok); } try { using (var storeCtx = new SafeX509StoreCtxHandle(ctx, ownsHandle: false)) { Interop.Crypto.X509VerifyStatusCode errorCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); // We don't report "OK" as an error. // For compatibility with Windows / .NET Framework, do not report X509_V_CRL_NOT_YET_VALID. if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK && errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID) { while (Errors.Count <= errorDepth) { Errors.Add(null); } if (Errors[errorDepth] == null) { Errors[errorDepth] = new List <Interop.Crypto.X509VerifyStatusCode>(); } Errors[errorDepth].Add(errorCode); } } return(1); } catch { return(-1); } }
internal int VerifyCallback(int ok, IntPtr ctx) { if (ok < 0) { return(ok); } try { using (var storeCtx = new SafeX509StoreCtxHandle(ctx, ownsHandle: false)) { Interop.Crypto.X509VerifyStatusCode errorCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); int errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); if (errorCode != Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { while (Errors.Count <= errorDepth) { Errors.Add(null); } if (Errors[errorDepth] == null) { Errors[errorDepth] = new List <Interop.Crypto.X509VerifyStatusCode>(); } Errors[errorDepth].Add(errorCode); } } return(1); } catch { return(-1); } }
internal Interop.Crypto.X509VerifyStatusCode FindChainViaAia( ref List <X509Certificate2> downloadedCerts) { IntPtr lastCert = IntPtr.Zero; SafeX509StoreCtxHandle storeCtx = _storeCtx; Interop.Crypto.X509VerifyStatusCode statusCode = Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT; while (!IsCompleteChain(statusCode)) { using (SafeX509Handle currentCert = Interop.Crypto.X509StoreCtxGetCurrentCert(storeCtx)) { IntPtr currentHandle = currentCert.DangerousGetHandle(); // No progress was made, give up. if (currentHandle == lastCert) { break; } lastCert = currentHandle; ArraySegment <byte> authorityInformationAccess = OpenSslX509CertificateReader.FindFirstExtension( currentCert, Oids.AuthorityInformationAccess); if (authorityInformationAccess.Count == 0) { break; } X509Certificate2 downloaded = DownloadCertificate( authorityInformationAccess, ref _remainingDownloadTime); // The AIA record is contained in a public structure, so no need to clear it. CryptoPool.Return(authorityInformationAccess.Array, clearSize: 0); if (downloaded == null) { break; } if (downloadedCerts == null) { downloadedCerts = new List <X509Certificate2>(); } AddToStackAndUpRef(downloaded.Handle, _untrustedLookup); downloadedCerts.Add(downloaded); Interop.Crypto.X509StoreCtxRebuildChain(storeCtx); statusCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); } } if (statusCode == Interop.Crypto.X509VerifyStatusCode.X509_V_OK && downloadedCerts != null) { using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(_storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); Span <IntPtr> tempChain = stackalloc IntPtr[DefaultChainCapacity]; byte[] tempChainRent = null; if (chainSize <= tempChain.Length) { tempChain = tempChain.Slice(0, chainSize); } else { int targetSize = checked (chainSize * IntPtr.Size); tempChainRent = CryptoPool.Rent(targetSize); tempChain = MemoryMarshal.Cast <byte, IntPtr>(tempChainRent.AsSpan(0, targetSize)); } for (int i = 0; i < chainSize; i++) { tempChain[i] = Interop.Crypto.GetX509StackField(chainStack, i); } // In the average case we never made it here. // // Given that we made it here, in the average remaining case // we are doing a one item for which will match in the second position // of an (on-average) 3 item collection. // // The only case where this loop really matters is if downloading the // certificate made an alternate chain better, which may have resulted in // an extra download and made the first one not be involved any longer. In // that case, it's a 2 item for loop matching against a three item set. // // So N*M is well contained. for (int i = downloadedCerts.Count - 1; i >= 0; i--) { X509Certificate2 downloadedCert = downloadedCerts[i]; if (!tempChain.Contains(downloadedCert.Handle)) { downloadedCert.Dispose(); downloadedCerts.RemoveAt(i); } } if (downloadedCerts.Count == 0) { downloadedCerts = null; } if (tempChainRent != null) { CryptoPool.Return(tempChainRent); } } } return(statusCode); }
private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.Crypto.X509VerifyStatusCode code) { switch (code) { case Interop.Crypto.X509VerifyStatusCode.X509_V_OK: return(X509ChainStatusFlags.NoError); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_NOT_YET_VALID: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_HAS_EXPIRED: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: return(X509ChainStatusFlags.NotTimeValid); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED: return(X509ChainStatusFlags.Revoked); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE: return(X509ChainStatusFlags.NotSignatureValid); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: return(X509ChainStatusFlags.UntrustedRoot); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED: return(X509ChainStatusFlags.OfflineRevocation); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION: return(X509ChainStatusFlags.RevocationStatusUnknown); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_EXTENSION: return(X509ChainStatusFlags.InvalidExtension); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: return(X509ChainStatusFlags.PartialChain); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_PURPOSE: return(X509ChainStatusFlags.NotValidForUsage); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_CA: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_NON_CA: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_PATH_LENGTH_EXCEEDED: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CERTSIGN: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE: return(X509ChainStatusFlags.InvalidBasicConstraints); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_INVALID_POLICY_EXTENSION: case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_NO_EXPLICIT_POLICY: return(X509ChainStatusFlags.InvalidPolicyConstraints); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_REJECTED: return(X509ChainStatusFlags.ExplicitDistrust); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION: return(X509ChainStatusFlags.HasNotSupportedCriticalExtension); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_CHAIN_TOO_LONG: throw new CryptographicException(); case Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_OUT_OF_MEM: throw new OutOfMemoryException(); default: Debug.Fail("Unrecognized X509VerifyStatusCode:" + code); throw new CryptographicException(); } }
public static IChainPal BuildChain( bool useMachineContext, ICertificatePal cert, X509Certificate2Collection?extraStore, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, X509Certificate2Collection customTrustStore, X509ChainTrustMode trustMode, DateTime verificationTime, TimeSpan timeout) { // An input value of 0 on the timeout is "take all the time you need". if (timeout == TimeSpan.Zero) { timeout = TimeSpan.MaxValue; } // Let Unspecified mean Local, so only convert if the source was UTC. // // Converge on Local instead of UTC because OpenSSL is going to assume we gave it // local time. if (verificationTime.Kind == DateTimeKind.Utc) { verificationTime = verificationTime.ToLocalTime(); } // Until we support the Disallowed store, ensure it's empty (which is done by the ctor) using (new X509Store(StoreName.Disallowed, StoreLocation.CurrentUser, OpenFlags.ReadOnly)) { } TimeSpan remainingDownloadTime = timeout; OpenSslX509ChainProcessor chainPal = OpenSslX509ChainProcessor.InitiateChain( ((OpenSslX509CertificateReader)cert).SafeHandle, customTrustStore, trustMode, verificationTime, remainingDownloadTime); Interop.Crypto.X509VerifyStatusCode status = chainPal.FindFirstChain(extraStore); if (!OpenSslX509ChainProcessor.IsCompleteChain(status)) { List <X509Certificate2>?tmp = null; status = chainPal.FindChainViaAia(ref tmp); if (tmp != null) { if (status == Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { SaveIntermediateCertificates(tmp); } foreach (X509Certificate2 downloaded in tmp) { downloaded.Dispose(); } } } // In NoCheck+OK then we don't need to build the chain any more, we already // know it's error-free. So skip straight to finish. if (status != Interop.Crypto.X509VerifyStatusCode.X509_V_OK || revocationMode != X509RevocationMode.NoCheck) { if (OpenSslX509ChainProcessor.IsCompleteChain(status)) { chainPal.CommitToChain(); chainPal.ProcessRevocation(revocationMode, revocationFlag); } } chainPal.Finish(applicationPolicy, certificatePolicy); #if DEBUG if (chainPal.ChainElements !.Length > 0) { X509Certificate2 reportedLeaf = chainPal.ChainElements[0].Certificate; Debug.Assert(reportedLeaf != null, "reportedLeaf != null"); Debug.Assert(!ReferenceEquals(cert, reportedLeaf.Pal), "!ReferenceEquals(cert, reportedLeaf.Pal)"); } #endif return(chainPal); }
private static IChainPal?BuildChainCore( bool useMachineContext, ICertificatePal cert, X509Certificate2Collection?extraStore, OidCollection?applicationPolicy, OidCollection?certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, X509Certificate2Collection?customTrustStore, X509ChainTrustMode trustMode, DateTime verificationTime, TimeSpan timeout, bool disableAia) { if (timeout == TimeSpan.Zero) { // An input value of 0 on the timeout is treated as 15 seconds, to match Windows. timeout = TimeSpan.FromSeconds(15); } else if (timeout > s_maxUrlRetrievalTimeout || timeout < TimeSpan.Zero) { // Windows has a max timeout of 1 minute, so we'll match. Windows also treats // the timeout as unsigned, so a negative value gets treated as a large positive // value that is also clamped. timeout = s_maxUrlRetrievalTimeout; } // Let Unspecified mean Local, so only convert if the source was UTC. // // Converge on Local instead of UTC because OpenSSL is going to assume we gave it // local time. if (verificationTime.Kind == DateTimeKind.Utc) { verificationTime = verificationTime.ToLocalTime(); } // Until we support the Disallowed store, ensure it's empty (which is done by the ctor) using (new X509Store(StoreName.Disallowed, StoreLocation.CurrentUser, OpenFlags.ReadOnly)) { } TimeSpan downloadTimeout = timeout; OpenSslX509ChainProcessor chainPal = OpenSslX509ChainProcessor.InitiateChain( ((OpenSslX509CertificateReader)cert).SafeHandle, customTrustStore, trustMode, verificationTime, downloadTimeout); Interop.Crypto.X509VerifyStatusCode status = chainPal.FindFirstChain(extraStore); if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.FindFirstChainFinished(status); } if (!OpenSslX509ChainProcessor.IsCompleteChain(status)) { if (disableAia) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.AiaDisabled(); } } else { List <X509Certificate2>?tmp = null; status = chainPal.FindChainViaAia(ref tmp); if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.FindChainViaAiaFinished(status, tmp?.Count ?? 0); } if (tmp != null) { if (status == Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { SaveIntermediateCertificates(tmp); } foreach (X509Certificate2 downloaded in tmp) { downloaded.Dispose(); } } } } if (revocationMode != X509RevocationMode.NoCheck) { if (OpenSslX509ChainProcessor.IsCompleteChain(status)) { // Checking the validity period for the certificates in the chain is done after the // check for a trusted root, so accept expired (or not yet valid) as acceptable for // processing revocation. if (status != Interop.Crypto.X509VerifyStatusCode.X509_V_OK && status != Interop.Crypto.X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_NOT_YET_VALID && status != Interop.Crypto.X509VerifyStatusCodeUniversal.X509_V_ERR_CERT_HAS_EXPIRED) { if (OpenSslX509ChainEventSource.Log.IsEnabled()) { OpenSslX509ChainEventSource.Log.UntrustedChainWithRevocation(); } revocationMode = X509RevocationMode.NoCheck; } chainPal.CommitToChain(); chainPal.ProcessRevocation(revocationMode, revocationFlag); } } chainPal.Finish(applicationPolicy, certificatePolicy); #if DEBUG if (chainPal.ChainElements !.Length > 0) { X509Certificate2 reportedLeaf = chainPal.ChainElements[0].Certificate; Debug.Assert(reportedLeaf != null, "reportedLeaf != null"); Debug.Assert(!ReferenceEquals(cert, reportedLeaf.Pal), "!ReferenceEquals(cert, reportedLeaf.Pal)"); } #endif return(chainPal); }
internal static partial IChainPal?BuildChain( bool useMachineContext, ICertificatePal cert, X509Certificate2Collection?extraStore, OidCollection?applicationPolicy, OidCollection?certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, X509Certificate2Collection?customTrustStore, X509ChainTrustMode trustMode, DateTime verificationTime, TimeSpan timeout, bool disableAia) { if (timeout == TimeSpan.Zero) { // An input value of 0 on the timeout is treated as 15 seconds, to match Windows. timeout = TimeSpan.FromSeconds(15); } else if (timeout > s_maxUrlRetrievalTimeout || timeout < TimeSpan.Zero) { // Windows has a max timeout of 1 minute, so we'll match. Windows also treats // the timeout as unsigned, so a negative value gets treated as a large positive // value that is also clamped. timeout = s_maxUrlRetrievalTimeout; } // Let Unspecified mean Local, so only convert if the source was UTC. // // Converge on Local instead of UTC because OpenSSL is going to assume we gave it // local time. if (verificationTime.Kind == DateTimeKind.Utc) { verificationTime = verificationTime.ToLocalTime(); } // Until we support the Disallowed store, ensure it's empty (which is done by the ctor) using (new X509Store(StoreName.Disallowed, StoreLocation.CurrentUser, OpenFlags.ReadOnly)) { } TimeSpan downloadTimeout = timeout; OpenSslX509ChainProcessor chainPal = OpenSslX509ChainProcessor.InitiateChain( ((OpenSslX509CertificateReader)cert).SafeHandle, customTrustStore, trustMode, verificationTime, downloadTimeout); Interop.Crypto.X509VerifyStatusCode status = chainPal.FindFirstChain(extraStore); if (!OpenSslX509ChainProcessor.IsCompleteChain(status) && !disableAia) { List <X509Certificate2>?tmp = null; status = chainPal.FindChainViaAia(ref tmp); if (tmp != null) { if (status == Interop.Crypto.X509VerifyStatusCode.X509_V_OK) { SaveIntermediateCertificates(tmp); } foreach (X509Certificate2 downloaded in tmp) { downloaded.Dispose(); } } } // In NoCheck+OK then we don't need to build the chain any more, we already // know it's error-free. So skip straight to finish. if (status != Interop.Crypto.X509VerifyStatusCode.X509_V_OK || revocationMode != X509RevocationMode.NoCheck) { if (OpenSslX509ChainProcessor.IsCompleteChain(status)) { chainPal.CommitToChain(); chainPal.ProcessRevocation(revocationMode, revocationFlag); } } chainPal.Finish(applicationPolicy, certificatePolicy); #if DEBUG if (chainPal.ChainElements !.Length > 0) { X509Certificate2 reportedLeaf = chainPal.ChainElements[0].Certificate; Debug.Assert(reportedLeaf != null, "reportedLeaf != null"); Debug.Assert(!ReferenceEquals(cert, reportedLeaf.Pal), "!ReferenceEquals(cert, reportedLeaf.Pal)"); } #endif return(chainPal); }
public static IChainPal BuildChain( X509Certificate2 leaf, List <X509Certificate2> candidates, List <X509Certificate2> downloaded, List <X509Certificate2> systemTrusted, OidCollection applicationPolicy, OidCollection certificatePolicy, X509RevocationMode revocationMode, X509RevocationFlag revocationFlag, DateTime verificationTime, ref TimeSpan remainingDownloadTime) { X509ChainElement[] elements; List <X509ChainStatus> overallStatus = new List <X509ChainStatus>(); // An X509_STORE is more comparable to Cryptography.X509Certificate2Collection than to // Cryptography.X509Store. So read this with OpenSSL eyes, not CAPI/CNG eyes. // // (If you need to think of it as an X509Store, it's a volatile memory store) using (SafeX509StoreHandle store = Interop.Crypto.X509StoreCreate()) using (SafeX509StoreCtxHandle storeCtx = Interop.Crypto.X509StoreCtxCreate()) { Interop.Crypto.CheckValidOpenSslHandle(store); Interop.Crypto.CheckValidOpenSslHandle(storeCtx); bool lookupCrl = revocationMode != X509RevocationMode.NoCheck; foreach (X509Certificate2 cert in candidates) { OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal; if (!Interop.Crypto.X509StoreAddCert(store, pal.SafeHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } if (lookupCrl) { CrlCache.AddCrlForCertificate( cert, store, revocationMode, verificationTime, ref remainingDownloadTime); // If we only wanted the end-entity certificate CRL then don't look up // any more of them. lookupCrl = revocationFlag != X509RevocationFlag.EndCertificateOnly; } } if (revocationMode != X509RevocationMode.NoCheck) { if (!Interop.Crypto.X509StoreSetRevocationFlag(store, revocationFlag)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } } // When CRL checking support is added, it should be done before the call to // X509_STORE_CTX_init (aka here) by calling X509_STORE_set_flags(store, flags); SafeX509Handle leafHandle = ((OpenSslX509CertificateReader)leaf.Pal).SafeHandle; if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle)) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime); int verify = Interop.Crypto.X509VerifyCert(storeCtx); if (verify < 0) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(storeCtx)) { int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack); int errorDepth = -1; Interop.Crypto.X509VerifyStatusCode errorCode = 0; if (verify == 0) { errorCode = Interop.Crypto.X509StoreCtxGetError(storeCtx); errorDepth = Interop.Crypto.X509StoreCtxGetErrorDepth(storeCtx); } elements = new X509ChainElement[chainSize]; int maybeRootDepth = chainSize - 1; // The leaf cert is 0, up to (maybe) the root at chainSize - 1 for (int i = 0; i < chainSize; i++) { List <X509ChainStatus> status = new List <X509ChainStatus>(); if (i == errorDepth) { AddElementStatus(errorCode, status, overallStatus); } IntPtr elementCertPtr = Interop.Crypto.GetX509StackField(chainStack, i); if (elementCertPtr == IntPtr.Zero) { throw Interop.Crypto.CreateOpenSslCryptographicException(); } // Duplicate the certificate handle X509Certificate2 elementCert = new X509Certificate2(elementCertPtr); // If the last cert is self signed then it's the root cert, do any extra checks. if (i == maybeRootDepth && IsSelfSigned(elementCert)) { // If the root certificate was downloaded or the system // doesn't trust it, it's untrusted. if (downloaded.Contains(elementCert) || !systemTrusted.Contains(elementCert)) { AddElementStatus( Interop.Crypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED, status, overallStatus); } } elements[i] = new X509ChainElement(elementCert, status.ToArray(), ""); } } } if ((certificatePolicy != null && certificatePolicy.Count > 0) || (applicationPolicy != null && applicationPolicy.Count > 0)) { List <X509Certificate2> certsToRead = new List <X509Certificate2>(); foreach (X509ChainElement element in elements) { certsToRead.Add(element.Certificate); } CertificatePolicyChain policyChain = new CertificatePolicyChain(certsToRead); bool failsPolicyChecks = false; if (certificatePolicy != null) { if (!policyChain.MatchesCertificatePolicies(certificatePolicy)) { failsPolicyChecks = true; } } if (applicationPolicy != null) { if (!policyChain.MatchesApplicationPolicies(applicationPolicy)) { failsPolicyChecks = true; } } if (failsPolicyChecks) { X509ChainElement leafElement = elements[0]; X509ChainStatus chainStatus = new X509ChainStatus { Status = X509ChainStatusFlags.InvalidPolicyConstraints, StatusInformation = SR.Chain_NoPolicyMatch, }; var elementStatus = new List <X509ChainStatus>(leafElement.ChainElementStatus.Length + 1); elementStatus.AddRange(leafElement.ChainElementStatus); AddUniqueStatus(elementStatus, ref chainStatus); AddUniqueStatus(overallStatus, ref chainStatus); elements[0] = new X509ChainElement( leafElement.Certificate, elementStatus.ToArray(), leafElement.Information); } } return(new OpenSslX509ChainProcessor { ChainStatus = overallStatus.ToArray(), ChainElements = elements, }); }