//****************************************************************************************************** // SimpleCertificateChecker.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 11/06/2012 - Stephen C. Wills // Generated original version of source code. // 12/14/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System.Collections.Generic; using System.Linq; using System.Net.Security; using System.Security.Cryptography.X509Certificates; namespace GSF.Net.Security { /// /// Simple implementation of . /// public class SimpleCertificateChecker : ICertificateChecker { #region [ Members ] // Fields private readonly List m_trustedCertificates; private SslPolicyErrors m_validPolicyErrors; private X509ChainStatusFlags m_validChainFlags; private string m_reasonForFailure; #endregion #region [ Constructors ] /// /// Creates a new instance of the class. /// public SimpleCertificateChecker() { m_trustedCertificates = new List(); m_validPolicyErrors = SslPolicyErrors.None; m_validChainFlags = X509ChainStatusFlags.NoError; } #endregion #region [ Properties ] /// /// Gets the list of certificates on the system which are /// considered trusted when validating remote certificates. /// public List TrustedCertificates { get { return m_trustedCertificates; } } /// /// Gets or sets the set of invalid policy errors. /// public SslPolicyErrors ValidPolicyErrors { get { return m_validPolicyErrors; } set { m_validPolicyErrors = value; } } /// /// Gets or sets the set of invalid chain flags. /// public X509ChainStatusFlags ValidChainFlags { get { return m_validChainFlags; } set { m_validChainFlags = value; } } /// /// Gets the reason why the remote certificate validation /// failed, or null if certificate validation did not fail. /// public string ReasonForFailure { get { return m_reasonForFailure; } } #endregion #region [ Methods ] /// /// Validates the given remote certificate to determine if the host is trusted. /// /// Certificate of the remote host. /// True if the remote certificate is trusted; false otherwise. public bool ValidateRemoteCertificate(X509Certificate remoteCertificate) { byte[] hash = remoteCertificate.GetCertHash(); byte[] key = remoteCertificate.GetPublicKey(); bool hashMatch, keyMatch; foreach (X509Certificate certificate in TrustedCertificates) { hashMatch = hash.SequenceEqual(certificate.GetCertHash()); keyMatch = hashMatch && key.SequenceEqual(certificate.GetPublicKey()); if (keyMatch) return true; } m_reasonForFailure = "No matching certificate found in the list of trusted certificates."; return false; } /// /// Verifies the remote certificate used for authentication. /// /// An object that contains state information for this validation. /// The certificate used to authenticate the remote party. /// The chain of certificate authorities associated with the remote certificate. /// One or more errors associated with the remote certificate. /// A flag that determines whether the specified certificate is accepted for authentication. public bool ValidateRemoteCertificate(object sender, X509Certificate remoteCertificate, X509Chain chain, SslPolicyErrors errors) { X509ChainStatusFlags chainFlags; m_reasonForFailure = null; if ((errors & ~m_validPolicyErrors) != SslPolicyErrors.None) { m_reasonForFailure = string.Format("Policy errors encountered during validation: {0}", errors & ~m_validPolicyErrors); return false; } chainFlags = chain.ChainStatus.Aggregate(X509ChainStatusFlags.NoError, (flags, status) => flags | (status.Status & ~m_validChainFlags)); if (chainFlags != X509ChainStatusFlags.NoError) { m_reasonForFailure = string.Format("Invalid chain flags found during validation: {0}", chainFlags); return false; } return ValidateRemoteCertificate(remoteCertificate); } #endregion } }