summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-08-22 16:42:01 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-08-22 16:42:01 +0700
commit15ff1e2c34e44e9543b875713a36b83a96c0417c (patch)
tree05855e9c035f17e6577bb842d897b9f3c633872e /crypto
parentAdd support for a 'cause' Exception to TlsFatalAlert and use (diff)
downloadBouncyCastle.NET-ed25519-15ff1e2c34e44e9543b875713a36b83a96c0417c.tar.xz
More TLS porting from Java API
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.csproj60
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs232
-rw-r--r--crypto/src/crypto/tls/AbstractTlsPeer.cs48
-rw-r--r--crypto/src/crypto/tls/AbstractTlsServer.cs318
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs333
-rw-r--r--crypto/src/crypto/tls/HeartbeatExtension.cs52
-rw-r--r--crypto/src/crypto/tls/HeartbeatMessage.cs102
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs217
-rw-r--r--crypto/src/crypto/tls/ServerName.cs105
-rw-r--r--crypto/src/crypto/tls/ServerNameList.cs81
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs219
-rw-r--r--crypto/src/crypto/tls/SupplementalDataEntry.cs26
-rw-r--r--crypto/src/crypto/tls/TlsClient.cs58
-rw-r--r--crypto/src/crypto/tls/TlsEccUtilities.cs16
-rw-r--r--crypto/src/crypto/tls/TlsExtensionsUtilities.cs243
-rw-r--r--crypto/src/crypto/tls/TlsPeer.cs43
-rw-r--r--crypto/src/crypto/tls/TlsServer.cs90
-rw-r--r--crypto/src/crypto/tls/TlsSessionImpl.cs54
-rw-r--r--crypto/src/crypto/tls/TlsSrpUtilities.cs41
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs102
20 files changed, 1882 insertions, 558 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index fc577c303..0e9227970 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4279,6 +4279,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\AbstractTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AbstractTlsContext.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4294,6 +4299,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\AbstractTlsPeer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AbstractTlsSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4489,6 +4504,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\HeartbeatExtension.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\HeartbeatMessage.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\HeartbeatMessageType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4574,6 +4599,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\ServerName.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\ServerNameList.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SessionParameters.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4609,6 +4644,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SupplementalDataEntry.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SupplementalDataType.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4729,6 +4769,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsExtensionsUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsFatalAlert.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4799,6 +4844,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsServerContext.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4814,6 +4864,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsSessionImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsSigner.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4829,6 +4884,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsSrpUtilities.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsStream.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
new file mode 100644
index 000000000..3398d94df
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsClient
+        :   AbstractTlsPeer, TlsClient
+    {
+        protected TlsCipherFactory mCipherFactory;
+
+        protected TlsClientContext mContext;
+
+        protected IList mSupportedSignatureAlgorithms;
+        protected int[] mNamedCurves;
+        protected byte[] mClientECPointFormats, mServerECPointFormats;
+
+        protected int mSelectedCipherSuite;
+        protected short mSelectedCompressionMethod;
+
+        public AbstractTlsClient()
+            :   this(new DefaultTlsCipherFactory())
+        {
+        }
+
+        public AbstractTlsClient(TlsCipherFactory cipherFactory)
+        {
+            this.mCipherFactory = cipherFactory;
+        }
+
+        public virtual void Init(TlsClientContext context)
+        {
+            this.mContext = context;
+        }
+
+        public virtual TlsSession GetSessionToResume()
+        {
+            return null;
+        }
+
+        /**
+         * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
+         * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
+         * version number supported by the client, and the value of ClientHello.client_version. No
+         * single value will guarantee interoperability with all old servers, but this is a complex
+         * topic beyond the scope of this document."
+         */
+        public virtual ProtocolVersion ClientHelloRecordLayerVersion
+        {
+            get
+            {
+                // "{03,00}"
+                // return ProtocolVersion.SSLv3;
+
+                // "the lowest version number supported by the client"
+                // return getMinimumVersion();
+
+                // "the value of ClientHello.client_version"
+                return ClientVersion;
+            }
+        }
+
+        public virtual ProtocolVersion ClientVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public virtual IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = null;
+
+            ProtocolVersion clientVersion = mContext.ClientVersion;
+
+            /*
+             * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior to 1.2.
+             * Clients MUST NOT offer it if they are offering prior versions.
+             */
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion))
+            {
+                // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
+
+                byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+                    HashAlgorithm.sha224, HashAlgorithm.sha1 };
+
+                // TODO Sort out ECDSA signatures and add them as the preferred option here
+                byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa };
+
+                this.mSupportedSignatureAlgorithms = Platform.CreateArrayList();
+                for (int i = 0; i < hashAlgorithms.Length; ++i)
+                {
+                    for (int j = 0; j < signatureAlgorithms.Length; ++j)
+                    {
+                        this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i],
+                            signatureAlgorithms[j]));
+                    }
+                }
+
+                /*
+                 * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
+                 */
+                this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
+                    SignatureAlgorithm.dsa));
+
+                clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+                TlsUtilities.AddSignatureAlgorithmsExtension(clientExtensions, mSupportedSignatureAlgorithms);
+            }
+
+            if (TlsEccUtilities.ContainsEccCipherSuites(GetCipherSuites()))
+            {
+                /*
+                 * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+                 * appends these extensions (along with any others), enumerating the curves it supports
+                 * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+                 * Curves Extension and the Supported Point Formats Extension.
+                 */
+                /*
+                 * TODO Could just add all the curves since we support them all, but users may not want
+                 * to use unnecessarily large fields. Need configuration options.
+                 */
+                this.mNamedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
+                this.mClientECPointFormats = new byte[]{ ECPointFormat.uncompressed,
+                    ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+                clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
+
+                TlsEccUtilities.AddSupportedEllipticCurvesExtension(clientExtensions, mNamedCurves);
+                TlsEccUtilities.AddSupportedPointFormatsExtension(clientExtensions, mClientECPointFormats);
+            }
+
+            return clientExtensions;
+        }
+
+        public virtual ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv10; }
+        }
+
+        public virtual void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            if (!MinimumVersion.IsEqualOrEarlierVersionOf(serverVersion))
+                throw new TlsFatalAlert(AlertDescription.protocol_version);
+        }
+
+        public abstract int[] GetCipherSuites();
+
+        public virtual byte[] GetCompressionMethods()
+        {
+            return new byte[]{ CompressionMethod.NULL };
+        }
+
+        public virtual void NotifySessionID(byte[] sessionID)
+        {
+            // Currently ignored
+        }
+
+        public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
+        {
+            this.mSelectedCipherSuite = selectedCipherSuite;
+        }
+
+        public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
+        {
+            this.mSelectedCompressionMethod = selectedCompressionMethod;
+        }
+
+        public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+        {
+            /*
+             * TlsProtocol implementation validates that any server extensions received correspond to
+             * client extensions sent. By default, we don't send any, and this method is not called.
+             */
+            if (serverExtensions != null)
+            {
+                /*
+                 * RFC 5246 7.4.1.4.1. Servers MUST NOT send this extension.
+                 */
+                if (serverExtensions.Contains(ExtensionType.signature_algorithms))
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+                int[] namedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(serverExtensions);
+                if (namedCurves != null)
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+                this.mServerECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(serverExtensions);
+                if (this.mServerECPointFormats != null && !TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+        }
+
+        public virtual void ProcessServerSupplementalData(IList serverSupplementalData)
+        {
+            if (serverSupplementalData != null)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        public abstract TlsKeyExchange GetKeyExchange();
+
+        public abstract TlsAuthentication GetAuthentication();
+
+        public virtual IList GetClientSupplementalData()
+        {
+            return null;
+        }
+
+        public override TlsCompression GetCompression()
+        {
+            switch (mSelectedCompressionMethod)
+            {
+            case CompressionMethod.NULL:
+                return new TlsNullCompression();
+
+            case CompressionMethod.DEFLATE:
+                return new TlsDeflateCompression();
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected compression method was in the list of client-offered compression
+                 * methods, so if we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsPeer.cs b/crypto/src/crypto/tls/AbstractTlsPeer.cs
new file mode 100644
index 000000000..81a53386c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsPeer.cs
@@ -0,0 +1,48 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsPeer
+        :   TlsPeer
+    {
+        public virtual bool ShouldUseGmtUnixTime()
+        {
+            /*
+             * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
+             * TLS implementors MUST by default set the entire value the ClientHello.Random and
+             * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
+             * sequence.
+             */
+            return false;
+        }
+
+        public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+        {
+            if (!secureRenegotiation)
+            {
+                /*
+                 * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead
+                 * of continuing; see Section 4.1/4.3 for discussion.
+                 */
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+        }
+
+        public abstract TlsCompression GetCompression();
+
+        public abstract TlsCipher GetCipher();
+
+        public virtual void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+        }
+
+        public virtual void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+        }
+
+        public virtual void NotifyHandshakeComplete()
+        {
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs
new file mode 100644
index 000000000..036187c02
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -0,0 +1,318 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsServer
+        :   AbstractTlsPeer, TlsServer
+    {
+        protected TlsCipherFactory mCipherFactory;
+
+        protected TlsServerContext mContext;
+
+        protected ProtocolVersion mClientVersion;
+        protected int[] mOfferedCipherSuites;
+        protected byte[] mOfferedCompressionMethods;
+        protected IDictionary mClientExtensions;
+
+        protected bool mEncryptThenMacOffered;
+        protected short mMaxFragmentLengthOffered;
+        protected bool mTruncatedHMacOffered;
+        protected IList mSupportedSignatureAlgorithms;
+        protected bool mEccCipherSuitesOffered;
+        protected int[] mNamedCurves;
+        protected byte[] mClientECPointFormats, mServerECPointFormats;
+
+        protected ProtocolVersion mServerVersion;
+        protected int mSelectedCipherSuite;
+        protected byte mSelectedCompressionMethod;
+        protected IDictionary mServerExtensions;
+
+        public AbstractTlsServer()
+            :   this(new DefaultTlsCipherFactory())
+        {
+        }
+
+        public AbstractTlsServer(TlsCipherFactory cipherFactory)
+        {
+            this.mCipherFactory = cipherFactory;
+        }
+
+        protected virtual bool AllowEncryptThenMac
+        {
+            get { return true; }
+        }
+
+        protected virtual bool AllowTruncatedHMac
+        {
+            get { return false; }
+        }
+
+        protected virtual IDictionary CheckServerExtensions()
+        {
+            return this.mServerExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(this.mServerExtensions);
+        }
+
+        protected abstract int[] GetCipherSuites();
+
+        protected byte[] GetCompressionMethods()
+        {
+            return new byte[]{ CompressionMethod.NULL };
+        }
+
+        protected virtual ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.TLSv11; }
+        }
+
+        protected virtual ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv10; }
+        }
+
+        protected virtual bool SupportsClientEccCapabilities(int[] namedCurves, byte[] ecPointFormats)
+        {
+            // NOTE: BC supports all the current set of point formats so we don't check them here
+
+            if (namedCurves == null)
+            {
+                /*
+                 * RFC 4492 4. A client that proposes ECC cipher suites may choose not to include these
+                 * extensions. In this case, the server is free to choose any one of the elliptic curves
+                 * or point formats [...].
+                 */
+                return TlsEccUtilities.HasAnySupportedNamedCurves();
+            }
+
+            for (int i = 0; i < namedCurves.Length; ++i)
+            {
+                int namedCurve = namedCurves[i];
+                if (NamedCurve.IsValid(namedCurve)
+                    && (!NamedCurve.RefersToASpecificNamedCurve(namedCurve) || TlsEccUtilities.IsSupportedNamedCurve(namedCurve)))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public virtual void Init(TlsServerContext context)
+        {
+            this.mContext = context;
+        }
+
+        public virtual void NotifyClientVersion(ProtocolVersion clientVersion)
+        {
+            this.mClientVersion = clientVersion;
+        }
+
+        public virtual void NotifyOfferedCipherSuites(int[] offeredCipherSuites)
+        {
+            this.mOfferedCipherSuites = offeredCipherSuites;
+            this.mEccCipherSuitesOffered = TlsEccUtilities.ContainsEccCipherSuites(this.mOfferedCipherSuites);
+        }
+
+        public virtual void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods)
+        {
+            this.mOfferedCompressionMethods = offeredCompressionMethods;
+        }
+
+        public virtual void ProcessClientExtensions(IDictionary clientExtensions)
+        {
+            this.mClientExtensions = clientExtensions;
+
+            if (clientExtensions != null)
+            {
+                this.mEncryptThenMacOffered = TlsExtensionsUtilities.HasEncryptThenMacExtension(clientExtensions);
+                this.mMaxFragmentLengthOffered = TlsExtensionsUtilities.GetMaxFragmentLengthExtension(clientExtensions);
+                this.mTruncatedHMacOffered = TlsExtensionsUtilities.HasTruncatedHMacExtension(clientExtensions);
+
+                this.mSupportedSignatureAlgorithms = TlsUtilities.GetSignatureAlgorithmsExtension(clientExtensions);
+                if (this.mSupportedSignatureAlgorithms != null)
+                {
+                    /*
+                     * RFC 5246 7.4.1.4.1. Note: this extension is not meaningful for TLS versions prior
+                     * to 1.2. Clients MUST NOT offer it if they are offering prior versions.
+                     */
+                    if (!TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mClientVersion))
+                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
+
+                this.mNamedCurves = TlsEccUtilities.GetSupportedEllipticCurvesExtension(clientExtensions);
+                this.mClientECPointFormats = TlsEccUtilities.GetSupportedPointFormatsExtension(clientExtensions);
+            }
+
+            /*
+             * RFC 4429 4. The client MUST NOT include these extensions in the ClientHello message if it
+             * does not propose any ECC cipher suites.
+             */
+            if (!this.mEccCipherSuitesOffered && (this.mNamedCurves != null || this.mClientECPointFormats != null))
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        public virtual ProtocolVersion GetServerVersion()
+        {
+            if (MinimumVersion.IsEqualOrEarlierVersionOf(mClientVersion))
+            {
+                ProtocolVersion maximumVersion = MaximumVersion;
+                if (mClientVersion.IsEqualOrEarlierVersionOf(maximumVersion))
+                {
+                    return mServerVersion = mClientVersion;
+                }
+                if (mClientVersion.IsLaterVersionOf(maximumVersion))
+                {
+                    return mServerVersion = maximumVersion;
+                }
+            }
+            throw new TlsFatalAlert(AlertDescription.protocol_version);
+        }
+
+        public virtual int GetSelectedCipherSuite()
+        {
+            /*
+             * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
+             * cipher suites against the "signature_algorithms" extension before selecting them. This is
+             * somewhat inelegant but is a compromise designed to minimize changes to the original
+             * cipher suite design.
+             */
+
+            /*
+             * RFC 4429 5.1. A server that receives a ClientHello containing one or both of these
+             * extensions MUST use the client's enumerated capabilities to guide its selection of an
+             * appropriate cipher suite. One of the proposed ECC cipher suites must be negotiated only
+             * if the server can successfully complete the handshake while using the curves and point
+             * formats supported by the client [...].
+             */
+            bool eccCipherSuitesEnabled = SupportsClientEccCapabilities(this.mNamedCurves, this.mClientECPointFormats);
+
+            int[] cipherSuites = GetCipherSuites();
+            for (int i = 0; i < cipherSuites.Length; ++i)
+            {
+                int cipherSuite = cipherSuites[i];
+
+                if (Arrays.Contains(this.mOfferedCipherSuites, cipherSuite)
+                    && (eccCipherSuitesEnabled || !TlsEccUtilities.IsEccCipherSuite(cipherSuite))
+                    && TlsUtilities.IsValidCipherSuiteForVersion(cipherSuite, mServerVersion))
+                {
+                    return this.mSelectedCipherSuite = cipherSuite;
+                }
+            }
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
+        public virtual byte GetSelectedCompressionMethod()
+        {
+            byte[] compressionMethods = GetCompressionMethods();
+            for (int i = 0; i < compressionMethods.Length; ++i)
+            {
+                if (Arrays.Contains(mOfferedCompressionMethods, compressionMethods[i]))
+                {
+                    return this.mSelectedCompressionMethod = compressionMethods[i];
+                }
+            }
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+
+        // IDictionary is (Int32 -> byte[])
+        public virtual IDictionary GetServerExtensions()
+        {
+            if (this.mEncryptThenMacOffered && AllowEncryptThenMac)
+            {
+                /*
+                 * draft-ietf-tls-encrypt-then-mac-03 3. If a server receives an encrypt-then-MAC
+                 * request extension from a client and then selects a stream or AEAD cipher suite, it
+                 * MUST NOT send an encrypt-then-MAC response extension back to the client.
+                 */
+                if (TlsUtilities.IsBlockCipherSuite(this.mSelectedCipherSuite))
+                {
+                    TlsExtensionsUtilities.AddEncryptThenMacExtension(CheckServerExtensions());
+                }
+            }
+
+            if (this.mMaxFragmentLengthOffered >= 0)
+            {
+                TlsExtensionsUtilities.AddMaxFragmentLengthExtension(CheckServerExtensions(), (byte)this.mMaxFragmentLengthOffered);
+            }
+
+            if (this.mTruncatedHMacOffered && AllowTruncatedHMac)
+            {
+                TlsExtensionsUtilities.AddTruncatedHMacExtension(CheckServerExtensions());
+            }
+
+            if (this.mClientECPointFormats != null && TlsEccUtilities.IsEccCipherSuite(this.mSelectedCipherSuite))
+            {
+                /*
+                 * RFC 4492 5.2. A server that selects an ECC cipher suite in response to a ClientHello
+                 * message including a Supported Point Formats Extension appends this extension (along
+                 * with others) to its ServerHello message, enumerating the point formats it can parse.
+                 */
+                this.mServerECPointFormats = new byte[]{ ECPointFormat.uncompressed,
+                    ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+                TlsEccUtilities.AddSupportedPointFormatsExtension(CheckServerExtensions(), mServerECPointFormats);
+            }
+
+            return mServerExtensions;
+        }
+
+        public virtual IList GetServerSupplementalData()
+        {
+            return null;
+        }
+
+        public abstract TlsCredentials GetCredentials();
+
+        public virtual CertificateStatus GetCertificateStatus()
+        {
+            return null;
+        }
+
+        public abstract TlsKeyExchange GetKeyExchange();
+
+        public virtual CertificateRequest GetCertificateRequest()
+        {
+            return null;
+        }
+
+        public virtual void ProcessClientSupplementalData(IList clientSupplementalData)
+        {
+            if (clientSupplementalData != null)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        public virtual void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        public override TlsCompression GetCompression()
+        {
+            switch (mSelectedCompressionMethod)
+            {
+            case CompressionMethod.NULL:
+                return new TlsNullCompression();
+
+            default:
+                /*
+                 * Note: internal error here; we selected the compression method, so if we now can't
+                 * produce an implementation, we shouldn't have chosen it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public virtual NewSessionTicket GetNewSessionTicket()
+        {
+            /*
+             * RFC 5077 3.3. If the server determines that it does not want to include a ticket after it
+             * has included the SessionTicket extension in the ServerHello, then it sends a zero-length
+             * ticket in the NewSessionTicket handshake message.
+             */
+            return new NewSessionTicket(0L, TlsUtilities.EmptyBytes);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index 924e6ee2e..65106adef 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -12,42 +12,19 @@ using Org.BouncyCastle.Crypto.Parameters;
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public abstract class DefaultTlsClient
-        : TlsClient
+        :   AbstractTlsClient
     {
-        protected TlsCipherFactory cipherFactory;
-
-        protected TlsClientContext context;
-
-        protected byte selectedCompressionMethod;
-        protected int selectedCipherSuite;
-
         public DefaultTlsClient()
-            : this(new DefaultTlsCipherFactory())
+            :   base()
         {
         }
 
         public DefaultTlsClient(TlsCipherFactory cipherFactory)
+            :   base(cipherFactory)
         {
-            this.cipherFactory = cipherFactory;
-        }
-
-        public virtual void Init(TlsClientContext context)
-        {
-            this.context = context;
-        }
-
-        public virtual bool ShouldUseGmtUnixTime()
-        {
-            /*
-             * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
-             * TLS implementors MUST by default set the entire value the ClientHello.Random and
-             * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
-             * sequence.
-             */
-            return false;
         }
 
-        public virtual int[] GetCipherSuites()
+        public override int[] GetCipherSuites()
         {
             return new int[] {
                 CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
@@ -63,224 +40,150 @@ namespace Org.BouncyCastle.Crypto.Tls
             };
         }
 
-        public virtual byte[] GetCompressionMethods()
-        {
-            /*
-             * To offer DEFLATE compression, override this method:
-             *     return new byte[] { CompressionMethod.DEFLATE, CompressionMethod.NULL };
-             */
-
-            return new byte[] { CompressionMethod.NULL };
-        }
-
-        public virtual IDictionary GetClientExtensions()
-        {
-            return null;
-        }
-
-        public virtual void NotifySessionID(byte[] sessionID)
-        {
-            // Currently ignored
-        }
-
-        public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
-        {
-            this.selectedCipherSuite = selectedCipherSuite;
-        }
-
-        public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
-        {
-            this.selectedCompressionMethod = selectedCompressionMethod;
-        }
-
-        public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+        public override TlsKeyExchange GetKeyExchange()
         {
-            if (!secureRenegotiation)
+            switch (mSelectedCipherSuite)
             {
+            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+                return CreateRsaKeyExchange();
+
+            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+                return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+                return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+            default:
                 /*
-                 * RFC 5746 3.4.
-                 * If the extension is not present, the server does not support
-                 * secure renegotiation; set secure_renegotiation flag to FALSE.
-                 * In this case, some clients may want to terminate the handshake
-                 * instead of continuing; see Section 4.1 for discussion.
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
                  */
-//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
-            }
-        }
-
-        public virtual void ProcessServerExtensions(IDictionary serverExtensions)
-        {
-        }
-
-        public virtual TlsKeyExchange GetKeyExchange()
-        {
-            switch (selectedCipherSuite)
-            {
-                case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-                    return CreateRsaKeyExchange();
-
-                case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
-                    return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
-
-                case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-                    return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
-
-                case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-                    return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
-
-                case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-                    return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
-
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-                    return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
-
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
-                    return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
-
-                case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-                    return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
-
-                case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-                    return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
-
-                default:
-                    /*
-                    * Note: internal error here; the TlsProtocolHandler verifies that the
-                    * server-selected cipher suite was in the list of client-offered cipher
-                    * suites, so if we now can't produce an implementation, we shouldn't have
-                    * offered it!
-                    */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
-        public abstract TlsAuthentication GetAuthentication();
-
-        public virtual TlsCompression GetCompression()
-        {
-            switch (selectedCompressionMethod)
-            {
-                case CompressionMethod.NULL:
-                    return new TlsNullCompression();
-
-                case CompressionMethod.DEFLATE:
-                    return new TlsDeflateCompression();
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected compression method was in the list of client-offered compression
-                     * methods, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-
-        public virtual TlsCipher GetCipher()
+        public override TlsCipher GetCipher()
         {
-            switch (selectedCipherSuite)
+            switch (mSelectedCipherSuite)
             {
-                case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                default:
-                    /*
-                    * Note: internal error here; the TlsProtocolHandler verifies that the
-                    * server-selected cipher suite was in the list of client-offered cipher
-                    * suites, so if we now can't produce an implementation, we shouldn't have
-                    * offered it!
-                    */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
+            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
         protected virtual TlsKeyExchange CreateDHKeyExchange(int keyExchange)
         {
-            return new TlsDHKeyExchange(context, keyExchange);
+            return new TlsDHKeyExchange(mContext, keyExchange);
         }
 
         protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
         {
-            return new TlsDheKeyExchange(context, keyExchange);
+            return new TlsDheKeyExchange(mContext, keyExchange);
         }
 
         protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
         {
-            return new TlsECDHKeyExchange(context, keyExchange);
+            return new TlsECDHKeyExchange(mContext, keyExchange);
         }
 
         protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
         {
-            return new TlsECDheKeyExchange(context, keyExchange);
+            return new TlsECDheKeyExchange(mContext, keyExchange);
         }
 
         protected virtual TlsKeyExchange CreateRsaKeyExchange()
         {
-            return new TlsRsaKeyExchange(context);
+            return new TlsRsaKeyExchange(mContext);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/HeartbeatExtension.cs b/crypto/src/crypto/tls/HeartbeatExtension.cs
new file mode 100644
index 000000000..049837266
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatExtension.cs
@@ -0,0 +1,52 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class HeartbeatExtension
+    {
+        protected readonly byte mMode;
+
+        public HeartbeatExtension(byte mode)
+        {
+            if (!HeartbeatMode.IsValid(mode))
+                throw new ArgumentException("not a valid HeartbeatMode value", "mode");
+
+            this.mMode = mode;
+        }
+
+        public virtual byte Mode
+        {
+            get { return mMode; }
+        }
+
+        /**
+         * Encode this {@link HeartbeatExtension} to a {@link Stream}.
+         * 
+         * @param output
+         *            the {@link Stream} to encode to.
+         * @throws IOException
+         */
+        public virtual void Encode(Stream output)
+        {
+            TlsUtilities.WriteUint8(mMode, output);
+        }
+
+        /**
+         * Parse a {@link HeartbeatExtension} from a {@link Stream}.
+         * 
+         * @param input
+         *            the {@link Stream} to parse from.
+         * @return a {@link HeartbeatExtension} object.
+         * @throws IOException
+         */
+        public static HeartbeatExtension Parse(Stream input)
+        {
+            byte mode = TlsUtilities.ReadUint8(input);
+            if (!HeartbeatMode.IsValid(mode))
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            return new HeartbeatExtension(mode);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/HeartbeatMessage.cs b/crypto/src/crypto/tls/HeartbeatMessage.cs
new file mode 100644
index 000000000..f64a7baa4
--- /dev/null
+++ b/crypto/src/crypto/tls/HeartbeatMessage.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class HeartbeatMessage
+    {
+        protected readonly byte mType;
+        protected readonly byte[] mPayload;
+        protected readonly int mPaddingLength;
+
+        public HeartbeatMessage(byte type, byte[] payload, int paddingLength)
+        {
+            if (!HeartbeatMessageType.IsValid(type))
+                throw new ArgumentException("not a valid HeartbeatMessageType value", "type");
+            if (payload == null || payload.Length >= (1 << 16))
+                throw new ArgumentException("must have length < 2^16", "payload");
+            if (paddingLength < 16)
+                throw new ArgumentException("must be at least 16", "paddingLength");
+
+            this.mType = type;
+            this.mPayload = payload;
+            this.mPaddingLength = paddingLength;
+        }
+
+        /**
+         * Encode this {@link HeartbeatMessage} to a {@link Stream}.
+         * 
+         * @param output
+         *            the {@link Stream} to encode to.
+         * @throws IOException
+         */
+        public virtual void Encode(TlsContext context, Stream output)
+        {
+            TlsUtilities.WriteUint8(mType, output);
+
+            TlsUtilities.CheckUint16(mPayload.Length);
+            TlsUtilities.WriteUint16(mPayload.Length, output);
+            output.Write(mPayload, 0, mPayload.Length);
+
+            byte[] padding = new byte[mPaddingLength];
+            context.NonceRandomGenerator.NextBytes(padding);
+            output.Write(padding, 0, padding.Length);
+        }
+
+        /**
+         * Parse a {@link HeartbeatMessage} from a {@link Stream}.
+         * 
+         * @param input
+         *            the {@link Stream} to parse from.
+         * @return a {@link HeartbeatMessage} object.
+         * @throws IOException
+         */
+        public static HeartbeatMessage Parse(Stream input)
+        {
+            byte type = TlsUtilities.ReadUint8(input);
+            if (!HeartbeatMessageType.IsValid(type))
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            int payload_length = TlsUtilities.ReadUint16(input);
+
+            PayloadBuffer buf = new PayloadBuffer();
+            Streams.PipeAll(input, buf);
+
+            byte[] payload = buf.ToTruncatedByteArray(payload_length);
+            if (payload == null)
+            {
+                /*
+                 * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the
+                 * received HeartbeatMessage MUST be discarded silently.
+                 */
+                return null;
+            }
+
+            TlsUtilities.CheckUint16(buf.Length);
+            int padding_length = (int)buf.Length - payload.Length;
+
+            /*
+             * RFC 6520 4. The padding of a received HeartbeatMessage message MUST be ignored
+             */
+            return new HeartbeatMessage(type, payload, padding_length);
+        }
+
+        internal class PayloadBuffer
+            :   MemoryStream
+        {
+            internal byte[] ToTruncatedByteArray(int payloadLength)
+            {
+                /*
+                 * RFC 6520 4. The padding_length MUST be at least 16.
+                 */
+                int minimumCount = payloadLength + 16;
+                if (Length < minimumCount)
+                    return null;
+                return Arrays.CopyOf(GetBuffer(), payloadLength);
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index f6d83b5be..620a6d8f7 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -4,44 +4,22 @@ using System.Collections;
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public abstract class PskTlsClient
-        :TlsClient
+        :   AbstractTlsClient
     {
-        protected TlsCipherFactory cipherFactory;
-        protected TlsPskIdentity pskIdentity;
-
-        protected TlsClientContext context;
-
-        protected byte selectedCompressionMethod;
-        protected int selectedCipherSuite;
+        protected TlsPskIdentity mPskIdentity;
 
         public PskTlsClient(TlsPskIdentity pskIdentity)
-            : this(new DefaultTlsCipherFactory(), pskIdentity)
+            :   this(new DefaultTlsCipherFactory(), pskIdentity)
         {
         }
 
         public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity)
+            :   base(cipherFactory)
         {
-            this.cipherFactory = cipherFactory;
-            this.pskIdentity = pskIdentity;
-        }
-
-        public virtual void Init(TlsClientContext context)
-        {
-            this.context = context;
-        }
-
-        public virtual bool ShouldUseGmtUnixTime()
-        {
-            /*
-             * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
-             * TLS implementors MUST by default set the entire value the ClientHello.Random and
-             * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
-             * sequence.
-             */
-            return false;
+            this.mPskIdentity = pskIdentity;
         }
 
-        public virtual int[] GetCipherSuites()
+        public override int[] GetCipherSuites()
         {
             return new int[] {
                 CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
@@ -59,144 +37,79 @@ namespace Org.BouncyCastle.Crypto.Tls
             };
         }
 
-        public virtual IDictionary GetClientExtensions()
-        {
-            return null;
-        }
-
-        public virtual byte[] GetCompressionMethods()
-        {
-            return new byte[] { CompressionMethod.NULL };
-        }
-
-        public virtual void NotifySessionID(byte[] sessionID)
-        {
-            // Currently ignored 
-        }
-
-        public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
-        {
-            this.selectedCipherSuite = selectedCipherSuite;
-        }
-
-        public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
-        {
-            this.selectedCompressionMethod = selectedCompressionMethod;
-        }
-
-        public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+        public override TlsKeyExchange GetKeyExchange()
         {
-            if (!secureRenegotiation)
+            switch (mSelectedCipherSuite)
             {
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+            default:
                 /*
-                 * RFC 5746 3.4. If the extension is not present, the server does not support
-                 * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
-                 * some clients may want to terminate the handshake instead of continuing; see
-                 * Section 4.1 for discussion.
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
                  */
-//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
-        public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+        public override TlsCipher GetCipher()
         {
-        }
-
-        public virtual TlsKeyExchange GetKeyExchange()
-        {
-            switch (selectedCipherSuite)
-            {
-                case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-                    return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
-
-                case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-                    return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
-
-                case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-                    return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected cipher suite was in the list of client-offered cipher
-                     * suites, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-
-        public abstract TlsAuthentication GetAuthentication();
-
-        public virtual TlsCompression GetCompression()
-        {
-            switch (selectedCompressionMethod)
+            switch (mSelectedCipherSuite)
             {
-                case CompressionMethod.NULL:
-                    return new TlsNullCompression();
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected compression method was in the list of client-offered compression
-                     * methods, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-
-        public virtual TlsCipher GetCipher()
-        {
-            switch (selectedCipherSuite)
-            {
-                case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-                case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.RC4_128,
-                        MacAlgorithm.hmac_sha1);
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected cipher suite was in the list of client-offered cipher
-                     * suites, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC,
+                    MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128,
+                    MacAlgorithm.hmac_sha1);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
         protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
         {
-            return new TlsPskKeyExchange(context, keyExchange, pskIdentity);
+            return new TlsPskKeyExchange(mContext, keyExchange, mPskIdentity);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/ServerName.cs b/crypto/src/crypto/tls/ServerName.cs
new file mode 100644
index 000000000..3d1e8f844
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerName.cs
@@ -0,0 +1,105 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class ServerName
+    {
+        protected readonly byte mNameType;
+        protected readonly object mName;
+
+        public ServerName(byte nameType, object name)
+        {
+            if (!IsCorrectType(nameType, name))
+                throw new ArgumentException("not an instance of the correct type", "name");
+
+            this.mNameType = nameType;
+            this.mName = name;
+        }
+
+        public virtual byte NameType
+        {
+            get { return mNameType; }
+        }
+
+        public virtual object Name
+        {
+            get { return mName; }
+        }
+
+        public virtual string GetHostName()
+        {
+            if (!IsCorrectType(Tls.NameType.host_name, mName))
+                throw new InvalidOperationException("'name' is not a HostName string");
+
+            return (string)mName;
+        }
+
+        /**
+         * Encode this {@link ServerName} to a {@link Stream}.
+         * 
+         * @param output
+         *            the {@link Stream} to encode to.
+         * @throws IOException
+         */
+        public virtual void Encode(Stream output)
+        {
+            TlsUtilities.WriteUint8(mNameType, output);
+
+            switch (mNameType)
+            {
+            case Tls.NameType.host_name:
+                byte[] utf8Encoding = Strings.ToUtf8ByteArray((string)mName);
+                if (utf8Encoding.Length < 1)
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
+                TlsUtilities.WriteOpaque16(utf8Encoding, output);
+                break;
+            default:
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        /**
+         * Parse a {@link ServerName} from a {@link Stream}.
+         * 
+         * @param input
+         *            the {@link Stream} to parse from.
+         * @return a {@link ServerName} object.
+         * @throws IOException
+         */
+        public static ServerName Parse(Stream input)
+        {
+            byte name_type = TlsUtilities.ReadUint8(input);
+            object name;
+
+            switch (name_type)
+            {
+            case Tls.NameType.host_name:
+            {
+                byte[] utf8Encoding = TlsUtilities.ReadOpaque16(input);
+                if (utf8Encoding.Length < 1)
+                    throw new TlsFatalAlert(AlertDescription.decode_error);
+                name = Strings.FromUtf8ByteArray(utf8Encoding);
+                break;
+            }
+            default:
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+            }
+
+            return new ServerName(name_type, name);
+        }
+
+        protected static bool IsCorrectType(byte nameType, object name)
+        {
+            switch (nameType)
+            {
+            case Tls.NameType.host_name:
+                return name is string;
+            default:
+                throw new ArgumentException("unsupported value", "name");
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/ServerNameList.cs b/crypto/src/crypto/tls/ServerNameList.cs
new file mode 100644
index 000000000..13da79bf6
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerNameList.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class ServerNameList
+    {
+        protected readonly IList mServerNameList;
+
+        /**
+         * @param serverNameList an {@link IList} of {@link ServerName}.
+         */
+        public ServerNameList(IList serverNameList)
+        {
+            if (serverNameList == null || serverNameList.Count < 1)
+                throw new ArgumentException("must not be null or empty", "serverNameList");
+
+            this.mServerNameList = serverNameList;
+        }
+
+        /**
+         * @return an {@link IList} of {@link ServerName}.
+         */
+        public virtual IList ServerNames
+        {
+            get { return mServerNameList; }
+        }
+
+        /**
+         * Encode this {@link ServerNameList} to a {@link Stream}.
+         * 
+         * @param output
+         *            the {@link Stream} to encode to.
+         * @throws IOException
+         */
+        public virtual void Encode(Stream output)
+        {
+            MemoryStream buf = new MemoryStream();
+
+            foreach (ServerName entry in ServerNames)
+            {
+                entry.Encode(buf);
+            }
+
+            TlsUtilities.CheckUint16(buf.Length);
+            TlsUtilities.WriteUint16((int)buf.Length, output);
+            buf.WriteTo(output);
+        }
+
+        /**
+         * Parse a {@link ServerNameList} from a {@link Stream}.
+         * 
+         * @param input
+         *            the {@link Stream} to parse from.
+         * @return a {@link ServerNameList} object.
+         * @throws IOException
+         */
+        public static ServerNameList Parse(Stream input)
+        {
+            int length = TlsUtilities.ReadUint16(input);
+            if (length < 1)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+
+            byte[] data = TlsUtilities.ReadFully(length, input);
+
+            MemoryStream buf = new MemoryStream(data, false);
+
+            IList server_name_list = Platform.CreateArrayList();
+            while (buf.Position < buf.Length)
+            {
+                ServerName entry = ServerName.Parse(buf);
+                server_name_list.Add(entry);
+            }
+
+            return new ServerNameList(server_name_list);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index dfd7603b8..a7c72b862 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -7,16 +7,10 @@ using Org.BouncyCastle.Utilities;
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public abstract class SrpTlsClient
-        : TlsClient
+        :   AbstractTlsClient
     {
-        protected TlsCipherFactory cipherFactory;
-        protected byte[] identity;
-        protected byte[] password;
-
-        protected TlsClientContext context;
-
-        protected byte selectedCompressionMethod;
-        protected int selectedCipherSuite;
+        protected byte[] mIdentity;
+        protected byte[] mPassword;
 
         public SrpTlsClient(byte[] identity, byte[] password)
             : this(new DefaultTlsCipherFactory(), identity, password)
@@ -24,179 +18,96 @@ namespace Org.BouncyCastle.Crypto.Tls
         }
 
         public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+            :   base(cipherFactory)
         {
-            this.cipherFactory = cipherFactory;
-            this.identity = Arrays.Clone(identity);
-            this.password = Arrays.Clone(password);
-        }
-
-        public virtual void Init(TlsClientContext context)
-        {
-            this.context = context;
+            this.mIdentity = Arrays.Clone(identity);
+            this.mPassword = Arrays.Clone(password);
         }
 
-        public virtual bool ShouldUseGmtUnixTime()
+        public override int[] GetCipherSuites()
         {
-            /*
-             * draft-mathewson-no-gmtunixtime-00 2. For the reasons we discuss above, we recommend that
-             * TLS implementors MUST by default set the entire value the ClientHello.Random and
-             * ServerHello.Random fields, including gmt_unix_time, to a cryptographically random
-             * sequence.
-             */
-            return false;
+            return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
         }
 
-        public virtual int[] GetCipherSuites()
+        public override IDictionary GetClientExtensions()
         {
-            return new int[] {
-                CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
-            };
-        }
-
-        public virtual IDictionary GetClientExtensions()
-        {
-            IDictionary clientExtensions = Platform.CreateHashtable();
-
-            MemoryStream srpData = new MemoryStream();
-            TlsUtilities.WriteOpaque8(this.identity, srpData);
-            clientExtensions[ExtensionType.srp] = srpData.ToArray();
-
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsSrpUtilities.AddSrpExtension(clientExtensions, this.mIdentity);
             return clientExtensions;
         }
 
-        public virtual byte[] GetCompressionMethods()
+        public override void ProcessServerExtensions(IDictionary serverExtensions)
         {
-            return new byte[] { CompressionMethod.NULL };
-        }
-
-        public virtual void NotifySessionID(byte[] sessionID)
-        {
-            // Currently ignored 
-        }
-
-        public virtual void NotifySelectedCipherSuite(int selectedCipherSuite)
-        {
-            this.selectedCipherSuite = selectedCipherSuite;
-        }
-
-        public virtual void NotifySelectedCompressionMethod(byte selectedCompressionMethod)
-        {
-            this.selectedCompressionMethod = selectedCompressionMethod;
-        }
-
-        public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
-        {
-            if (!secureRenegotiation)
+            if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp,
+                AlertDescription.illegal_parameter))
             {
-                /*
-                 * RFC 5746 3.4. If the extension is not present, the server does not support
-                 * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
-                 * some clients may want to terminate the handshake instead of continuing; see
-                 * Section 4.1 for discussion.
-                 */
-//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
+                // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
             }
-        }
 
-        public virtual void ProcessServerExtensions(IDictionary serverExtensions)
-        {
-            // There is no server response for the SRP extension
+            base.ProcessServerExtensions(serverExtensions);
         }
 
-        public virtual TlsKeyExchange GetKeyExchange()
+        public override TlsKeyExchange GetKeyExchange()
         {
-            switch (selectedCipherSuite)
+            switch (mSelectedCipherSuite)
             {
-                case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
-                    return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
-
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
-                    return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
-
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
-                    return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected cipher suite was in the list of client-offered cipher
-                     * suites, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-        
-        public abstract TlsAuthentication GetAuthentication();
-
-        public virtual TlsCompression GetCompression()
-        {
-            switch (selectedCompressionMethod)
-            {
-                case CompressionMethod.NULL:
-                    return new TlsNullCompression();
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected compression method was in the list of client-offered compression
-                     * methods, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
-        public virtual TlsCipher GetCipher()
+        public override TlsCipher GetCipher()
         {
-            switch (selectedCipherSuite)
+            switch (mSelectedCipherSuite)
             {
-                case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
-                case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
-                    return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
-                        MacAlgorithm.hmac_sha1);
-
-                default:
-                    /*
-                     * Note: internal error here; the TlsProtocolHandler verifies that the
-                     * server-selected cipher suite was in the list of client-offered cipher
-                     * suites, so if we now can't produce an implementation, we shouldn't have
-                     * offered it!
-                     */
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
         protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
         {
-            return new TlsSrpKeyExchange(context, keyExchange, identity, password);
+            return new TlsSrpKeyExchange(mContext, keyExchange, mIdentity, mPassword);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/SupplementalDataEntry.cs b/crypto/src/crypto/tls/SupplementalDataEntry.cs
new file mode 100644
index 000000000..5adc4fa52
--- /dev/null
+++ b/crypto/src/crypto/tls/SupplementalDataEntry.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class SupplementalDataEntry
+    {
+        protected readonly int mDataType;
+        protected readonly byte[] mData;
+
+        public SupplementalDataEntry(int dataType, byte[] data)
+        {
+            this.mDataType = dataType;
+            this.mData = data;
+        }
+
+        public virtual int DataType
+        {
+            get { return mDataType; }
+        }
+
+        public virtual byte[] Data
+        {
+            get { return mData; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsClient.cs b/crypto/src/crypto/tls/TlsClient.cs
index a4cdc647d..cd5dfad13 100644
--- a/crypto/src/crypto/tls/TlsClient.cs
+++ b/crypto/src/crypto/tls/TlsClient.cs
@@ -15,6 +15,18 @@ namespace Org.BouncyCastle.Crypto.Tls
         /// </param>
         void Init(TlsClientContext context);
 
+        /// <summary>Return the session this client wants to resume, if any.</summary>
+        /// <remarks>Note that the peer's certificate chain for the session (if any) may need to be periodically revalidated.</remarks>
+        /// <returns>
+        /// A <see cref="TlsSession"/> representing the resumable session to be used for this connection,
+        /// or null to use a new session.
+        /// </returns>
+        TlsSession GetSessionToResume();
+
+        ProtocolVersion ClientHelloRecordLayerVersion { get; }
+
+        ProtocolVersion ClientVersion { get; }
+
         /// <summary>
         /// Get the list of cipher suites that this client supports.
         /// </summary>
@@ -40,12 +52,13 @@ namespace Org.BouncyCastle.Crypto.Tls
         /// <exception cref="IOException"></exception>
         IDictionary GetClientExtensions();
 
+        /// <exception cref="IOException"></exception>
+        void NotifyServerVersion(ProtocolVersion selectedVersion);
+
         /// <summary>
-        /// Reports the session ID once it has been determined.
+        /// Notifies the client of the session_id sent in the ServerHello.
         /// </summary>
-        /// <param name="sessionID">
-        /// A <see cref="System.Byte"/>
-        /// </param>
+        /// <param name="sessionID">An array of <see cref="System.Byte"/></param>
         void NotifySessionID(byte[] sessionID);
 
         /// <summary>
@@ -73,18 +86,6 @@ namespace Org.BouncyCastle.Crypto.Tls
         void NotifySelectedCompressionMethod(byte selectedCompressionMethod);
 
         /// <summary>
-        /// Report whether the server supports secure renegotiation
-        /// </summary>
-        /// <remarks>
-        /// The protocol handler automatically processes the relevant extensions
-        /// </remarks>
-        /// <param name="secureRenegotiation">
-        /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
-        /// </param>
-        /// <exception cref="IOException"></exception>
-        void NotifySecureRenegotiation(bool secureRenegotiation);
-
-        /// <summary>
         /// Report the extensions from an extended server hello.
         /// </summary>
         /// <remarks>
@@ -95,6 +96,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         /// </param>
         void ProcessServerExtensions(IDictionary serverExtensions);
 
+        /// <param name="serverSupplementalData">A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></param>
+        /// <exception cref="IOException"/>
+        void ProcessServerSupplementalData(IList serverSupplementalData);
+
         /// <summary>
         /// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange
         /// part of the protocol.
@@ -112,19 +117,18 @@ namespace Org.BouncyCastle.Crypto.Tls
         /// <exception cref="IOException"/>
         TlsAuthentication GetAuthentication();
 
-        /// <summary>
-        /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
-        /// </summary>
+        /// <returns>A <see cref="IList">list</see> of <see cref="SupplementalDataEntry"/></returns>
         /// <exception cref="IOException"/>
-        TlsCompression GetCompression();
+        IList GetClientSupplementalData();
 
-        /// <summary>
-        /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
-        /// </summary>
-        /// <returns>
-        /// A <see cref="TlsCipher"/>
-        /// </returns>
+        /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message</summary>
+        /// <remarks>
+        /// This method will be called (only) when a NewSessionTicket handshake message is received. The
+        /// ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
+        /// that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
+        /// </remarks>
+        /// <param name="newSessionTicket">The <see cref="NewSessionTicket">ticket</see></param>
         /// <exception cref="IOException"/>
-        TlsCipher GetCipher();
+        void NotifyNewSessionTicket(NewSessionTicket newSessionTicket);
     }
 }
diff --git a/crypto/src/crypto/tls/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index 9bbfa844e..399438879 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -18,29 +18,29 @@ namespace Org.BouncyCastle.Crypto.Tls
 {
     public class TlsEccUtilities
     {
-        private static readonly string[] curveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
+        private static readonly string[] CurveNames = new string[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
             "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
             "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
             "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
             "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
 
-        public static void AddSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
+        public static void AddSupportedEllipticCurvesExtension(IDictionary extensions, int[] namedCurves)
         {
             extensions[ExtensionType.elliptic_curves] = CreateSupportedEllipticCurvesExtension(namedCurves);
         }
 
-        public static void AddSupportedPointFormatsExtension(Hashtable extensions, byte[] ecPointFormats)
+        public static void AddSupportedPointFormatsExtension(IDictionary extensions, byte[] ecPointFormats)
         {
             extensions[ExtensionType.ec_point_formats] = CreateSupportedPointFormatsExtension(ecPointFormats);
         }
 
-        public static int[] GetSupportedEllipticCurvesExtension(Hashtable extensions)
+        public static int[] GetSupportedEllipticCurvesExtension(IDictionary extensions)
         {
             byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.elliptic_curves);
             return extensionData == null ? null : ReadSupportedEllipticCurvesExtension(extensionData);
         }
 
-        public static byte[] GetSupportedPointFormatsExtension(Hashtable extensions)
+        public static byte[] GetSupportedPointFormatsExtension(IDictionary extensions)
         {
             byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.ec_point_formats);
             return extensionData == null ? null : ReadSupportedPointFormatsExtension(extensionData);
@@ -117,7 +117,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public static string GetNameOfNamedCurve(int namedCurve)
         {
-            return IsSupportedNamedCurve(namedCurve) ? curveNames[namedCurve - 1] : null;
+            return IsSupportedNamedCurve(namedCurve) ? CurveNames[namedCurve - 1] : null;
         }
 
         public static ECDomainParameters GetParametersForNamedCurve(int namedCurve)
@@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public static bool HasAnySupportedNamedCurves()
         {
-            return curveNames.Length > 0;
+            return CurveNames.Length > 0;
         }
 
         public static bool ContainsEccCipherSuites(int[] cipherSuites)
@@ -276,7 +276,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public static bool IsSupportedNamedCurve(int namedCurve)
         {
-            return (namedCurve > 0 && namedCurve <= curveNames.Length);
+            return (namedCurve > 0 && namedCurve <= CurveNames.Length);
         }
 
         public static bool IsCompressionPreferred(byte[] ecPointFormats, byte compressionFormat)
diff --git a/crypto/src/crypto/tls/TlsExtensionsUtilities.cs b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
new file mode 100644
index 000000000..ca1d4183b
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsExtensionsUtilities.cs
@@ -0,0 +1,243 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class TlsExtensionsUtilities
+    {
+        public static IDictionary EnsureExtensionsInitialised(IDictionary extensions)
+        {
+            return extensions == null ? Platform.CreateHashtable() : extensions;
+        }
+
+        public static void AddEncryptThenMacExtension(IDictionary extensions)
+        {
+            extensions[ExtensionType.encrypt_then_mac] = CreateEncryptThenMacExtension();
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static void AddHeartbeatExtension(IDictionary extensions, HeartbeatExtension heartbeatExtension)
+        {
+            extensions[ExtensionType.heartbeat] = CreateHeartbeatExtension(heartbeatExtension);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static void AddMaxFragmentLengthExtension(IDictionary extensions, byte maxFragmentLength)
+        {
+            extensions[ExtensionType.max_fragment_length] = CreateMaxFragmentLengthExtension(maxFragmentLength);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static void AddServerNameExtension(IDictionary extensions, ServerNameList serverNameList)
+        {
+            extensions[ExtensionType.server_name] = CreateServerNameExtension(serverNameList);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static void AddStatusRequestExtension(IDictionary extensions, CertificateStatusRequest statusRequest)
+        {
+            extensions[ExtensionType.status_request] = CreateStatusRequestExtension(statusRequest);
+        }
+
+        public static void AddTruncatedHMacExtension(IDictionary extensions)
+        {
+            extensions[ExtensionType.truncated_hmac] = CreateTruncatedHMacExtension();
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static HeartbeatExtension GetHeartbeatExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.heartbeat);
+            return extensionData == null ? null : ReadHeartbeatExtension(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static short GetMaxFragmentLengthExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.max_fragment_length);
+            return extensionData == null ? (short)-1 : (short)ReadMaxFragmentLengthExtension(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static ServerNameList GetServerNameExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_name);
+            return extensionData == null ? null : ReadServerNameExtension(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static CertificateStatusRequest GetStatusRequestExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.status_request);
+            return extensionData == null ? null : ReadStatusRequestExtension(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static bool HasEncryptThenMacExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.encrypt_then_mac);
+            return extensionData == null ? false : ReadEncryptThenMacExtension(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static bool HasTruncatedHMacExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.truncated_hmac);
+            return extensionData == null ? false : ReadTruncatedHMacExtension(extensionData);
+        }
+
+        public static byte[] CreateEmptyExtensionData()
+        {
+            return TlsUtilities.EmptyBytes;
+        }
+
+        public static byte[] CreateEncryptThenMacExtension()
+        {
+            return CreateEmptyExtensionData();
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static byte[] CreateHeartbeatExtension(HeartbeatExtension heartbeatExtension)
+        {
+            if (heartbeatExtension == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            MemoryStream buf = new MemoryStream();
+
+            heartbeatExtension.Encode(buf);
+
+            return buf.ToArray();
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static byte[] CreateMaxFragmentLengthExtension(byte maxFragmentLength)
+        {
+            if (!MaxFragmentLength.IsValid(maxFragmentLength))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            return new byte[]{ maxFragmentLength };
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static byte[] CreateServerNameExtension(ServerNameList serverNameList)
+        {
+            if (serverNameList == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            MemoryStream buf = new MemoryStream();
+        
+            serverNameList.Encode(buf);
+
+            return buf.ToArray();
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static byte[] CreateStatusRequestExtension(CertificateStatusRequest statusRequest)
+        {
+            if (statusRequest == null)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            MemoryStream buf = new MemoryStream();
+
+            statusRequest.Encode(buf);
+
+            return buf.ToArray();
+        }
+
+        public static byte[] CreateTruncatedHMacExtension()
+        {
+            return CreateEmptyExtensionData();
+        }
+
+        /// <exception cref="IOException"></exception>
+        private static bool ReadEmptyExtensionData(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            if (extensionData.Length != 0)
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            return true;
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static bool ReadEncryptThenMacExtension(byte[] extensionData)
+        {
+            return ReadEmptyExtensionData(extensionData);
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static HeartbeatExtension ReadHeartbeatExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            HeartbeatExtension heartbeatExtension = HeartbeatExtension.Parse(buf);
+
+            TlsProtocol.AssertEmpty(buf);
+
+            return heartbeatExtension;
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static short ReadMaxFragmentLengthExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            if (extensionData.Length != 1)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+
+            byte maxFragmentLength = extensionData[0];
+
+            if (!MaxFragmentLength.IsValid(maxFragmentLength))
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+
+            return maxFragmentLength;
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static ServerNameList ReadServerNameExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            ServerNameList serverNameList = ServerNameList.Parse(buf);
+
+            TlsProtocol.AssertEmpty(buf);
+
+            return serverNameList;
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static CertificateStatusRequest ReadStatusRequestExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            CertificateStatusRequest statusRequest = CertificateStatusRequest.Parse(buf);
+
+            TlsProtocol.AssertEmpty(buf);
+
+            return statusRequest;
+        }
+
+        /// <exception cref="IOException"></exception>
+        public static bool ReadTruncatedHMacExtension(byte[] extensionData)
+        {
+            return ReadEmptyExtensionData(extensionData);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsPeer.cs b/crypto/src/crypto/tls/TlsPeer.cs
index 5b5c94a44..1ae41a41a 100644
--- a/crypto/src/crypto/tls/TlsPeer.cs
+++ b/crypto/src/crypto/tls/TlsPeer.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
@@ -15,5 +16,47 @@ namespace Org.BouncyCastle.Crypto.Tls
         /// random value.
         /// </returns>
         bool ShouldUseGmtUnixTime();
+
+        /// <summary>
+        /// Report whether the server supports secure renegotiation
+        /// </summary>
+        /// <remarks>
+        /// The protocol handler automatically processes the relevant extensions
+        /// </remarks>
+        /// <param name="secureRenegotiation">
+        /// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
+        /// </param>
+        /// <exception cref="IOException"></exception>
+        void NotifySecureRenegotiation(bool secureRenegotiation);
+
+        /// <summary>
+        /// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
+        /// </summary>
+        /// <returns>A <see cref="TlsCompression"/></returns>
+        /// <exception cref="IOException"/>
+        TlsCompression GetCompression();
+
+        /// <summary>
+        /// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
+        /// </summary>
+        /// <returns>A <see cref="TlsCipher"/></returns>
+        /// <exception cref="IOException"/>
+        TlsCipher GetCipher();
+
+        /// <summary>This method will be called when an alert is raised by the protocol.</summary>
+        /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+        /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+        /// <param name="message">A human-readable message explaining what caused this alert. May be null.</param>
+        /// <param name="cause">The <c>Exception</c> that caused this alert to be raised. May be null.</param>
+        void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause);
+
+        /// <summary>This method will be called when an alert is received from the remote peer.</summary>
+        /// <param name="alertLevel"><see cref="AlertLevel"/></param>
+        /// <param name="alertDescription"><see cref="AlertDescription"/></param>
+        void NotifyAlertReceived(byte alertLevel, byte alertDescription);
+
+        /// <summary>Notifies the peer that the handshake has been successfully completed.</summary>
+        /// <exception cref="IOException"></exception>
+        void NotifyHandshakeComplete();
     }
 }
diff --git a/crypto/src/crypto/tls/TlsServer.cs b/crypto/src/crypto/tls/TlsServer.cs
new file mode 100644
index 000000000..93e62b9ac
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServer.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsServer
+        :   TlsPeer
+    {
+        void Init(TlsServerContext context);
+
+        /// <exception cref="IOException"></exception>
+        void NotifyClientVersion(ProtocolVersion clientVersion);
+
+        /// <exception cref="IOException"></exception>
+        void NotifyOfferedCipherSuites(int[] offeredCipherSuites);
+
+        /// <exception cref="IOException"></exception>
+        void NotifyOfferedCompressionMethods(byte[] offeredCompressionMethods);
+
+        /// <param name="clientExtensions">A <see cref="IDictionary"/> (Int32 -> byte[]). Will never be null.</param>
+        /// <exception cref="IOException"></exception>
+        void ProcessClientExtensions(IDictionary clientExtensions);
+
+        /// <exception cref="IOException"></exception>
+        ProtocolVersion GetServerVersion();
+
+        /// <exception cref="IOException"></exception>
+        int GetSelectedCipherSuite();
+
+        /// <exception cref="IOException"></exception>
+        byte GetSelectedCompressionMethod();
+
+        /// <summary>
+        /// Get the (optional) table of server extensions to be included in (extended) server hello.
+        /// </summary>
+        /// <returns>
+        /// A <see cref="IDictionary"/> (Int32 -> byte[]). May be null.
+        /// </returns>
+        /// <exception cref="IOException"></exception>
+        IDictionary GetServerExtensions();
+
+        /// <returns>
+        /// A <see cref="IList"/> (<see cref="SupplementalDataEntry"/>). May be null.
+        /// </returns>
+        /// <exception cref="IOException"></exception>
+        IList GetServerSupplementalData();
+
+        /// <exception cref="IOException"></exception>
+        TlsCredentials GetCredentials();
+
+        /// <remarks>
+        /// This method will be called (only) if the server included an extension of type
+        /// "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546
+        /// 3.6. Certificate Status Request</i>. If a non-null <see cref="CertificateStatus"/> is returned, it
+        /// is sent to the client as a handshake message of type "certificate_status".
+        /// </remarks>
+        /// <returns>A <see cref="CertificateStatus"/> to be sent to the client (or null for none).</returns>
+        /// <exception cref="IOException"></exception>
+        CertificateStatus GetCertificateStatus();
+
+        /// <exception cref="IOException"></exception>
+        TlsKeyExchange GetKeyExchange();
+
+        /// <exception cref="IOException"></exception>
+        CertificateRequest GetCertificateRequest();
+
+        /// <param name="clientSupplementalData"><see cref="IList"/> (<see cref="SupplementalDataEntry"/>)</param>
+        /// <exception cref="IOException"></exception>
+        void ProcessClientSupplementalData(IList clientSupplementalData);
+
+        /// <summary>
+        /// Called by the protocol handler to report the client certificate, only if <c>GetCertificateRequest</c>
+        /// returned non-null.
+        /// </summary>
+        /// <remarks>Note: this method is responsible for certificate verification and validation.</remarks>
+        /// <param name="clientCertificate">the effective client certificate (may be an empty chain).</param>
+        /// <exception cref="IOException"></exception>
+        void NotifyClientCertificate(Certificate clientCertificate);
+
+        /// <summary>RFC 5077 3.3. NewSessionTicket Handshake Message.</summary>
+        /// <remarks>
+        /// This method will be called (only) if a NewSessionTicket extension was sent by the server. See
+        /// <i>RFC 5077 4. Recommended Ticket Construction</i> for recommended format and protection.
+        /// </remarks>
+        /// <returns>The <see cref="NewSessionTicket">ticket</see>)</returns>
+        /// <exception cref="IOException"></exception>
+        NewSessionTicket GetNewSessionTicket();
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSessionImpl.cs b/crypto/src/crypto/tls/TlsSessionImpl.cs
new file mode 100644
index 000000000..866392623
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSessionImpl.cs
@@ -0,0 +1,54 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    internal class TlsSessionImpl
+        :   TlsSession
+    {
+        internal readonly byte[] mSessionID;
+        internal SessionParameters mSessionParameters;
+
+        internal TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+        {
+            if (sessionID == null)
+                throw new ArgumentNullException("sessionID");
+            if (sessionID.Length < 1 || sessionID.Length > 32)
+                throw new ArgumentException("must have length between 1 and 32 bytes, inclusive", "sessionID");
+
+            this.mSessionID = Arrays.Clone(sessionID);
+            this.mSessionParameters = sessionParameters;
+        }
+
+        public virtual SessionParameters ExportSessionParameters()
+        {
+            lock (this)
+            {
+                return this.mSessionParameters == null ? null : this.mSessionParameters.Copy();
+            }
+        }
+
+        public virtual byte[] SessionID
+        {
+            get { lock (this) return mSessionID; }
+        }
+
+        public virtual void Invalidate()
+        {
+            lock (this)
+            {
+                if (this.mSessionParameters != null)
+                {
+                    this.mSessionParameters.Clear();
+                    this.mSessionParameters = null;
+                }
+            }
+        }
+
+        public virtual bool IsResumable
+        {
+            get { lock (this) return this.mSessionParameters != null; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs
new file mode 100644
index 000000000..ada08ef9f
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class TlsSrpUtilities
+    {
+        public static void AddSrpExtension(IDictionary extensions, byte[] identity)
+        {
+            extensions[ExtensionType.srp] = CreateSrpExtension(identity);
+        }
+
+        public static byte[] GetSrpExtension(IDictionary extensions)
+        {
+            byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.srp);
+            return extensionData == null ? null : ReadSrpExtension(extensionData);
+        }
+
+        public static byte[] CreateSrpExtension(byte[] identity)
+        {
+            if (identity == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            return TlsUtilities.EncodeOpaque8(identity);
+        }
+
+        public static byte[] ReadSrpExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+            byte[] identity = TlsUtilities.ReadOpaque8(buf);
+
+            TlsProtocol.AssertEmpty(buf);
+
+            return identity;
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 33d10dcd0..3fc6c7df1 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -634,6 +634,80 @@ namespace Org.BouncyCastle.Crypto.Tls
             return true;
         }
 
+        public static TlsSession ImportSession(byte[] sessionID, SessionParameters sessionParameters)
+        {
+            return new TlsSessionImpl(sessionID, sessionParameters);
+        }
+
+        public static bool IsSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
+        {
+            return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(clientVersion.GetEquivalentTLSVersion());
+        }
+
+        /**
+         * Add a 'signature_algorithms' extension to existing extensions.
+         *
+         * @param extensions                   A {@link Hashtable} to add the extension to.
+         * @param supportedSignatureAlgorithms {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+         * @throws IOException
+         */
+        public static void AddSignatureAlgorithmsExtension(IDictionary extensions, IList supportedSignatureAlgorithms)
+        {
+            extensions[ExtensionType.signature_algorithms] = CreateSignatureAlgorithmsExtension(supportedSignatureAlgorithms);
+        }
+
+        /**
+         * Get a 'signature_algorithms' extension from extensions.
+         *
+         * @param extensions A {@link Hashtable} to get the extension from, if it is present.
+         * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}, or null.
+         * @throws IOException
+         */
+        public static IList GetSignatureAlgorithmsExtension(IDictionary extensions)
+        {
+            byte[] extensionData = GetExtensionData(extensions, ExtensionType.signature_algorithms);
+            return extensionData == null ? null : ReadSignatureAlgorithmsExtension(extensionData);
+        }
+
+        /**
+         * Create a 'signature_algorithms' extension value.
+         *
+         * @param supportedSignatureAlgorithms A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+         * @return A byte array suitable for use as an extension value.
+         * @throws IOException
+         */
+        public static byte[] CreateSignatureAlgorithmsExtension(IList supportedSignatureAlgorithms)
+        {
+            MemoryStream buf = new MemoryStream();
+
+            // supported_signature_algorithms
+            EncodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf);
+
+            return buf.ToArray();
+        }
+
+        /**
+         * Read 'signature_algorithms' extension data.
+         *
+         * @param extensionData The extension data.
+         * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
+         * @throws IOException
+         */
+        public static IList ReadSignatureAlgorithmsExtension(byte[] extensionData)
+        {
+            if (extensionData == null)
+                throw new ArgumentNullException("extensionData");
+
+            MemoryStream buf = new MemoryStream(extensionData, false);
+
+            // supported_signature_algorithms
+            IList supported_signature_algorithms = ParseSupportedSignatureAlgorithms(false, buf);
+
+            TlsProtocol.AssertEmpty(buf);
+
+            return supported_signature_algorithms;
+        }
+
         public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous,
             Stream output)
         {
@@ -645,8 +719,8 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             // supported_signature_algorithms
             int length = 2 * supportedSignatureAlgorithms.Count;
-            TlsUtilities.CheckUint16(length);
-            TlsUtilities.WriteUint16(length, output);
+            CheckUint16(length);
+            WriteUint16(length, output);
 
             foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
             {
@@ -663,6 +737,30 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public static IList ParseSupportedSignatureAlgorithms(bool allowAnonymous, Stream input)
+        {
+            // supported_signature_algorithms
+            int length = ReadUint16(input);
+            if (length < 2 || (length & 1) != 0)
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+            int count = length / 2;
+            IList supportedSignatureAlgorithms = Platform.CreateArrayList(count);
+            for (int i = 0; i < count; ++i)
+            {
+                SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.Parse(input);
+                if (!allowAnonymous && entry.Signature == SignatureAlgorithm.anonymous)
+                {
+                    /*
+                     * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+                     * in Section 7.4.3. It MUST NOT appear in this extension.
+                     */
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
+                supportedSignatureAlgorithms.Add(entry);
+            }
+            return supportedSignatureAlgorithms;
+        }
+
         public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size)
         {
             ProtocolVersion version = context.ServerVersion;