InfoCardX509Validator.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 / infocard / Service / managed / Microsoft / InfoCards / InfoCardX509Validator.cs / 1 / InfoCardX509Validator.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
//
// Presharp uses the c# pragma mechanism to supress its warnings. 
// These are not recognised by the base compiler so we need to explictly
// disable the following warnings. See http://winweb/cse/Tools/PREsharp/userguide/default.asp 
// for details. 
//
#pragma warning disable 1634, 1691      // unknown message, unknown pragma 



namespace Microsoft.InfoCards 
{
    using System; 
    using System.Collections.Generic; 
    using System.Diagnostics;
    using System.IO; //Stream 
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Security.Cryptography.Xml;
    using System.IdentityModel.Tokens; 
    using System.IdentityModel.Selectors;
    using System.ServiceModel; 
    using System.ServiceModel.Security; 
    using System.ServiceModel.Security.Tokens;
    using System.Text; 
    using System.Xml;
    using System.Globalization;
    using System.Xml.Schema;
    using System.Net.Security; 

    using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace; 
 

    // 
    // Summary:
    // Implements offline chain trust validation
    // Really a helper class to InfoCardX509Validator which has two copies of this
    // class - one for m_useMachineContext = true and another for m_useMachineContext = false. 
    //
    class InfoCardOfflineChainTrustValidator : X509CertificateValidator 
    { 
        bool m_useMachineContext;
 
        //
        // Summary: Constructor
        //
        // Parameters: 
        // useMachineContext - whether to use machine context while validating
        // 
        public InfoCardOfflineChainTrustValidator( bool useMachineContext ) 
        {
            m_useMachineContext = useMachineContext; 
        }


        public override void Validate( X509Certificate2 certificate ) 
        {
            X509Chain chain; 
            Validate( certificate, null, out chain ); 
            chain = null;
        } 

        //
        // Summary:
        // Implements trust chain validation 
        //
        // Parameters: 
        // certificate - the certificate to check 
        // chain - chain information from the certificate
        // supportingCerts - intermediate certs that may be needed for chain building 
        //                   (if they are not already on the box)
        //
        // Remarks - as to why we are doing offline:
        // XP: we have to be offline for perf reasons, IE7 is same 
        // Vista:  The crypto team has made online checking much better. IE7 does online checking.
        // So we could use WinVerifyTrust (which uses IE settings does Online on Vista and Offline on XP) 
        // However we want a consistent story for STS builders for XP and Vista. We want the CRL technology to advance a bit more before we force STS builders to support online validation. 
        // In addition InfoCard does not support I/O cancellation semantics in the UI currently so we are
        // not inclined to take the network hit of doing the CRL checking at this time. 
        //
        public void Validate( X509Certificate2 certificate, X509Certificate2Collection supportingCerts, out X509Chain chain )
        {
            if( null == certificate ) 
            {
                throw IDT.ThrowHelperArgumentNull( "certificate" ); 
            } 

            chain = new X509Chain( m_useMachineContext ); 

            //
            // chain.ChainPolicy creates a new X509ChainPolicy if one is not already set.
            // We cannot have static X509ChainPolicy becuase we need to add different supportingCerts 
            // each time
            // 
            if( null != supportingCerts ) 
            {
                chain.ChainPolicy.ExtraStore.AddRange( supportingCerts ); 
            }
            chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
            chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;
            chain.ChainPolicy.VerificationFlags = 
                X509VerificationFlags.IgnoreCertificateAuthorityRevocationUnknown
                | X509VerificationFlags.IgnoreEndRevocationUnknown 
                | X509VerificationFlags.IgnoreRootRevocationUnknown 
                | X509VerificationFlags.IgnoreCtlSignerRevocationUnknown;
 
            if( !chain.Build( certificate ) )
            {
                throw IDT.ThrowHelperError(
                        new SecurityTokenValidationException( SR.GetString( 
                        SR.X509ChainBuildFail,
                        GetCertificateId( certificate ), 
                        GetChainStatusInformation( chain.ChainStatus ) ) ) ); 
            }
        } 

        internal static string GetCertificateId( X509Certificate2 certificate )
        {
            StringBuilder str = new StringBuilder( 256 ); 
            AppendCertificateIdentityName( str, certificate );
            return str.ToString(); 
        } 

        // 
        // Summary:
        //  Concatenate chain status information to return a string
        //
        // Parameters: 
        //  chainStatus - resulting from a chain.Build
        // 
        static string GetChainStatusInformation( X509ChainStatus[] chainStatus ) 
        {
            if( chainStatus != null ) 
            {
                StringBuilder error = new StringBuilder( 256 );
                for( int i = 0; i < chainStatus.Length; ++i )
                { 
                    //
                    // Exclude CRL offline errors because we ignore those 
                    // 
                    if( X509ChainStatusFlags.RevocationStatusUnknown != chainStatus[ i ].Status &&
                        X509ChainStatusFlags.OfflineRevocation != chainStatus[ i ].Status ) 
                    {
                        error.Append( chainStatus[ i ].StatusInformation );
                        error.Append( " " );
                    } 
                }
                return error.ToString(); 
            } 
            return String.Empty;
        } 

        //
        // Summary:
        //  Create a displayable name for the certificate 
        //
        // Parameters: 
        // str - the StringBuilder to populate 
        // certificate - the certificate to check
        // 
        static void AppendCertificateIdentityName( StringBuilder str, X509Certificate2 certificate )
        {
            string value = certificate.SubjectName.Name;
            if( String.IsNullOrEmpty( value ) ) 
            {
                value = certificate.GetNameInfo( X509NameType.DnsName, false ); 
                if( String.IsNullOrEmpty( value ) ) 
                {
                    value = certificate.GetNameInfo( X509NameType.SimpleName, false ); 
                    if( String.IsNullOrEmpty( value ) )
                    {
                        value = certificate.GetNameInfo( X509NameType.EmailName, false );
                        if( String.IsNullOrEmpty( value ) ) 
                        {
                            value = certificate.GetNameInfo( X509NameType.UpnName, false ); 
                        } 
                    }
                } 
            }
            // Same format as X509Identity
            str.Append( String.IsNullOrEmpty( value ) ? "" : value );
            str.Append( "; " ); 
            str.Append( certificate.Thumbprint );
        } 
    } 

    // 
    // Summary: Primary class to cater for all our X509 validation needs
    // Several uses for this class.
    // a) Need to pass this to Indigo (in InfoCardServiceClientCredentials)
    //    so need to derive from X509CertificateValidator 
    //    i) Indigo wrt RemoteTokenFactory
    //    ii) Indigo wrt Browser case 
    // b) Validation of incoming recipient certs 
    // c) Validation of cert during import of managed card
    // d) Building the chain in HA cert, before calling the native API to determine if root is HA 
    //
    // Question:
    // Why can't we just use Indigo's validators in System.IdentityModel.dll?
    // 
    // Answer:
    // After discussion with Indigo Security team (that owns the validators in System.IdentityModel.dll) 
    // it was deemed suitable to have a custom validator as we have done so. 
    //
    // The primary reason is the following: 
    // Indigo's out-of-the-box validators support
    //   a) {chaining up to LM Trusted CA} OR {Peer Trust}
    //   b) {chaining up to CU Trusted CA} OR {Peer Trust}
    // But CardSpace needs special behavior of 
    // allowing certs {chaining up to LM or CU Trusted CA} OR {Peer Trust}.
    // 
    // In addition, CardSpace needs additional info from the results of the Validation 
    // that are NOT exposed by Indigo's out-of-the-box validator:
    // (1) We (CardSpace) need to know if the validation passed via chain building or via peer trust. 
    //     This is because we compute a recipient id based on this boolean.
    // (2) We (CardSpace) need access to the X509Chain that was built by the validator.
    //     This is because we need to pass the chain context to a native function that helps determine
    //     if the cert (representing the relying party) is EV. Without overriding Indigo's validator 
    //     our code would have to call chain.Build() separately which is ESSENTIALLY what the validator does.
    //     Therefore having a custom validator for CardSpace would save duplication of effort 
    //     effort in building the chain for a given cert. 
    //
    class InfoCardX509Validator : X509CertificateValidator 
    {
        static InfoCardOfflineChainTrustValidator s_identityMachineChainTrustValidator = null;
        static InfoCardOfflineChainTrustValidator s_identityUserChainTrustValidator = null;
        static X509CertificateValidator s_identityPeerTrustValidator; 

        static X509Certificate2Collection m_supportingCertificates; 
 
        static InfoCardX509Validator()
        { 
            s_identityMachineChainTrustValidator = new InfoCardOfflineChainTrustValidator( true );
            s_identityUserChainTrustValidator = new InfoCardOfflineChainTrustValidator( false );
            s_identityPeerTrustValidator  = X509CertificateValidator.PeerTrust;
 
        }
 
        InfoCardX509Validator( X509Certificate2Collection supportingCerts ) 
        {
            m_supportingCertificates = supportingCerts; 
        }

        public static X509CertificateValidator Create( X509Certificate2Collection supportingCerts )
        { 
            return ( X509CertificateValidator ) new InfoCardX509Validator( supportingCerts );
        } 
 
        //
        // Summary: 
        // Called within InfoCard
        // The InfoCardX509Validator validation passes if either
        // peer or chain trust validation passes. Chain trust
        // validation passes either for user context or machine context 
        //
        // Parameters: 
        // certificate - the certificate to check 
        // chainTrustPassed - out param returning true if chainTrust passed (as opposed to peer trust)
        // supportingRecipientCerts - intermediate certs that may be needed for chain building 
        //                   (if they are not already on the box)
        //
        public static void ValidateChainOrPeer( X509Certificate2 certificate, X509Certificate2Collection supportingRecipientCerts, out bool chainTrustPassed )
        { 
            Validate( certificate, supportingRecipientCerts, out chainTrustPassed );
        } 
 

        // 
        // Summary:
        // Called by Indigo
        // SAME behavior as ValidateChainOrPeer, but this matches the abstract function
        // that we need to override. 
        //
        // Parameters: 
        // certificate - the certificate to check 
        //
        public override void Validate( X509Certificate2 certificate ) 
        {
            bool chainPassed;
            //
            // In RemoteTokenFactory we override Indigo's validator with this one 
            // (so that CardSpace can have specialized behavior e.g. offline CRL validation).
            // This does not have an incoming chain but we would have initialized m_supportingCertificates 
            // in the constructor 
            //
            Validate( certificate, m_supportingCertificates, out chainPassed ); 
        }


        // 
        // Summary:
        // Called from .NET 
        // SAME behavior as ValidateChainOrPeer 
        //
        // sender: An object that contains state information for this validation. 
        // certificate: The certificate used to authenticate the remote party.
        // chain: The chain of certificate authorities associated with the remote certificate.
        // sslPolicyErrors: One or more errors associated with the remote certificate.
        // 
        // Return Value
        // A Boolean value that determines whether the specified certificate is accepted for authentication. 
        // 
        // Remarks:
        // 
        // Even if there are no policy errors in "sslPolicyErrors" [i.e. SslPolicyErrors.None]
        // we still need to check for revocation, i.e. need another chain build
        //
        // 
        // If there are already policy errors but no name errors
        // (e.g. SslPolicyErrors.RemoteCertificateChainErrors) then we could optimize this function 
        // to just check for peerTrust in that case 
        //
        // 
        public static bool ValidateChainOrPeerCallback(
            object sender,
            X509Certificate certificate,
            X509Chain chain, 
            SslPolicyErrors sslPolicyErrors )
        { 
            bool chainTrustPassedTemp = false; 

            if( 0 != ( sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch ) 
                || 0 != ( sslPolicyErrors & SslPolicyErrors.RemoteCertificateNotAvailable ) )
            {
                return false;
            } 

            IDT.Assert( ( null != certificate ) && ( null != chain ), 
                "We should have returned false for SslPolicyErrors.RemoteCertificateNotAvailable" ); 

            try 
            {
                ValidateChainOrPeer( new X509Certificate2( certificate ), chain.ChainPolicy.ExtraStore, out chainTrustPassedTemp );
            }
            catch( SecurityTokenValidationException stve ) 
            {
                // 
                // Log this case with the best matching resource string we have currently 
                //
                IDT.TraceAndLogException( 
                    new IdentityValidationException(
                        SR.GetString( SR.RecipientCertificateNotValid ),
                        stve ) );
                return false; 
            }
 
 
            return true;
 
        }

        //
        // Summary: 
        // Gate function to Validate* functions
        // 
        // Parameters: 
        // certificate - the certificate to check
        // chainTrustPassed - out param returning true if chainTrust passed (as opposed to peer trust) 
        // supportingCerts - intermediate certs that may be needed for chain building
        //                   (if they are not already on the box)
        //
        private static void Validate( X509Certificate2 certificate, X509Certificate2Collection supportingRecipientCerts, out bool chainTrustPassed ) 
        {
            chainTrustPassed = false; 
            string chainTrustMessage = String.Empty; 

            try 
            {
                X509Chain chain;
                InfoCardX509Validator.ValidateChain( certificate, supportingRecipientCerts, out chain );
                chainTrustPassed = true; 
            }
            catch( SecurityTokenValidationException e ) 
            { 
               chainTrustMessage = e.Message;
            } 


            if( !chainTrustPassed )
            { 
               try
                { 
                    InfoCardX509Validator.ValidatePeer( certificate ); 
                }
                catch( SecurityTokenValidationException ex ) 
                {
                     throw IDT.ThrowHelperError(
                        new SecurityTokenValidationException(
                        SR.GetString( SR.X509ChainFailAndPeerTrustFail, chainTrustMessage, ex.Message ) ) ); 
                }
            } 
            else 
            {
                return; 
            }
        }

 
        //
        // Summary: 
        // Validates that the given certificate chains to either the machine or user trusted roots. 
        //
        // Parameters: 
        // certificate - the certificate to check
        // chain - the chain that is constructed from chain.Build( certificate )
        // supportingCerts - intermediate certs that may be needed for chain building
        //                   (if they are not already on the box) 
        //
        public static void ValidateChain( X509Certificate2 certificate, X509Certificate2Collection supportingCerts, out X509Chain chain ) 
        { 
            try
            { 
                //
                // First try machine context
                //
                s_identityMachineChainTrustValidator.Validate( certificate, supportingCerts, out chain ); 
            }
            catch( SecurityTokenValidationException ) 
            { 
                //
                // Then try user context 
                //
                s_identityUserChainTrustValidator.Validate( certificate, supportingCerts, out chain );
            }
        } 

        // 
        // Summary: 
        // Validates that the given certificate chains to either the machine or user trusted roots.
        // 
        // Parameters:
        // certificate - the certificate to check
        // supportingCerts - intermediate certs that may be needed for chain building
        //                   (if they are not already on the box) 
        //
        public static bool TryValidateChain( X509Certificate2 certificate, X509Certificate2Collection supportingCerts ) 
        { 
            bool isValid = true;
            try 
            {
                X509Chain chain;
                ValidateChain( certificate, supportingCerts, out chain );
            } 
            catch( SecurityTokenValidationException )
            { 
                isValid = false; 
            }
            return isValid; 
        }


        // 
        // Summary:
        // Validates that the given certificate is conforms to our peer cert policy: 
        // must be in user / trusted people 
        // must not be in user / untrusted certificates
        // must be within date. 
        //
        // Parameters:
        // certificate - the certificate to check
        // 
        public static void ValidatePeer( X509Certificate2 certificate )
        { 
            s_identityPeerTrustValidator.Validate( certificate ); 
        }
 


    }
 
}

// 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