File: System\IdentityModel\X509CertificateValidatorEx.cs
Project: ndp\cdf\src\WCF\IdentityModel\System.IdentityModel.csproj (System.IdentityModel)
//-----------------------------------------------------------------------
// <copyright file="X509CertificateValidatorEx.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
 
namespace System.IdentityModel
{
    using System;
    using System.IdentityModel.Selectors;
    using System.Security.Cryptography.X509Certificates;
    using System.ServiceModel.Security;
 
    /// <summary>
    /// This class wraps the four WCF validator types (Peer, Chain, PeerOrChain, and None).
    /// This class also resets the validation time each time a certificate is validated, to fix a .NET issue
    /// where certificates created after the validator is created will not chain.
    /// </summary>
    internal class X509CertificateValidatorEx : X509CertificateValidator
    {
        private X509CertificateValidationMode certificateValidationMode;
        private X509ChainPolicy chainPolicy;
        private X509CertificateValidator validator;
 
        public X509CertificateValidatorEx(
            X509CertificateValidationMode certificateValidationMode,
            X509RevocationMode revocationMode,
            StoreLocation trustedStoreLocation)
        {
            this.certificateValidationMode = certificateValidationMode;
 
            switch (this.certificateValidationMode)
            {
                case X509CertificateValidationMode.None:
                    {
                        this.validator = X509CertificateValidator.None;
                        break;
                    }
 
                case X509CertificateValidationMode.PeerTrust:
                    {
                        this.validator = X509CertificateValidator.PeerTrust;
                        break;
                    }
 
                case X509CertificateValidationMode.ChainTrust:
                    {
                        bool useMachineContext = trustedStoreLocation == StoreLocation.LocalMachine;
                        this.chainPolicy = new X509ChainPolicy();
                        this.chainPolicy.RevocationMode = revocationMode;
 
                        this.validator = X509CertificateValidator.CreateChainTrustValidator(useMachineContext, this.chainPolicy);
                        break;
                    }
 
                case X509CertificateValidationMode.PeerOrChainTrust:
                    {
                        bool useMachineContext = trustedStoreLocation == StoreLocation.LocalMachine;
                        this.chainPolicy = new X509ChainPolicy();
                        this.chainPolicy.RevocationMode = revocationMode;
 
                        this.validator = X509CertificateValidator.CreatePeerOrChainTrustValidator(useMachineContext, this.chainPolicy);
                        break;
                    }
 
                case X509CertificateValidationMode.Custom:
                default:
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.ID4256)));
            }
        }
 
        public override void Validate(X509Certificate2 certificate)
        {
            if (this.certificateValidationMode == X509CertificateValidationMode.ChainTrust ||
                 this.certificateValidationMode == X509CertificateValidationMode.PeerOrChainTrust)
            {
                // This is needed due to a .NET issue where the validation time is not properly set, 
                // causing certificates created after the creation of the validator to fail chain trust.
                this.chainPolicy.VerificationTime = DateTime.Now;
            }
 
            this.validator.Validate(certificate);
        }
    }
}