/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Net.Security; using System.Reflection; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using MongoDB.Shared; namespace MongoDB.Driver { /// /// Represents the settings for using SSL. /// public class SslSettings : IEquatable { // private static fields private static readonly IEqualityComparer __certificateCollectionEqualityComparer = new X509CertificateCollectionEqualityComparer(); // private fields private bool _checkCertificateRevocation = false; private X509CertificateCollection _clientCertificateCollection; private LocalCertificateSelectionCallback _clientCertificateSelectionCallback; private SslProtocols _enabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls; private RemoteCertificateValidationCallback _serverCertificateValidationCallback; // the following fields are set when the SslSettings are frozen private bool _isFrozen; private int _hashCode; // public properties /// /// Gets or sets a value indicating whether to check for certificate revocation. /// public bool CheckCertificateRevocation { get { return _checkCertificateRevocation; } set { if (_isFrozen) { throw new InvalidOperationException("SslSettings is frozen."); } _checkCertificateRevocation = value; } } /// /// Gets or sets the client certificates. /// public IEnumerable ClientCertificates { get { return (_clientCertificateCollection == null) ? null : ((IEnumerable)_clientCertificateCollection).Cast(); } set { if (_isFrozen) { throw new InvalidOperationException("SslSettings is frozen."); } _clientCertificateCollection = (value == null) ? null : new X509CertificateCollection(value.ToArray()); } } /// /// Gets or sets the client certificate selection callback. /// public LocalCertificateSelectionCallback ClientCertificateSelectionCallback { get { return _clientCertificateSelectionCallback; } set { if (_isFrozen) { throw new InvalidOperationException("SslSettings is frozen."); } _clientCertificateSelectionCallback = value; } } /// /// Gets or sets the enabled SSL protocols. /// public SslProtocols EnabledSslProtocols { get { return _enabledSslProtocols; } set { if (_isFrozen) { throw new InvalidOperationException("SslSettings is frozen."); } _enabledSslProtocols = value; } } /// /// Gets or sets the server certificate validation callback. /// public RemoteCertificateValidationCallback ServerCertificateValidationCallback { get { return _serverCertificateValidationCallback; } set { if (_isFrozen) { throw new InvalidOperationException("SslSettings is frozen."); } _serverCertificateValidationCallback = value; } } // internal properties internal X509CertificateCollection ClientCertificateCollection { get { return _clientCertificateCollection; } } // public operators /// /// Determines whether two instances are equal. /// /// The LHS. /// The RHS. /// /// true if the left hand side is equal to the right hand side; otherwise, false. /// public static bool operator ==(SslSettings lhs, SslSettings rhs) { return object.Equals(lhs, rhs); // handles lhs == null correctly } /// /// Determines whether two instances are not equal. /// /// The LHS. /// The RHS. /// /// true if the left hand side is not equal to the right hand side; otherwise, false. /// public static bool operator !=(SslSettings lhs, SslSettings rhs) { return !(lhs == rhs); } // public methods /// /// Clones an SslSettings. /// /// The cloned SslSettings. public SslSettings Clone() { var clone = new SslSettings(); clone._checkCertificateRevocation = _checkCertificateRevocation; clone._clientCertificateCollection = _clientCertificateCollection; // is immutable clone._clientCertificateSelectionCallback = _clientCertificateSelectionCallback; clone._enabledSslProtocols = _enabledSslProtocols; clone._serverCertificateValidationCallback = _serverCertificateValidationCallback; return clone; } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public bool Equals(SslSettings obj) { return Equals((object)obj); // handles obj == null correctly } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object obj) { if (object.ReferenceEquals(obj, null) || GetType() != obj.GetType()) { return false; } var rhs = (SslSettings)obj; return _checkCertificateRevocation == rhs._checkCertificateRevocation && __certificateCollectionEqualityComparer.Equals(_clientCertificateCollection, rhs._clientCertificateCollection) && object.Equals(_clientCertificateSelectionCallback, rhs._clientCertificateSelectionCallback) && _enabledSslProtocols == rhs._enabledSslProtocols && object.Equals(_serverCertificateValidationCallback, rhs._serverCertificateValidationCallback); } /// /// Freezes the settings. /// /// The frozen settings. public SslSettings Freeze() { if (!_isFrozen) { _hashCode = GetHashCode(); _isFrozen = true; } return this; } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { if (_isFrozen) { return _hashCode; } return new Hasher() .Hash(_checkCertificateRevocation) .HashElements(_clientCertificateCollection) .Hash(_clientCertificateSelectionCallback) .Hash(_enabledSslProtocols) .Hash(_serverCertificateValidationCallback) .GetHashCode(); } /// /// Returns a string representation of the settings. /// /// A string representation of the settings. public override string ToString() { var parts = new List(); parts.Add(string.Format("CheckCertificateRevocation={0}", _checkCertificateRevocation)); if (_clientCertificateCollection != null) { parts.Add(string.Format("ClientCertificates=[{0}]", string.Join(",", ((IEnumerable)_clientCertificateCollection).Cast().Select(c => c.Subject).ToArray()))); } if (_clientCertificateSelectionCallback != null) { parts.Add(string.Format("ClientCertificateSelectionCallback={0}", _clientCertificateSelectionCallback.GetMethodInfo().Name)); } parts.Add(string.Format("EnabledProtocols={0}", _enabledSslProtocols)); if (_serverCertificateValidationCallback != null) { parts.Add(string.Format("ServerCertificateValidationCallback={0}", _serverCertificateValidationCallback.GetMethodInfo().Name)); } return string.Format("{{{0}}}", string.Join(",", parts.ToArray())); } // nested classes private class X509CertificateCollectionEqualityComparer : IEqualityComparer { public bool Equals(X509CertificateCollection lhs, X509CertificateCollection rhs) { if (lhs == null) { return rhs == null; } return ((IEnumerable)lhs).Cast().SequenceEqual(((IEnumerable)rhs).Cast()); } public int GetHashCode(X509CertificateCollection obj) { if (obj == null) { throw new ArgumentNullException("obj"); } var hash = 17; foreach (X509Certificate certificate in obj) { hash += 37 * hash + certificate.GetHashCode(); } return hash; } } } }