X509CertificateChain.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / IdentityModel / System / IdentityModel / Selectors / X509CertificateChain.cs / 1 / X509CertificateChain.cs

                            //------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------

namespace System.IdentityModel.Selectors 
{
    using System.IdentityModel.Tokens; 
    using System.Runtime.InteropServices; 
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates; 
    using System.Text;

    using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
 
    // Most of codes are copied from \ndp\fx\src\security\system\security\cryptography\x509\X509Chain.cs
    class X509CertificateChain 
    { 
        public const uint DefaultChainPolicyOID = CAPI.CERT_CHAIN_POLICY_BASE;
        bool useMachineContext; 
        X509ChainPolicy chainPolicy;
        uint chainPolicyOID = X509CertificateChain.DefaultChainPolicyOID;

        public X509CertificateChain() 
            : this(false)
        { 
        } 

        public X509CertificateChain(bool useMachineContext) 
        {
            this.useMachineContext = useMachineContext;
        }
 
        public X509CertificateChain(bool useMachineContext, uint chainPolicyOID)
        { 
            this.useMachineContext = useMachineContext; 
            // One of the condition to pass NT_AUTH is the issuer of the cert must be trusted by NT auth.
            // Simply add to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\EnterpriseCertificates\NTAuth\Certificates 
            this.chainPolicyOID = chainPolicyOID;
        }

        public X509ChainPolicy ChainPolicy 
        {
            get 
            { 
                if (this.chainPolicy == null)
                { 
                    this.chainPolicy = new X509ChainPolicy();
                }
                return this.chainPolicy;
            } 
            set
            { 
                this.chainPolicy = value; 
            }
        } 

        public X509ChainStatus[] ChainStatus
        {
#pragma warning suppress 56503 // 
            get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException()); }
        } 
 
        // There are 2 steps in chain validation.
        // 1) BuildChain by calling CAPI.CertGetCertificateChain.  The result is 
        // the chain context containing the chain and status.
        // 2) VerifyChain by calling CAPI.CertVerifyCertificateChainPolicy.
        // Refer to MB50916, Since Vista out-of-the-box will trust the chain with PeerTrust,
        // we include the flag to ignore PeerTrust for CAPI.CertVerifyCertificateChainPolicy. 
        public bool Build(X509Certificate2 certificate)
        { 
            if (certificate == null) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("certificate");
            if (certificate.Handle == IntPtr.Zero) 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("certificate", SR.GetString(SR.ArgumentInvalidCertificate));

            SafeCertChainHandle safeCertChainHandle = SafeCertChainHandle.InvalidHandle;
            X509ChainPolicy chainPolicy = this.ChainPolicy; 
            BuildChain(this.useMachineContext ? new IntPtr(CAPI.HCCE_LOCAL_MACHINE) : new IntPtr(CAPI.HCCE_CURRENT_USER),
                    certificate.Handle, 
                    chainPolicy.ExtraStore, 
                    chainPolicy.ApplicationPolicy,
                    chainPolicy.CertificatePolicy, 
                    chainPolicy.RevocationMode,
                    chainPolicy.RevocationFlag,
                    chainPolicy.VerificationTime,
                    chainPolicy.UrlRetrievalTimeout, 
                    out safeCertChainHandle);
 
            // Verify the chain using the specified policy. 
            CAPI.CERT_CHAIN_POLICY_PARA PolicyPara = new CAPI.CERT_CHAIN_POLICY_PARA(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_PARA)));
            CAPI.CERT_CHAIN_POLICY_STATUS PolicyStatus = new CAPI.CERT_CHAIN_POLICY_STATUS(Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_POLICY_STATUS))); 

            // Ignore peertrust.  Peer trust caused the chain to succeed out-of-the-box in Vista.
            // This new flag is only available in Vista.
            PolicyPara.dwFlags = (uint)chainPolicy.VerificationFlags | CAPI.CERT_CHAIN_POLICY_IGNORE_PEER_TRUST_FLAG; 

            if (!CAPI.CertVerifyCertificateChainPolicy(new IntPtr(this.chainPolicyOID), 
                                                       safeCertChainHandle, 
                                                       ref PolicyPara,
                                                       ref PolicyStatus)) 
            {
                int error = Marshal.GetLastWin32Error();
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error));
            } 

            if (PolicyStatus.dwError != CAPI.S_OK) 
            { 
                int error = (int)PolicyStatus.dwError;
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SR.GetString(SR.X509ChainBuildFail, 
                    SecurityUtils.GetCertificateId(certificate), new CryptographicException(error).Message)));
            }

            return true; 
        }
 
        static unsafe void BuildChain(IntPtr hChainEngine, 
                                     IntPtr pCertContext,
                                     X509Certificate2Collection extraStore, 
                                     OidCollection applicationPolicy,
                                     OidCollection certificatePolicy,
                                     X509RevocationMode revocationMode,
                                     X509RevocationFlag revocationFlag, 
                                     DateTime verificationTime,
                                     TimeSpan timeout, 
                                     out SafeCertChainHandle ppChainContext) 
        {
            SafeCertStoreHandle hCertStore = ExportToMemoryStore(extraStore, pCertContext); 

            CAPI.CERT_CHAIN_PARA ChainPara = new CAPI.CERT_CHAIN_PARA();
            ChainPara.cbSize = (uint)Marshal.SizeOf(typeof(CAPI.CERT_CHAIN_PARA));
 
            // Application policy
            SafeHGlobalHandle applicationPolicyHandle = SafeHGlobalHandle.InvalidHandle; 
            SafeHGlobalHandle certificatePolicyHandle = SafeHGlobalHandle.InvalidHandle; 
            try
            { 
                if (applicationPolicy != null && applicationPolicy.Count > 0)
                {
                    ChainPara.RequestedUsage.dwType = CAPI.USAGE_MATCH_TYPE_AND;
                    ChainPara.RequestedUsage.Usage.cUsageIdentifier = (uint)applicationPolicy.Count; 
                    applicationPolicyHandle = CopyOidsToUnmanagedMemory(applicationPolicy);
                    ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = applicationPolicyHandle.DangerousGetHandle(); 
                } 

                // Certificate policy 
                if (certificatePolicy != null && certificatePolicy.Count > 0)
                {
                    ChainPara.RequestedIssuancePolicy.dwType = CAPI.USAGE_MATCH_TYPE_AND;
                    ChainPara.RequestedIssuancePolicy.Usage.cUsageIdentifier = (uint)certificatePolicy.Count; 
                    certificatePolicyHandle = CopyOidsToUnmanagedMemory(certificatePolicy);
                    ChainPara.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = certificatePolicyHandle.DangerousGetHandle(); 
                } 

                ChainPara.dwUrlRetrievalTimeout = (uint)timeout.Milliseconds; 

                FILETIME ft = new FILETIME();
                *((long*)&ft) = verificationTime.ToFileTime();
 
                uint flags = MapRevocationFlags(revocationMode, revocationFlag);
 
                // Build the chain. 
                if (!CAPI.CertGetCertificateChain(hChainEngine,
                                                  pCertContext, 
                                                  ref ft,
                                                  hCertStore,
                                                  ref ChainPara,
                                                  flags, 
                                                  IntPtr.Zero,
                                                  out ppChainContext)) 
                { 
                    int error = Marshal.GetLastWin32Error();
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error)); 
                }
            }
            finally
            { 
                if (applicationPolicyHandle != null)
                    applicationPolicyHandle.Dispose(); 
                if (certificatePolicyHandle != null) 
                    certificatePolicyHandle.Dispose();
                hCertStore.Close(); 
            }
        }

        static SafeCertStoreHandle ExportToMemoryStore(X509Certificate2Collection collection, IntPtr pCertContext) 
        {
            CAPI.CERT_CONTEXT certContext = (CAPI.CERT_CONTEXT)Marshal.PtrToStructure(pCertContext, typeof(CAPI.CERT_CONTEXT)); 
 
            // No extra store nor intermediate certificates
            if ((collection == null || collection.Count <= 0) && certContext.hCertStore == IntPtr.Zero) 
            {
                return SafeCertStoreHandle.InvalidHandle;
            }
 
            // we always want to use CERT_STORE_ENUM_ARCHIVED_FLAG since we want to preserve the collection in this operation.
            // By default, Archived certificates will not be included. 
            SafeCertStoreHandle certStoreHandle = CAPI.CertOpenStore(new IntPtr(CAPI.CERT_STORE_PROV_MEMORY), 
                                                     CAPI.X509_ASN_ENCODING | CAPI.PKCS_7_ASN_ENCODING,
                                                     IntPtr.Zero, 
                                                     CAPI.CERT_STORE_ENUM_ARCHIVED_FLAG | CAPI.CERT_STORE_CREATE_NEW_FLAG,
                                                     null);

            if (certStoreHandle == null || certStoreHandle.IsInvalid) 
            {
                int error = Marshal.GetLastWin32Error(); 
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error)); 
            }
 
            //
            // We use CertAddCertificateLinkToStore to keep a link to the original store, so any property changes get
            // applied to the original store. This has a limit of 99 links per cert context however.
            // 

            // Add extra store 
            if (collection != null && collection.Count > 0) 
            {
                foreach (X509Certificate2 x509 in collection) 
                {
                    if (!CAPI.CertAddCertificateLinkToStore(certStoreHandle,
                                                            x509.Handle,
                                                            CAPI.CERT_STORE_ADD_ALWAYS, 
                                                            SafeCertContextHandle.InvalidHandle))
                    { 
                        int error = Marshal.GetLastWin32Error(); 
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error));
                    } 
                }
            }

            // Add intermediates 
            if (certContext.hCertStore != IntPtr.Zero)
            { 
                X509Store store = new X509Store(certContext.hCertStore); 
                X509Certificate2Collection intermediates = null;
                try 
                {
                    intermediates = store.Certificates;
                    foreach (X509Certificate2 x509 in intermediates)
                    { 
                        if (!CAPI.CertAddCertificateLinkToStore(certStoreHandle,
                                                                x509.Handle, 
                                                                CAPI.CERT_STORE_ADD_ALWAYS, 
                                                                SafeCertContextHandle.InvalidHandle))
                        { 
                            int error = Marshal.GetLastWin32Error();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CryptographicException(error));
                        }
                    } 
                }
                finally 
                { 
                    SecurityUtils.ResetAllCertificates(intermediates);
                    store.Close(); 
                }
            }

            return certStoreHandle; 
        }
 
        static SafeHGlobalHandle CopyOidsToUnmanagedMemory(OidCollection oids) 
        {
            SafeHGlobalHandle safeAllocHandle = SafeHGlobalHandle.InvalidHandle; 
            if (oids == null || oids.Count == 0)
                return safeAllocHandle;

            int ptrSize = oids.Count * Marshal.SizeOf(typeof(IntPtr)); 
            int oidSize = 0;
            foreach (Oid oid in oids) 
            { 
                oidSize += (oid.Value.Length + 1);
            } 
            safeAllocHandle = SafeHGlobalHandle.AllocHGlobal(ptrSize + oidSize);
            IntPtr pOid = new IntPtr((long)safeAllocHandle.DangerousGetHandle() + ptrSize);
            for (int index = 0; index < oids.Count; index++)
            { 
                Marshal.WriteIntPtr(new IntPtr((long)safeAllocHandle.DangerousGetHandle() + index * Marshal.SizeOf(typeof(IntPtr))), pOid);
                byte[] ansiOid = Encoding.ASCII.GetBytes(oids[index].Value); 
                Marshal.Copy(ansiOid, 0, pOid, ansiOid.Length); 
                pOid = new IntPtr((long)pOid + oids[index].Value.Length + 1);
            } 
            return safeAllocHandle;
        }

        static uint MapRevocationFlags(X509RevocationMode revocationMode, X509RevocationFlag revocationFlag) 
        {
            uint dwFlags = 0; 
            if (revocationMode == X509RevocationMode.NoCheck) 
                return dwFlags;
 
            if (revocationMode == X509RevocationMode.Offline)
                dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY;

            if (revocationFlag == X509RevocationFlag.EndCertificateOnly) 
                dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_END_CERT;
            else if (revocationFlag == X509RevocationFlag.EntireChain) 
                dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CHAIN; 
            else
                dwFlags |= CAPI.CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT; 

            return dwFlags;
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK