Exemplo n.º 1
0
        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);
                    }
                }
            }
        public static IChainPal BuildChain(
            X509Certificate2 leaf,
            HashSet<X509Certificate2> candidates,
            HashSet<X509Certificate2> downloaded,
            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())
            {
                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();
                    }
                }

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

                if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle))
                {
                    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);

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

            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,
            };
        }
Exemplo n.º 3
0
        public static IChainPal BuildChain(
            X509Certificate2 leaf,
            HashSet <X509Certificate2> candidates,
            HashSet <X509Certificate2> downloaded,
            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())
                {
                    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();
                        }
                    }

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

                    if (!Interop.Crypto.X509StoreCtxInit(storeCtx, store, leafHandle))
                    {
                        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);

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

            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.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,
            });
        }