private static void AddElementStatus(
            Interop.libcrypto.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            = MapVerifyErrorToChainStatus(errorCode),
                StatusInformation = Interop.libcrypto.X509_verify_cert_error_string(errorCode),
            };

            elementStatus.Add(chainStatus);
            AddUniqueStatus(overallStatus, ref chainStatus);
        }
        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,
            });
        }
        private static X509ChainStatusFlags MapVerifyErrorToChainStatus(Interop.libcrypto.X509VerifyStatusCode code)
        {
            switch (code)
            {
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_OK:
                return(X509ChainStatusFlags.NoError);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_NOT_YET_VALID:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_HAS_EXPIRED:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
                return(X509ChainStatusFlags.NotTimeValid);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_REVOKED:
                return(X509ChainStatusFlags.Revoked);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_SIGNATURE_FAILURE:
                return(X509ChainStatusFlags.NotSignatureValid);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_UNTRUSTED:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
                return(X509ChainStatusFlags.UntrustedRoot);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_HAS_EXPIRED:
                return(X509ChainStatusFlags.OfflineRevocation);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_NOT_YET_VALID:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CRL_SIGNATURE_FAILURE:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
                return(X509ChainStatusFlags.RevocationStatusUnknown);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_EXTENSION:
                return(X509ChainStatusFlags.InvalidExtension);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
                return(X509ChainStatusFlags.PartialChain);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_PURPOSE:
                return(X509ChainStatusFlags.NotValidForUsage);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_CA:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_NON_CA:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_PATH_LENGTH_EXCEEDED:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
                return(X509ChainStatusFlags.InvalidBasicConstraints);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_INVALID_POLICY_EXTENSION:
            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_NO_EXPLICIT_POLICY:
                return(X509ChainStatusFlags.InvalidPolicyConstraints);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_REJECTED:
                return(X509ChainStatusFlags.ExplicitDistrust);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
                return(X509ChainStatusFlags.HasNotSupportedCriticalExtension);

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_CERT_CHAIN_TOO_LONG:
                throw new CryptographicException();

            case Interop.libcrypto.X509VerifyStatusCode.X509_V_ERR_OUT_OF_MEM:
                throw new OutOfMemoryException();

            default:
                Debug.Fail("Unrecognized X509VerifyStatusCode:" + code);
                throw new CryptographicException();
            }
        }