Ejemplo n.º 1
0
        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);
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
 internal void CrlChainFinished(Interop.Crypto.X509VerifyStatusCode code)
 {
     if (IsEnabled())
     {
         CrlChainFinished(code.Code);
     }
 }
Ejemplo n.º 4
0
 internal void OcspResponseFromDownload(int chainDepth, Interop.Crypto.X509VerifyStatusCode code)
 {
     if (IsEnabled())
     {
         OcspResponseFromDownload(chainDepth, code.Code);
     }
 }
Ejemplo n.º 5
0
 internal void FindChainViaAiaFinished(Interop.Crypto.X509VerifyStatusCode code, int downloadCount)
 {
     if (IsEnabled())
     {
         FindChainViaAiaFinished(code.Code, downloadCount);
     }
 }
Ejemplo n.º 6
0
        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);
            }
        }
Ejemplo n.º 7
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);
                    }
                }
            }
Ejemplo n.º 8
0
            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);
                }
            }
Ejemplo n.º 9
0
            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);
                }
            }
Ejemplo n.º 10
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);
        }
Ejemplo n.º 11
0
        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();
            }
        }
Ejemplo n.º 12
0
        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);
        }
Ejemplo n.º 13
0
        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);
        }
Ejemplo n.º 14
0
        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,
            });
        }