/* 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;
}
}
}
}