예제 #1
0
        private byte[] ExportPfx(string password)
        {
            using (SafeX509StackHandle publicCerts = Interop.Crypto.NewX509Stack())
            {
                X509Certificate2 privateCert = null;

                // Walk the collection backwards, because we're pushing onto a stack.
                // This will cause the read order later to be the same as it was now.
                for (int i = _certs.Length - 1; i >= 0; --i)
                {
                    X509Certificate2 cert = _certs[i];

                    if (cert.HasPrivateKey)
                    {
                        if (privateCert != null)
                        {
                            // OpenSSL's PKCS12 accelerator (PKCS12_create) only supports one
                            // private key.  The data structure supports more than one, but
                            // being able to use that functionality requires a lot more code for
                            // a low-usage scenario.
                            throw new PlatformNotSupportedException(SR.NotSupported_Export_MultiplePrivateCerts);
                        }

                        privateCert = cert;
                    }
                    else
                    {
                        using (SafeX509Handle certHandle = Interop.Crypto.X509Duplicate(cert.Handle))
                        {
                            if (!Interop.Crypto.PushX509StackField(publicCerts, certHandle))
                            {
                                throw Interop.Crypto.CreateOpenSslCryptographicException();
                            }

                            // The handle ownership has been transferred into the STACK_OF(X509).
                            certHandle.SetHandleAsInvalid();
                        }
                    }
                }

                SafeX509Handle    privateCertHandle;
                SafeEvpPKeyHandle privateCertKeyHandle;

                if (privateCert != null)
                {
                    OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)privateCert.Pal;
                    privateCertHandle    = pal.SafeHandle;
                    privateCertKeyHandle = pal.PrivateKeyHandle ?? InvalidPKeyHandle;
                }
                else
                {
                    privateCertHandle    = SafeX509Handle.InvalidHandle;
                    privateCertKeyHandle = InvalidPKeyHandle;
                }

                using (SafePkcs12Handle pkcs12 = Interop.Crypto.Pkcs12Create(
                           password,
                           privateCertKeyHandle,
                           privateCertHandle,
                           publicCerts))
                {
                    if (pkcs12.IsInvalid)
                    {
                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                    }

                    return(Interop.Crypto.OpenSslEncode(
                               Interop.Crypto.GetPkcs12DerSize,
                               Interop.Crypto.EncodePkcs12,
                               pkcs12));
                }
            }
        }
예제 #2
0
        private byte[] ExportPfx(SafePasswordHandle password)
        {
            using (SafeX509StackHandle publicCerts = Interop.Crypto.NewX509Stack())
            {
                SafeX509Handle    privateCertHandle    = SafeX509Handle.InvalidHandle;
                SafeEvpPKeyHandle privateCertKeyHandle = InvalidPKeyHandle;

                if (_singleCertPal != null)
                {
                    var certPal = (OpenSslX509CertificateReader)_singleCertPal;

                    if (_singleCertPal.HasPrivateKey)
                    {
                        privateCertHandle    = certPal.SafeHandle;
                        privateCertKeyHandle = certPal.PrivateKeyHandle;
                    }
                    else
                    {
                        PushHandle(certPal.Handle, publicCerts);
                    }

                    GC.KeepAlive(certPal); // ensure reader's safe handle isn't finalized while raw handle is in use
                }
                else
                {
                    X509Certificate2 privateCert = null;

                    // Walk the collection backwards, because we're pushing onto a stack.
                    // This will cause the read order later to be the same as it was now.
                    for (int i = _certs.Count - 1; i >= 0; --i)
                    {
                        X509Certificate2 cert = _certs[i];

                        if (cert.HasPrivateKey)
                        {
                            if (privateCert != null)
                            {
                                // OpenSSL's PKCS12 accelerator (PKCS12_create) only supports one
                                // private key.  The data structure supports more than one, but
                                // being able to use that functionality requires a lot more code for
                                // a low-usage scenario.
                                throw new PlatformNotSupportedException(SR.NotSupported_Export_MultiplePrivateCerts);
                            }

                            privateCert = cert;
                            var certPal = (OpenSslX509CertificateReader)cert.Pal;
                            privateCertHandle    = certPal.SafeHandle;
                            privateCertKeyHandle = certPal.PrivateKeyHandle;
                        }
                        else
                        {
                            PushHandle(cert.Handle, publicCerts);
                        }
                    }
                }

                using (SafePkcs12Handle pkcs12 = Interop.Crypto.Pkcs12Create(
                           password,
                           privateCertKeyHandle,
                           privateCertHandle,
                           publicCerts))
                {
                    if (pkcs12.IsInvalid)
                    {
                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                    }

                    byte[] result = Interop.Crypto.OpenSslEncode(
                        Interop.Crypto.GetPkcs12DerSize,
                        Interop.Crypto.EncodePkcs12,
                        pkcs12);

                    // ensure cert handles aren't finalized while the raw handles are in use
                    GC.KeepAlive(_certs);
                    return(result);
                }
            }
        }
예제 #3
0
        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);
        }
        public static IChainPal BuildChain(
            X509Certificate2 leaf,
            HashSet <X509Certificate2> candidates,
            HashSet <X509Certificate2> systemTrusted,
            OidCollection applicationPolicy,
            OidCollection certificatePolicy,
            X509RevocationMode revocationMode,
            X509RevocationFlag revocationFlag,
            DateTime verificationTime,
            ref TimeSpan remainingDownloadTime)
        {
            X509ChainElement[]     elements;
            List <X509ChainStatus> overallStatus = new List <X509ChainStatus>();
            WorkingChain           workingChain  = new WorkingChain();

            Interop.Crypto.X509StoreVerifyCallback workingCallback = workingChain.VerifyCallback;

            // 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())
                    using (SafeX509StackHandle extraCerts = Interop.Crypto.NewX509Stack())
                    {
                        Interop.Crypto.CheckValidOpenSslHandle(store);
                        Interop.Crypto.CheckValidOpenSslHandle(storeCtx);
                        Interop.Crypto.CheckValidOpenSslHandle(extraCerts);

                        bool lookupCrl = revocationMode != X509RevocationMode.NoCheck;

                        foreach (X509Certificate2 cert in candidates)
                        {
                            OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal;

                            using (SafeX509Handle handle = Interop.Crypto.X509UpRef(pal.SafeHandle))
                            {
                                if (!Interop.Crypto.PushX509StackField(extraCerts, handle))
                                {
                                    throw Interop.Crypto.CreateOpenSslCryptographicException();
                                }

                                // Ownership was transferred to the cert stack.
                                handle.SetHandleAsInvalid();
                            }

                            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();
                            }
                        }

                        foreach (X509Certificate2 trustedCert in systemTrusted)
                        {
                            OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)trustedCert.Pal;

                            if (!Interop.Crypto.X509StoreAddCert(store, pal.SafeHandle))
                            {
                                throw Interop.Crypto.CreateOpenSslCryptographicException();
                            }
                        }

                        SafeX509Handle leafHandle = ((OpenSslX509CertificateReader)leaf.Pal).SafeHandle;

                        if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle, extraCerts))
                        {
                            throw Interop.Crypto.CreateOpenSslCryptographicException();
                        }

                        Interop.Crypto.X509StoreCtxSetVerifyCallback(storeCtx, workingCallback);
                        Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime);

                        int verify = Interop.Crypto.X509VerifyCert(storeCtx);

                        if (verify < 0)
                        {
                            throw Interop.Crypto.CreateOpenSslCryptographicException();
                        }

                        // 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 == 1, "verify == 1");

                        using (SafeX509StackHandle chainStack = Interop.Crypto.X509StoreCtxGetChain(storeCtx))
                        {
                            int chainSize = Interop.Crypto.GetX509StackFieldCount(chainStack);
                            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>();

                                List <Interop.Crypto.X509VerifyStatusCode> elementErrors =
                                    i < workingChain.Errors.Count ? workingChain.Errors[i] : null;

                                if (elementErrors != null)
                                {
                                    AddElementStatus(elementErrors, 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);
                                elements[i] = new X509ChainElement(elementCert, status.ToArray(), "");
                            }
                        }
                    }

            GC.KeepAlive(workingCallback);

            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.NotValidForUsage,
                        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,
            });
        }
예제 #5
0
 internal static partial int GetX509StackFieldCount(SafeX509StackHandle stack);
예제 #6
0
 internal static extern SafePkcs12Handle Pkcs12Create(
     SafePasswordHandle pass,
     SafeEvpPKeyHandle pkey,
     SafeX509Handle cert,
     SafeX509StackHandle ca);
예제 #7
0
 internal static extern bool PushX509StackField(SafeX509StackHandle stack, SafeX509Handle x509);
예제 #8
0
        internal static SafeX509StoreHandle X509ChainNew(SafeX509StackHandle systemTrust, SafeX509StackHandle userTrust)
        {
            SafeX509StoreHandle store = CryptoNative_X509ChainNew(systemTrust, userTrust);

            if (store.IsInvalid)
            {
                Exception e = CreateOpenSslCryptographicException();
                store.Dispose();
                throw e;
            }

            return(store);
        }
예제 #9
0
 internal static extern int GetX509StackFieldCount(SafeX509StackHandle stack);
예제 #10
0
 internal static extern IntPtr GetX509StackField(SafeX509StackHandle stack, int loc);
예제 #11
0
 private static partial int CryptoNative_X509StackAddMultiple(SafeX509StackHandle dest, SafeX509StackHandle src);
예제 #12
0
 private static partial int CryptoNative_X509StackAddDirectoryStore(SafeX509StackHandle stack, string storePath);
예제 #13
0
 internal static extern bool Pkcs12Parse(
     SafePkcs12Handle p12,
     SafePasswordHandle pass,
     out SafeEvpPKeyHandle pkey,
     out SafeX509Handle cert,
     out SafeX509StackHandle ca);
예제 #14
0
 internal static partial bool PushX509StackField(SafeX509StackHandle stack, SafeX509Handle x509);
예제 #15
0
        private static Tuple <SafeX509StackHandle, SafeX509StackHandle> LoadMachineStores(
            DirectoryInfo rootStorePath,
            FileInfo rootStoreFile)
        {
            Debug.Assert(
                Monitor.IsEntered(s_recheckStopwatch),
                "LoadMachineStores assumes a lock(s_recheckStopwatch)");

            SafeX509StackHandle rootStore = Interop.Crypto.NewX509Stack();

            Interop.Crypto.CheckValidOpenSslHandle(rootStore);
            SafeX509StackHandle intermedStore = Interop.Crypto.NewX509Stack();

            Interop.Crypto.CheckValidOpenSslHandle(intermedStore);

            DateTime newFileTime = default;
            DateTime newDirTime  = default;

            var uniqueRootCerts         = new HashSet <X509Certificate2>();
            var uniqueIntermediateCerts = new HashSet <X509Certificate2>();

            if (rootStoreFile != null && rootStoreFile.Exists)
            {
                newFileTime = rootStoreFile.LastWriteTimeUtc;
                ProcessFile(rootStoreFile);
            }

            if (rootStorePath != null && rootStorePath.Exists)
            {
                newDirTime = rootStorePath.LastWriteTimeUtc;
                foreach (FileInfo file in rootStorePath.EnumerateFiles())
                {
                    ProcessFile(file);
                }
            }

            void ProcessFile(FileInfo file)
            {
                using (SafeBioHandle fileBio = Interop.Crypto.BioNewFile(file.FullName, "rb"))
                {
                    // The handle may be invalid, for example when we don't have read permission for the file.
                    if (fileBio.IsInvalid)
                    {
                        Interop.Crypto.ErrClearError();
                        return;
                    }

                    // Some distros ship with two variants of the same certificate.
                    // One is the regular format ('BEGIN CERTIFICATE') and the other
                    // contains additional AUX-data ('BEGIN TRUSTED CERTIFICATE').
                    // The additional data contains the appropriate usage (e.g. emailProtection, serverAuth, ...).
                    // Because corefx doesn't validate for a specific usage, derived certificates are rejected.
                    // For now, we skip the certificates with AUX data and use the regular certificates.
                    ICertificatePal pal;
                    while (OpenSslX509CertificateReader.TryReadX509PemNoAux(fileBio, out pal) ||
                           OpenSslX509CertificateReader.TryReadX509Der(fileBio, out pal))
                    {
                        X509Certificate2 cert = new X509Certificate2(pal);

                        // The HashSets are just used for uniqueness filters, they do not survive this method.
                        if (StringComparer.Ordinal.Equals(cert.Subject, cert.Issuer))
                        {
                            if (uniqueRootCerts.Add(cert))
                            {
                                using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle))
                                {
                                    if (!Interop.Crypto.PushX509StackField(rootStore, tmp))
                                    {
                                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                                    }

                                    // The ownership has been transferred to the stack
                                    tmp.SetHandleAsInvalid();
                                }

                                continue;
                            }
                        }
                        else
                        {
                            if (uniqueIntermediateCerts.Add(cert))
                            {
                                using (SafeX509Handle tmp = Interop.Crypto.X509UpRef(pal.Handle))
                                {
                                    if (!Interop.Crypto.PushX509StackField(intermedStore, tmp))
                                    {
                                        throw Interop.Crypto.CreateOpenSslCryptographicException();
                                    }

                                    // The ownership has been transferred to the stack
                                    tmp.SetHandleAsInvalid();
                                }

                                continue;
                            }
                        }

                        // There's a good chance we'll encounter duplicates on systems that have both one-cert-per-file
                        // and one-big-file trusted certificate stores. Anything that wasn't unique will end up here.
                        cert.Dispose();
                    }
                }
            }

            foreach (X509Certificate2 cert in uniqueRootCerts)
            {
                cert.Dispose();
            }

            foreach (X509Certificate2 cert in uniqueIntermediateCerts)
            {
                cert.Dispose();
            }

            Tuple <SafeX509StackHandle, SafeX509StackHandle> newCollections =
                Tuple.Create(rootStore, intermedStore);

            Debug.Assert(
                Monitor.IsEntered(s_recheckStopwatch),
                "LoadMachineStores assumes a lock(s_recheckStopwatch)");

            // The existing collections are not Disposed here, intentionally.
            // They could be in the gap between when they are returned from this method and not yet used
            // in a P/Invoke, which would result in exceptions being thrown.
            // In order to maintain "finalization-free" the GetNativeCollections method would need to
            // DangerousAddRef, and the callers would need to DangerousRelease, adding more interlocked operations
            // on every call.

            Volatile.Write(ref s_nativeCollections, newCollections);
            s_directoryCertsLastWrite = newDirTime;
            s_fileCertsLastWrite      = newFileTime;
            s_recheckStopwatch.Restart();
            return(newCollections);
        }
예제 #16
0
 private static partial SafeX509StoreHandle CryptoNative_X509ChainNew(SafeX509StackHandle systemTrust, SafeX509StackHandle userTrust);
예제 #17
0
 internal static extern bool PKCS12_parse(SafePkcs12Handle p12, string pass, out SafeEvpPKeyHandle pkey, out SafeX509Handle cert, out SafeX509StackHandle ca);
예제 #18
0
 internal static partial bool X509StoreCtxInit(
     SafeX509StoreCtxHandle ctx,
     SafeX509StoreHandle store,
     SafeX509Handle x509,
     SafeX509StackHandle extraCerts);
예제 #19
0
 internal static partial SafePkcs7Handle Pkcs7CreateCertificateCollection(SafeX509StackHandle certs);
예제 #20
0
 internal static partial IntPtr GetX509StackField(SafeX509StackHandle stack, int loc);
예제 #21
0
        public static IChainPal BuildChain(
            X509Certificate2 leaf,
            List <X509Certificate2> candidates,
            List <X509Certificate2> downloaded,
            OidCollection applicationPolicy,
            OidCollection certificatePolicy,
            DateTime verificationTime)
        {
            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.libcrypto.X509_STORE_new())
                using (SafeX509StoreCtxHandle storeCtx = Interop.libcrypto.X509_STORE_CTX_new())
                {
                    Interop.libcrypto.CheckValidOpenSslHandle(store);
                    Interop.libcrypto.CheckValidOpenSslHandle(storeCtx);

                    foreach (X509Certificate2 cert in candidates)
                    {
                        OpenSslX509CertificateReader pal = (OpenSslX509CertificateReader)cert.Pal;

                        if (!Interop.libcrypto.X509_STORE_add_cert(store, pal.SafeHandle))
                        {
                            throw Interop.libcrypto.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.libcrypto.X509_STORE_CTX_init(storeCtx, store, leafHandle, IntPtr.Zero))
                    {
                        throw Interop.libcrypto.CreateOpenSslCryptographicException();
                    }

                    Interop.Crypto.SetX509ChainVerifyTime(storeCtx, verificationTime);

                    int verify = Interop.libcrypto.X509_verify_cert(storeCtx);

                    if (verify < 0)
                    {
                        throw Interop.libcrypto.CreateOpenSslCryptographicException();
                    }

                    using (SafeX509StackHandle chainStack = Interop.libcrypto.X509_STORE_CTX_get1_chain(storeCtx))
                    {
                        int chainSize  = Interop.Crypto.GetX509StackFieldCount(chainStack);
                        int errorDepth = -1;
                        Interop.libcrypto.X509VerifyStatusCode errorCode = 0;

                        if (verify == 0)
                        {
                            errorCode  = Interop.libcrypto.X509_STORE_CTX_get_error(storeCtx);
                            errorDepth = Interop.libcrypto.X509_STORE_CTX_get_error_depth(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.libcrypto.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, it's untrusted.
                                if (downloaded.Contains(elementCert))
                                {
                                    AddElementStatus(
                                        Interop.libcrypto.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,
            });
        }