From 0af984a39b46915d4ce1762edb4bdda8c41faef7 Mon Sep 17 00:00:00 2001 From: Sebastien Pouliot Date: Wed, 19 Feb 2014 08:29:23 -0500 Subject: [PATCH] [SSL/TLS] Add support to select and/or re-order ciphers suites [#16327] --- .../TlsServerHello.cs | 4 +- .../TlsClientHello.cs | 4 +- .../CipherSuiteCollection.cs | 384 +++++++-------------- .../CipherSuiteFactory.cs | 85 ++++- .../Mono.Security.Protocol.Tls/Context.cs | 4 +- .../SecurityProtocolType.cs | 7 +- .../Mono.Security.Protocol.Tls/SslClientStream.cs | 2 +- .../Mono.Security.Protocol.Tls/SslServerStream.cs | 2 +- 8 files changed, 212 insertions(+), 280 deletions(-) rewrite mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs (68%) diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs index bb662a02d05..0cbecb5f261 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Client/TlsServerHello.cs @@ -133,9 +133,7 @@ namespace Mono.Security.Protocol.Tls.Handshake.Client (this.Context.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default) { this.Context.SecurityProtocol = serverProtocol; - this.Context.SupportedCiphers.Clear(); - this.Context.SupportedCiphers = null; - this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(serverProtocol); + this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (false, serverProtocol); DebugHelper.WriteLine("Selected protocol {0}", serverProtocol); } diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs index 8a54ba785c4..1172626f3c1 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls.Handshake.Server/TlsClientHello.cs @@ -115,9 +115,7 @@ namespace Mono.Security.Protocol.Tls.Handshake.Server (this.Context.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default) { this.Context.SecurityProtocol = clientProtocol; - this.Context.SupportedCiphers.Clear(); - this.Context.SupportedCiphers = null; - this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(clientProtocol); + this.Context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (true, clientProtocol); } else { diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs dissimilarity index 68% index 8240e82a27f..973f4b788b8 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteCollection.cs @@ -1,254 +1,130 @@ -// Transport Security Layer (TLS) -// Copyright (c) 2003-2004 Carlos Guzman Alvarez - -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -using System; -using System.Collections; -using System.Globalization; -using System.Security.Cryptography; - -namespace Mono.Security.Protocol.Tls -{ - internal sealed class CipherSuiteCollection : ICollection, IList, IEnumerable - { - #region Fields - - private ArrayList cipherSuites; - private SecurityProtocolType protocol; - - #endregion - - #region Indexers - - public CipherSuite this[string name] - { - get { return (CipherSuite)this.cipherSuites[this.IndexOf(name)]; } - set { this.cipherSuites[this.IndexOf(name)] = (CipherSuite)value; } - } - - public CipherSuite this[int index] - { - get { return (CipherSuite)this.cipherSuites[index]; } - set { this.cipherSuites[index] = (CipherSuite)value; } - } - - public CipherSuite this[short code] - { - get { return (CipherSuite)this.cipherSuites[this.IndexOf(code)]; } - set { this.cipherSuites[this.IndexOf(code)] = (CipherSuite)value; } - } - - object IList.this[int index] - { - get { return this[index]; } - set { this[index] = (CipherSuite)value; } - } - - #endregion - - #region ICollection Properties - - bool ICollection.IsSynchronized - { - get { return this.cipherSuites.IsSynchronized; } - } - - object ICollection.SyncRoot - { - get { return this.cipherSuites.SyncRoot; } - } - - public int Count - { - get { return this.cipherSuites.Count; } - } - - #endregion - - #region IList Properties - - public bool IsFixedSize - { - get { return this.cipherSuites.IsFixedSize; } - } - - public bool IsReadOnly - { - get { return this.cipherSuites.IsReadOnly; } - } - - #endregion - - #region Constructors - - public CipherSuiteCollection(SecurityProtocolType protocol) : base() - { - this.protocol = protocol; - this.cipherSuites = new ArrayList(); - } - - #endregion - - #region ICollection Methods - - public void CopyTo(Array array, int index) - { - this.cipherSuites.CopyTo(array, index); - } - - #endregion - - #region IEnumerable Methods - - IEnumerator IEnumerable.GetEnumerator() - { - return this.cipherSuites.GetEnumerator(); - } - - #endregion - - #region IList Methods - - public void Clear() - { - this.cipherSuites.Clear(); - } - - bool IList.Contains(object value) - { - return this.cipherSuites.Contains(value as CipherSuite); - } - - public int IndexOf(string name) - { - int index = 0; - - foreach (CipherSuite cipherSuite in this.cipherSuites) - { - if (this.cultureAwareCompare(cipherSuite.Name, name)) - { - return index; - } - index++; - } - - return -1; - } - - public int IndexOf(short code) - { - int index = 0; - - foreach (CipherSuite cipherSuite in this.cipherSuites) - { - if (cipherSuite.Code == code) - { - return index; - } - index++; - } - - return -1; - } - - int IList.IndexOf(object value) - { - return this.cipherSuites.IndexOf(value as CipherSuite); - } - - void IList.Insert(int index, object value) - { - this.cipherSuites.Insert(index, value as CipherSuite); - } - - void IList.Remove(object value) - { - this.cipherSuites.Remove(value as CipherSuite); - } - - void IList.RemoveAt(int index) - { - this.cipherSuites.RemoveAt(index); - } - - public CipherSuite Add( - short code, string name, CipherAlgorithmType cipherType, - HashAlgorithmType hashType, ExchangeAlgorithmType exchangeType, - bool exportable, bool blockMode, byte keyMaterialSize, - byte expandedKeyMaterialSize, short effectiveKeyBytes, - byte ivSize, byte blockSize) - { - switch (this.protocol) - { - case SecurityProtocolType.Default: - case SecurityProtocolType.Tls: - return this.add( - new TlsCipherSuite( - code, name, cipherType, hashType, exchangeType, exportable, - blockMode, keyMaterialSize, expandedKeyMaterialSize, - effectiveKeyBytes, ivSize, blockSize)); - - case SecurityProtocolType.Ssl3: - return this.add( - new SslCipherSuite( - code, name, cipherType, hashType, exchangeType, exportable, - blockMode, keyMaterialSize, expandedKeyMaterialSize, - effectiveKeyBytes, ivSize, blockSize)); - - case SecurityProtocolType.Ssl2: - default: - throw new NotSupportedException("Unsupported security protocol type."); - } - } - - private TlsCipherSuite add(TlsCipherSuite cipherSuite) - { - this.cipherSuites.Add(cipherSuite); - - return cipherSuite; - } - - private SslCipherSuite add(SslCipherSuite cipherSuite) - { - this.cipherSuites.Add(cipherSuite); - - return cipherSuite; - } - - int IList.Add(object value) - { - return this.cipherSuites.Add(value as CipherSuite); - } - - private bool cultureAwareCompare(string strA, string strB) - { - return CultureInfo.CurrentCulture.CompareInfo.Compare( - strA, - strB, - CompareOptions.IgnoreKanaType | CompareOptions.IgnoreWidth | - CompareOptions.IgnoreCase) == 0 ? true : false; - } - - #endregion - } -} +// Transport Security Layer (TLS) +// Copyright (c) 2003-2004 Carlos Guzman Alvarez +// Copyright 2013-2014 Xamarin Inc. + +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; + +namespace Mono.Security.Protocol.Tls { + + internal sealed class CipherSuiteCollection : List { + + #region Fields + + SecurityProtocolType protocol; + + #endregion + + #region Indexers + + public CipherSuite this [string name] { + get { + int n = IndexOf (name); + return n == -1 ? null : this [n]; + } + } + + public CipherSuite this [short code] { + get { + int n = IndexOf (code); + return n == -1 ? null : this [n]; + } + } + + #endregion + + #region Constructors + + public CipherSuiteCollection (SecurityProtocolType protocol) + { + switch (protocol) { + case SecurityProtocolType.Default: + case SecurityProtocolType.Tls: + case SecurityProtocolType.Ssl3: + this.protocol = protocol; + break; + case SecurityProtocolType.Ssl2: + default: + throw new NotSupportedException ("Unsupported security protocol type."); + } + } + + #endregion + + #region Methods + + public int IndexOf (string name) + { + int index = 0; + foreach (CipherSuite cipherSuite in this) { + if (String.CompareOrdinal (name, cipherSuite.Name) == 0) + return index; + index++; + } + return -1; + } + + public int IndexOf (short code) + { + int index = 0; + foreach (CipherSuite cipherSuite in this) { + if (cipherSuite.Code == code) + return index; + index++; + } + return -1; + } + + public void Add ( + short code, string name, CipherAlgorithmType cipherType, + HashAlgorithmType hashType, ExchangeAlgorithmType exchangeType, + bool exportable, bool blockMode, byte keyMaterialSize, + byte expandedKeyMaterialSize, short effectiveKeyBytes, + byte ivSize, byte blockSize) + { + switch (protocol) { + case SecurityProtocolType.Default: + case SecurityProtocolType.Tls: + Add (new TlsCipherSuite (code, name, cipherType, hashType, exchangeType, exportable, blockMode, + keyMaterialSize, expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)); + break; + + case SecurityProtocolType.Ssl3: + Add (new SslCipherSuite (code, name, cipherType, hashType, exchangeType, exportable, blockMode, + keyMaterialSize, expandedKeyMaterialSize, effectiveKeyBytes, ivSize, blockSize)); + break; + } + } + + public IList GetNames () + { + var list = new List (Count); + foreach (CipherSuite cipherSuite in this) + list.Add (cipherSuite.Name); + return list; + } + + #endregion + } +} diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs index 3fa618a280a..a9d05ed78bb 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/CipherSuiteFactory.cs @@ -1,5 +1,6 @@ // Transport Security Layer (TLS) // Copyright (c) 2003-2004 Carlos Guzman Alvarez +// Copyright 2013-2014 Xamarin Inc. // // Permission is hereby granted, free of charge, to any person obtaining @@ -23,26 +24,81 @@ // using System; +using System.Collections.Generic; +using System.Reflection; +using System.Net; namespace Mono.Security.Protocol.Tls { - internal class CipherSuiteFactory + internal static class CipherSuiteFactory { - public static CipherSuiteCollection GetSupportedCiphers(SecurityProtocolType protocol) +#if !INSIDE_SYSTEM && !BOOTSTRAP_BASIC + static Type spm = typeof (ServicePointManager); + static PropertyInfo client_callback; + static PropertyInfo server_callback; +#endif + + public static CipherSuiteCollection GetSupportedCiphers (bool server, SecurityProtocolType protocol) { - switch (protocol) - { - case SecurityProtocolType.Default: - case SecurityProtocolType.Tls: - return CipherSuiteFactory.GetTls1SupportedCiphers(); - - case SecurityProtocolType.Ssl3: - return CipherSuiteFactory.GetSsl3SupportedCiphers(); - - case SecurityProtocolType.Ssl2: - default: - throw new NotSupportedException("Unsupported security protocol type"); + CipherSuiteCollection suites; + switch (protocol) { + case SecurityProtocolType.Default: + case SecurityProtocolType.Tls: + suites = CipherSuiteFactory.GetTls1SupportedCiphers (); + break; + case SecurityProtocolType.Ssl3: + suites = CipherSuiteFactory.GetSsl3SupportedCiphers (); + break; + case SecurityProtocolType.Ssl2: + default: + throw new NotSupportedException ("Unsupported security protocol type"); + } + + IEnumerable list = null; +#if INSIDE_SYSTEM + // if SSL/TLS support is built-in System.dll (e.g. monotouch) then we can access ServicePointManager + // extension directly + var cb = server ? ServicePointManager.ServerCipherSuitesCallback : ClientCipherSuitesCallback; + if (cb == null) + return suites; // e.g. no callback was set + + list = cb ((System.Net.SecurityProtocolType) (int) protocol, suites.GetNames ()); +#elif !BOOTSTRAP_BASIC + // Mono.Security must work on MS.NET so it cannot depend on any Mono-specific extensions + PropertyInfo pi = null; + if (server) { + if (server_callback == null) + server_callback = spm.GetProperty ("ServerCipherSuitesCallback", BindingFlags.Static | BindingFlags.Public); + pi = server_callback; + } else { + if (client_callback == null) + client_callback = spm.GetProperty ("ClientCipherSuitesCallback", BindingFlags.Static | BindingFlags.Public); + pi = client_callback; + } + if (pi == null) + return suites; // e.g. MS runtime - return every supported suites + + var cb = (Delegate) pi.GetGetMethod ().Invoke (null, null); + if (cb == null) + return suites; // e.g. no callback was set - return every supported suites + + list = (IEnumerable) cb.DynamicInvoke (new object[] { + (System.Net.SecurityProtocolType) (int) protocol, suites.GetNames () + }); +#else + // TODO: right now the callback is only available when using System.Net.* types for SSL/TLS + return suites; +#endif + CipherSuiteCollection allowed = new CipherSuiteCollection (protocol); + if (list != null) { + foreach (var name in list) { + // add any supported (ignore unknowns) ciphers requested by the callback + var cipher = suites [name]; + if (cipher != null) + allowed.Add (cipher); + } } + return allowed; } #region Private Static Methods @@ -132,6 +188,7 @@ namespace Mono.Security.Protocol.Tls // Supported ciphers scs.Add((0x00 << 0x08) | 0x35, "SSL_RSA_WITH_AES_256_CBC_SHA", CipherAlgorithmType.Rijndael, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 32, 32, 256, 16, 16); + scs.Add((0x00 << 0x08) | 0x2F, "SSL_RSA_WITH_AES_128_CBC_SHA", CipherAlgorithmType.Rijndael, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 16, 16, 128, 16, 16); scs.Add((0x00 << 0x08) | 0x0A, "SSL_RSA_WITH_3DES_EDE_CBC_SHA", CipherAlgorithmType.TripleDes, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, true, 24, 24, 168, 8, 8); scs.Add((0x00 << 0x08) | 0x05, "SSL_RSA_WITH_RC4_128_SHA", CipherAlgorithmType.Rc4, HashAlgorithmType.Sha1, ExchangeAlgorithmType.RsaKeyX, false, false, 16, 16, 128, 0, 0); scs.Add((0x00 << 0x08) | 0x04, "SSL_RSA_WITH_RC4_128_MD5", CipherAlgorithmType.Rc4, HashAlgorithmType.Md5, ExchangeAlgorithmType.RsaKeyX, false, false, 16, 16, 128, 0, 0); diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/Context.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/Context.cs index 340913fa040..b4caf28b5c7 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/Context.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/Context.cs @@ -432,9 +432,7 @@ namespace Mono.Security.Protocol.Tls (this.SecurityProtocolFlags & SecurityProtocolType.Default) == SecurityProtocolType.Default) { this.SecurityProtocol = protocolType; - this.SupportedCiphers.Clear(); - this.SupportedCiphers = null; - this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(protocolType); + this.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers ((this is ServerContext), protocolType); } else { diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SecurityProtocolType.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SecurityProtocolType.cs index d0693e89d78..787a3924793 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SecurityProtocolType.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SecurityProtocolType.cs @@ -1,5 +1,6 @@ // Transport Security Layer (TLS) // Copyright (c) 2003-2004 Carlos Guzman Alvarez +// Copyright (C) 2014 Xamarin Inc. (http://www.xamarin.com) // // Permission is hereby granted, free of charge, to any person obtaining @@ -38,6 +39,10 @@ namespace Mono.Security.Protocol.Tls Default = -1073741824, Ssl2 = 12, Ssl3 = 48, - Tls = 192 + Tls = 192, +#if NET_4_5 + Tls11 = 768, + Tls12 = 3072, +#endif } } \ No newline at end of file diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs index e615e83e3f6..d0d0721a12d 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslClientStream.cs @@ -400,7 +400,7 @@ namespace Mono.Security.Protocol.Tls } // Obtain supported cipher suites - this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (this.context.SecurityProtocol); + this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (false, context.SecurityProtocol); // Set handshake state this.context.HandshakeState = HandshakeState.Started; diff --git a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs index b0d8bba6ffc..6862c694a61 100644 --- a/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs +++ b/mcs/class/Mono.Security/Mono.Security.Protocol.Tls/SslServerStream.cs @@ -205,7 +205,7 @@ namespace Mono.Security.Protocol.Tls } // Obtain supported cipher suites - this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers(this.context.SecurityProtocol); + this.context.SupportedCiphers = CipherSuiteFactory.GetSupportedCiphers (true, context.SecurityProtocol); // Set handshake state this.context.HandshakeState = HandshakeState.Started; -- 2.11.4.GIT