| File: System\IdentityModel\Selectors\X509CertificateValidator.cs | Web Access |
| Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics.Contracts; using System.IdentityModel.Tokens; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.Text; namespace System.IdentityModel.Selectors { public abstract class X509CertificateValidator { private static X509CertificateValidator s_peerTrust; private static X509CertificateValidator s_chainTrust; private static X509CertificateValidator s_peerOrChainTrust; private static X509CertificateValidator s_none; public static X509CertificateValidator None { get { if (s_none == null) { s_none = new NoneX509CertificateValidator(); } return s_none; } } public static X509CertificateValidator PeerTrust { get { if (s_peerTrust == null) { s_peerTrust = new PeerTrustValidator(); } return s_peerTrust; } } public static X509CertificateValidator ChainTrust { get { if (s_chainTrust == null) { s_chainTrust = new ChainTrustValidator(); } return s_chainTrust; } } public static X509CertificateValidator PeerOrChainTrust { get { if (s_peerOrChainTrust == null) { s_peerOrChainTrust = new PeerOrChainTrustValidator(); } return s_peerOrChainTrust; } } public static X509CertificateValidator CreateChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) { if (chainPolicy == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(chainPolicy)); } return new ChainTrustValidator(useMachineContext, chainPolicy, X509CertificateChain.DefaultChainPolicyOID); } public static X509CertificateValidator CreatePeerOrChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) { if (chainPolicy == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(chainPolicy)); } return new PeerOrChainTrustValidator(useMachineContext, chainPolicy); } public abstract void Validate(X509Certificate2 certificate); private class NoneX509CertificateValidator : X509CertificateValidator { public override void Validate(X509Certificate2 certificate) { if (certificate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); } } } private class PeerTrustValidator : X509CertificateValidator { public override void Validate(X509Certificate2 certificate) { if (certificate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); } Exception exception; if (!TryValidate(certificate, out exception)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(exception); } } private static bool StoreContainsCertificate(StoreName storeName, X509Certificate2 certificate) { X509Store store = new X509Store(storeName, StoreLocation.CurrentUser); X509Certificate2Collection certificates = null; try { store.Open(OpenFlags.ReadOnly); certificates = store.Certificates.Find(X509FindType.FindByThumbprint, certificate.Thumbprint, false); return certificates.Count > 0; } finally { SecurityUtils.ResetAllCertificates(certificates); store.Dispose(); } } internal bool TryValidate(X509Certificate2 certificate, out Exception exception) { // Checklist // 1) time validity of cert // 2) in trusted people store // 3) not in disallowed store // The following code could be written as: // DateTime now = DateTime.UtcNow; // if (now > certificate.NotAfter.ToUniversalTime() || now < certificate.NotBefore.ToUniversalTime()) // // this is because X509Certificate2.xxx doesn't return UT. However this would be a SMALL perf hit. // I put an Assert so that this will ensure that the we are compatible with the CLR we shipped with DateTime now = DateTime.Now; Contract.Assert(now.Kind == certificate.NotAfter.Kind && now.Kind == certificate.NotBefore.Kind, ""); if (now > certificate.NotAfter || now < certificate.NotBefore) { exception = new SecurityTokenValidationException(SRP.Format(SRP.X509InvalidUsageTime, SecurityUtils.GetCertificateId(certificate), now, certificate.NotBefore, certificate.NotAfter)); return false; } if (!StoreContainsCertificate(StoreName.TrustedPeople, certificate)) { exception = new SecurityTokenValidationException(SRP.Format(SRP.X509IsNotInTrustedStore, SecurityUtils.GetCertificateId(certificate))); return false; } if (StoreContainsCertificate(StoreName.Disallowed, certificate)) { exception = new SecurityTokenValidationException(SRP.Format(SRP.X509IsInUntrustedStore, SecurityUtils.GetCertificateId(certificate))); return false; } exception = null; return true; } } private class ChainTrustValidator : X509CertificateValidator { private bool _useMachineContext; private X509ChainPolicy _chainPolicy; private uint _chainPolicyOID = X509CertificateChain.DefaultChainPolicyOID; public ChainTrustValidator() { _chainPolicy = null; } public ChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy, uint chainPolicyOID) { _useMachineContext = useMachineContext; _chainPolicy = chainPolicy; _chainPolicyOID = chainPolicyOID; } public override void Validate(X509Certificate2 certificate) { if (certificate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); } // implies _useMachineContext = false // ctor for X509Chain(_useMachineContext, _chainPolicyOID) not present in CoreCLR X509Chain chain = new X509Chain(); if (_chainPolicy != null) { _chainPolicy.VerificationTime = DateTime.Now; chain.ChainPolicy = _chainPolicy; } if (!chain.Build(certificate)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(SRP.Format(SRP.X509ChainBuildFail, SecurityUtils.GetCertificateId(certificate), GetChainStatusInformation(chain.ChainStatus)))); } } private static string GetChainStatusInformation(X509ChainStatus[] chainStatus) { if (chainStatus != null) { StringBuilder error = new StringBuilder(128); for (int i = 0; i < chainStatus.Length; ++i) { error.Append(chainStatus[i].StatusInformation); error.Append(" "); } return error.ToString(); } return String.Empty; } } private class PeerOrChainTrustValidator : X509CertificateValidator { private X509CertificateValidator _chain; private PeerTrustValidator _peer; public PeerOrChainTrustValidator() { _chain = X509CertificateValidator.ChainTrust; _peer = (PeerTrustValidator)X509CertificateValidator.PeerTrust; } public PeerOrChainTrustValidator(bool useMachineContext, X509ChainPolicy chainPolicy) { _chain = X509CertificateValidator.CreateChainTrustValidator(useMachineContext, chainPolicy); _peer = (PeerTrustValidator)X509CertificateValidator.PeerTrust; } public override void Validate(X509Certificate2 certificate) { if (certificate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(certificate)); } Exception exception; if (_peer.TryValidate(certificate, out exception)) { return; } try { _chain.Validate(certificate); } catch (SecurityTokenValidationException ex) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new SecurityTokenValidationException(exception.Message + " " + ex.Message)); } } } } }