summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-08-23 20:52:11 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-08-23 20:52:11 +0700
commit5078f3e6611b98fe50abcd3df3c63fb8c206f6b6 (patch)
tree2e540875099f89de29996b8e3599c90a4990e6b7
parentMore TLS porting from Java API (diff)
downloadBouncyCastle.NET-ed25519-5078f3e6611b98fe50abcd3df3c63fb8c206f6b6.tar.xz
Port latest TLS key exchange stuff from Java API
-rw-r--r--crypto/crypto.csproj5
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs166
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs378
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs226
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs16
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs196
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs97
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs243
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs190
-rw-r--r--crypto/src/crypto/tls/TlsKeyExchange.cs80
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs4
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs224
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs141
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs173
14 files changed, 1437 insertions, 702 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 6a3d80f7d..937e2a8de 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4299,6 +4299,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\AbstractTlsKeyExchange.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AbstractTlsPeer.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
new file mode 100644
index 000000000..155ac94d8
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
@@ -0,0 +1,166 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsKeyExchange
+        :   TlsKeyExchange
+    {
+        protected readonly int mKeyExchange;
+        protected IList mSupportedSignatureAlgorithms;
+
+        protected TlsContext context;
+
+        protected AbstractTlsKeyExchange(int keyExchange, IList supportedSignatureAlgorithms)
+        {
+            this.mKeyExchange = keyExchange;
+            this.mSupportedSignatureAlgorithms = supportedSignatureAlgorithms;
+        }
+
+        public virtual void Init(TlsContext context)
+        {
+            this.context = context;
+
+            ProtocolVersion clientVersion = context.ClientVersion;
+
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(clientVersion))
+            {
+                /*
+                 * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension,
+                 * the server MUST do the following:
+                 * 
+                 * - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, DH_RSA, RSA_PSK,
+                 * ECDH_RSA, ECDHE_RSA), behave as if client had sent the value {sha1,rsa}.
+                 * 
+                 * - If the negotiated key exchange algorithm is one of (DHE_DSS, DH_DSS), behave as if
+                 * the client had sent the value {sha1,dsa}.
+                 * 
+                 * - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, ECDHE_ECDSA),
+                 * behave as if the client had sent value {sha1,ecdsa}.
+                 */
+                if (this.mSupportedSignatureAlgorithms == null)
+                {
+                    switch (mKeyExchange)
+                    {
+                    case KeyExchangeAlgorithm.DH_DSS:
+                    case KeyExchangeAlgorithm.DHE_DSS:
+                    case KeyExchangeAlgorithm.SRP_DSS:
+                    {
+                        this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultDssSignatureAlgorithms();
+                        break;
+                    }
+
+                    case KeyExchangeAlgorithm.ECDH_ECDSA:
+                    case KeyExchangeAlgorithm.ECDHE_ECDSA:
+                    {
+                        this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+                        break;
+                    }
+
+                    case KeyExchangeAlgorithm.DH_RSA:
+                    case KeyExchangeAlgorithm.DHE_RSA:
+                    case KeyExchangeAlgorithm.ECDH_RSA:
+                    case KeyExchangeAlgorithm.ECDHE_RSA:
+                    case KeyExchangeAlgorithm.RSA:
+                    case KeyExchangeAlgorithm.RSA_PSK:
+                    case KeyExchangeAlgorithm.SRP_RSA:
+                    {
+                        this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+                        break;
+                    }
+
+                    case KeyExchangeAlgorithm.DHE_PSK:
+                    case KeyExchangeAlgorithm.ECDHE_PSK:
+                    case KeyExchangeAlgorithm.PSK:
+                    case KeyExchangeAlgorithm.SRP:
+                        break;
+
+                    default:
+                        throw new InvalidOperationException("unsupported key exchange algorithm");
+                    }
+                }
+
+            }
+            else if (this.mSupportedSignatureAlgorithms != null)
+            {
+                throw new InvalidOperationException("supported_signature_algorithms not allowed for " + clientVersion);
+            }
+        }
+
+        public abstract void SkipServerCredentials();
+
+        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        {
+            if (mSupportedSignatureAlgorithms == null)
+            {
+                /*
+                 * TODO RFC 2264 7.4.2. Unless otherwise specified, the signing algorithm for the
+                 * certificate must be the same as the algorithm for the certificate key.
+                 */
+            }
+            else
+            {
+                /*
+                 * TODO RFC 5264 7.4.2. If the client provided a "signature_algorithms" extension, then
+                 * all certificates provided by the server MUST be signed by a hash/signature algorithm
+                 * pair that appears in that extension.
+                 */
+            }
+        }
+
+        public virtual void ProcessServerCredentials(TlsCredentials serverCredentials)
+        {
+            ProcessServerCertificate(serverCredentials.Certificate);
+        }
+
+        public virtual bool RequiresServerKeyExchange
+        {
+            get { return false; }
+        }
+
+        public virtual byte[] GenerateServerKeyExchange()
+        {
+            if (RequiresServerKeyExchange)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            return null;
+        }
+
+        public virtual void SkipServerKeyExchange()
+        {
+            if (RequiresServerKeyExchange)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        public virtual void ProcessServerKeyExchange(Stream input)
+        {
+            if (!RequiresServerKeyExchange)
+            {
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            }
+        }
+
+        public abstract void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+        public virtual void SkipClientCredentials()
+        {
+        }
+
+        public abstract void ProcessClientCredentials(TlsCredentials clientCredentials);
+
+        public virtual void ProcessClientCertificate(Certificate clientCertificate)
+        {
+        }
+
+        public abstract void GenerateClientKeyExchange(Stream output);
+
+        public virtual void ProcessClientKeyExchange(Stream input)
+        {
+            // Key exchange implementation MUST support client key exchange
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        public abstract byte[] GeneratePremasterSecret();
+    }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index 65106adef..a2a04a33c 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -26,17 +26,14 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override int[] GetCipherSuites()
         {
-            return new int[] {
-                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+            return new int[]
+            {
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
                 CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_RSA_WITH_RC4_128_SHA,
             };
         }
 
@@ -44,62 +41,176 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             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_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_SEED_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_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_SEED_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_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_SEED_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_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SEED_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_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_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_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
             case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
                 return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
 
+            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_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+                return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
             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_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
             case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
                 return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
 
+            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+                return CreateRsaKeyExchange();
+
             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!
-                 */
+                    * 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);
             }
         }
@@ -108,82 +219,243 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             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_ECDSA_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_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_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
 
-            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_ECDSA_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_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
 
-            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_ECDSA_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);
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_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!
-                 */
+                    * 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(mContext, keyExchange);
+            return new TlsDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null);
         }
 
         protected virtual TlsKeyExchange CreateDheKeyExchange(int keyExchange)
         {
-            return new TlsDheKeyExchange(mContext, keyExchange);
+            return new TlsDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null);
         }
 
         protected virtual TlsKeyExchange CreateECDHKeyExchange(int keyExchange)
         {
-            return new TlsECDHKeyExchange(mContext, keyExchange);
+            return new TlsECDHKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+                mServerECPointFormats);
         }
 
         protected virtual TlsKeyExchange CreateECDheKeyExchange(int keyExchange)
         {
-            return new TlsECDheKeyExchange(mContext, keyExchange);
+            return new TlsECDheKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mNamedCurves, mClientECPointFormats,
+                mServerECPointFormats);
         }
 
         protected virtual TlsKeyExchange CreateRsaKeyExchange()
         {
-            return new TlsRsaKeyExchange(mContext);
+            return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index 620a6d8f7..6063572a0 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -21,19 +21,12 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override int[] GetCipherSuites()
         {
-            return new int[] {
-                CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
-                CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
-                CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA,
-                CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
-                CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA,
-                CipherSuite.TLS_PSK_WITH_RC4_128_SHA,
+            return new int[]
+            {
+                CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA
             };
         }
 
@@ -41,30 +34,92 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             switch (mSelectedCipherSuite)
             {
+            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_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+                return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
+
             case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
             case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
             case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
                 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_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
             case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
             case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
                 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 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!
-                 */
+                    * 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);
             }
         }
@@ -73,43 +128,134 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             switch (mSelectedCipherSuite)
             {
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
             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);
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
 
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
             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);
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
 
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
             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);
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
 
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
             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);
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, 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!
-                 */
+                    * 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(mContext, keyExchange, mPskIdentity);
+            return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, mNamedCurves,
+                mClientECPointFormats, mServerECPointFormats);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index a7c72b862..5d82ed470 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -24,9 +24,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mPassword = Arrays.Clone(password);
         }
 
+        protected virtual bool RequireSrpServerExtension
+        {
+            // No explicit guidance in RFC 5054; by default an (empty) extension from server is optional
+            get { return false; }
+        }
+
         public override int[] GetCipherSuites()
         {
-            return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
+            return new int[]
+            {
+                CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA
+            };
         }
 
         public override IDictionary GetClientExtensions()
@@ -41,7 +50,8 @@ namespace Org.BouncyCastle.Crypto.Tls
             if (!TlsUtilities.HasExpectedEmptyExtensionData(serverExtensions, ExtensionType.srp,
                 AlertDescription.illegal_parameter))
             {
-                // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
+                if (RequireSrpServerExtension)
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
             }
 
             base.ProcessServerExtensions(serverExtensions);
@@ -107,7 +117,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
         {
-            return new TlsSrpKeyExchange(mContext, keyExchange, mIdentity, mPassword);
+            return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mIdentity, mPassword);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
index ddc6a4527..b831249a6 100644
--- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -1,75 +1,87 @@
 using System;
+using System.Collections;
 using System.IO;
 
-using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    /// <summary>
-    /// TLS 1.0 DH key exchange.
-    /// </summary>
-    internal class TlsDHKeyExchange
-        : TlsKeyExchange
+    /// <summary>(D)TLS DH key exchange.</summary>
+    public class TlsDHKeyExchange
+        :   AbstractTlsKeyExchange
     {
-        protected TlsContext context;
-        protected int keyExchange;
-        protected TlsSigner tlsSigner;
+        protected TlsSigner mTlsSigner;
+        protected DHParameters mDHParameters;
 
-        protected AsymmetricKeyParameter serverPublicKey = null;
-        protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
-        protected TlsAgreementCredentials agreementCredentials;
-        protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+        protected AsymmetricKeyParameter mServerPublicKey;
+        protected DHPublicKeyParameters mDHAgreeServerPublicKey;
+        protected TlsAgreementCredentials mAgreementCredentials;
+        protected DHPrivateKeyParameters mDHAgreeClientPrivateKey;
 
-        internal TlsDHKeyExchange(TlsContext context, int keyExchange)
+        protected DHPrivateKeyParameters mDHAgreeServerPrivateKey;
+        protected DHPublicKeyParameters mDHAgreeClientPublicKey;
+
+        public TlsDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters)
+            :   base(keyExchange, supportedSignatureAlgorithms)
         {
             switch (keyExchange)
             {
-                case KeyExchangeAlgorithm.DH_RSA:
-                case KeyExchangeAlgorithm.DH_DSS:
-                    this.tlsSigner = null;
-                    break;
-                case KeyExchangeAlgorithm.DHE_RSA:
-                    this.tlsSigner = new TlsRsaSigner();
-                    break;
-                case KeyExchangeAlgorithm.DHE_DSS:
-                    this.tlsSigner = new TlsDssSigner();
-                    break;
-                default:
-                    throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+            case KeyExchangeAlgorithm.DH_RSA:
+            case KeyExchangeAlgorithm.DH_DSS:
+                this.mTlsSigner = null;
+                break;
+            case KeyExchangeAlgorithm.DHE_RSA:
+                this.mTlsSigner = new TlsRsaSigner();
+                break;
+            case KeyExchangeAlgorithm.DHE_DSS:
+                this.mTlsSigner = new TlsDssSigner();
+                break;
+            default:
+                throw new InvalidOperationException("unsupported key exchange algorithm");
             }
 
-            this.context = context;
-            this.keyExchange = keyExchange;
+            this.mDHParameters = dhParameters;
+        }
+
+        public override void Init(TlsContext context)
+        {
+            base.Init(context);
+
+            if (this.mTlsSigner != null)
+            {
+                this.mTlsSigner.Init(context);
+            }
         }
 
-        public virtual void SkipServerCertificate()
+        public override void SkipServerCredentials()
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        public override void ProcessServerCertificate(Certificate serverCertificate)
         {
+            if (serverCertificate.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
             X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
-            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
 
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
-                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+                this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
             }
             catch (Exception e)
             {
                 throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
             }
 
-            if (tlsSigner == null)
+            if (mTlsSigner == null)
             {
                 try
                 {
-                    this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+                    this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey);
                 }
                 catch (InvalidCastException e)
                 {
@@ -80,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
             else
             {
-                if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+                if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
                 {
                     throw new TlsFatalAlert(AlertDescription.certificate_unknown);
                 }
@@ -88,55 +100,51 @@ namespace Org.BouncyCastle.Crypto.Tls
                 TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
             }
 
-            // TODO
-            /*
-            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
-            * signing algorithm for the certificate must be the same as the algorithm for the
-            * certificate key."
-            */
-        }
-
-        public virtual void SkipServerKeyExchange()
-        {
-            // OK
+            base.ProcessServerCertificate(serverCertificate);
         }
 
-        public virtual void ProcessServerKeyExchange(Stream input)
+        public override bool RequiresServerKeyExchange
         {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            get
+            {
+                switch (mKeyExchange)
+                {
+                case KeyExchangeAlgorithm.DHE_DSS:
+                case KeyExchangeAlgorithm.DHE_RSA:
+                case KeyExchangeAlgorithm.DH_anon:
+                    return true;
+                default:
+                    return false;
+                }
+            }
         }
 
-        public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+        public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             byte[] types = certificateRequest.CertificateTypes;
-            foreach (byte type in types)
+            for (int i = 0; i < types.Length; ++i)
             {
-                switch (type)
+                switch (types[i])
                 {
-                    case ClientCertificateType.rsa_sign:
-                    case ClientCertificateType.dss_sign:
-                    case ClientCertificateType.rsa_fixed_dh:
-                    case ClientCertificateType.dss_fixed_dh:
-                    case ClientCertificateType.ecdsa_sign:
-                        break;
-                    default:
-                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                case ClientCertificateType.rsa_sign:
+                case ClientCertificateType.dss_sign:
+                case ClientCertificateType.rsa_fixed_dh:
+                case ClientCertificateType.dss_fixed_dh:
+                case ClientCertificateType.ecdsa_sign:
+                    break;
+                default:
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                 }
             }
         }
 
-        public virtual void SkipClientCredentials()
-        {
-            this.agreementCredentials = null;
-        }
-
-        public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+        public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
             if (clientCredentials is TlsAgreementCredentials)
             {
                 // TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
 
-                this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+                this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials;
             }
             else if (clientCredentials is TlsSignerCredentials)
             {
@@ -148,54 +156,38 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        public virtual void GenerateClientKeyExchange(Stream output)
+        public override void GenerateClientKeyExchange(Stream output)
         {
             /*
-             * RFC 2246 7.4.7.2 If the client certificate already contains a suitable
-             * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In
-             * this case, the Client Key Exchange message will be sent, but will be empty.
+             * RFC 2246 7.4.7.2 If the client certificate already contains a suitable Diffie-Hellman
+             * key, then Yc is implicit and does not need to be sent again. In this case, the Client Key
+             * Exchange message will be sent, but will be empty.
              */
-            if (agreementCredentials == null)
+            if (mAgreementCredentials == null)
             {
-                GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output);
+                this.mDHAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                    mDHAgreeServerPublicKey.Parameters, output);
             }
         }
 
-        public virtual byte[] GeneratePremasterSecret()
+        public override byte[] GeneratePremasterSecret()
         {
-            if (agreementCredentials != null)
+            if (mAgreementCredentials != null)
             {
-                return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey);
+                return mAgreementCredentials.GenerateAgreement(mDHAgreeServerPublicKey);
             }
 
-            return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
-        }
-        
-        protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b)
-        {
-            return a.P.Equals(b.P) && a.G.Equals(b.G);
-        }
-
-        protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
-            DHPrivateKeyParameters privateKey)
-        {
-            return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey);
-        }
-
-        protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams)
-        {
-            return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams);
-        }
+            if (mDHAgreeServerPrivateKey != null)
+            {
+                return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeClientPublicKey, mDHAgreeServerPrivateKey);
+            }
 
-        protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output)
-        {
-            this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
-                context.SecureRandom, dhParams, output);
-        }
+            if (mDHAgreeClientPrivateKey != null)
+            {
+                return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeServerPublicKey, mDHAgreeClientPrivateKey);
+            }
 
-        protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
-        {
-            return TlsDHUtilities.ValidateDHPublicKey(key);
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index a9e9a394c..3c05bb6f0 100644
--- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -1,53 +1,102 @@
 using System;
+using System.Collections;
 using System.IO;
 
-using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    internal class TlsDheKeyExchange
-        : TlsDHKeyExchange
+    public class TlsDheKeyExchange
+        :   TlsDHKeyExchange
     {
-        internal TlsDheKeyExchange(TlsContext context, int keyExchange)
-            : base(context, keyExchange)
+        protected TlsSignerCredentials mServerCredentials = null;
+
+        public TlsDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters)
+            :   base(keyExchange, supportedSignatureAlgorithms, dhParameters)
         {
         }
 
-        public override void SkipServerKeyExchange()
+        public override void ProcessServerCredentials(TlsCredentials serverCredentials)
         {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            if (!(serverCredentials is TlsSignerCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            ProcessServerCertificate(serverCredentials.Certificate);
+
+            this.mServerCredentials = (TlsSignerCredentials)serverCredentials;
         }
 
-        public override void ProcessServerKeyExchange(Stream input)
+        public override byte[] GenerateServerKeyExchange()
         {
-            SecurityParameters securityParameters = context.SecurityParameters;
+            if (this.mDHParameters == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            DigestInputBuffer buf = new DigestInputBuffer();
 
-            ISigner signer = InitSigner(tlsSigner, securityParameters);
-            Stream sigIn = new SignerStream(input, signer, null);
+            this.mDHAgreeServerPrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+                this.mDHParameters, buf);
 
-            byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn);
-            byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
-            byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn);
+            /*
+             * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+             */
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+            IDigest d;
 
-            byte[] sigByte = TlsUtilities.ReadOpaque16(input);
-            if (!signer.VerifySignature(sigByte))
+            if (TlsUtilities.IsTlsV12(context))
             {
-                throw new TlsFatalAlert(AlertDescription.decrypt_error);
+                signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm;
+                if (signatureAndHashAlgorithm == null)
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
+
+                d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
+            }
+            else
+            {
+                signatureAndHashAlgorithm = null;
+                d = new CombinedHash();
             }
 
-            BigInteger p = new BigInteger(1, pBytes);
-            BigInteger g = new BigInteger(1, gBytes);
-            BigInteger Ys = new BigInteger(1, YsBytes);
+            SecurityParameters securityParameters = context.SecurityParameters;
+            d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+            d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+            buf.UpdateDigest(d);
+
+            byte[] hash = DigestUtilities.DoFinal(d);
+
+            byte[] signature = mServerCredentials.GenerateCertificateSignature(hash);
+
+            DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+            signed_params.Encode(buf);
+
+            return buf.ToArray();
+        }
+
+        public override void ProcessServerKeyExchange(Stream input)
+        {
+            SecurityParameters securityParameters = context.SecurityParameters;
+
+            SignerInputBuffer buf = new SignerInputBuffer();
+            Stream teeIn = new TeeInputStream(input, buf);
+
+            ServerDHParams dhParams = ServerDHParams.Parse(teeIn);
+
+            DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+
+            ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+            buf.UpdateSigner(signer);
+            if (!signer.VerifySignature(signed_params.Signature))
+                throw new TlsFatalAlert(AlertDescription.decrypt_error);
 
-            this.dhAgreeServerPublicKey = ValidateDHPublicKey(
-                new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+            this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey);
         }
 
-        protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+        protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+            SecurityParameters securityParameters)
         {
-            ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+            ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
             signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
             signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
             return signer;
diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
index c4780eaaa..42dc2f2ef 100644
--- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -3,76 +3,87 @@ using System.Collections;
 using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto.Agreement;
-using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    /**
-    * ECDH key exchange (see RFC 4492)
-    */
-    internal class TlsECDHKeyExchange
-        : TlsKeyExchange
+    /// <summary>(D)TLS ECDH key exchange (see RFC 4492).</summary>
+    public class TlsECDHKeyExchange
+        :   AbstractTlsKeyExchange
     {
-        protected TlsContext context;
-        protected int keyExchange;
-        protected TlsSigner tlsSigner;
+        protected TlsSigner mTlsSigner;
+        protected int[] mNamedCurves;
+        protected byte[] mClientECPointFormats, mServerECPointFormats;
 
-        protected AsymmetricKeyParameter serverPublicKey;
-        protected ECPublicKeyParameters ecAgreeServerPublicKey;
-        protected TlsAgreementCredentials agreementCredentials;
-        protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null;
+        protected AsymmetricKeyParameter mServerPublicKey;
+        protected TlsAgreementCredentials mAgreementCredentials;
 
-        internal TlsECDHKeyExchange(TlsContext context, int keyExchange)
+        protected ECPrivateKeyParameters mECAgreePrivateKey;
+        protected ECPublicKeyParameters mECAgreePublicKey;
+
+        public TlsECDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves,
+            byte[] clientECPointFormats, byte[] serverECPointFormats)
+            :   base(keyExchange, supportedSignatureAlgorithms)
         {
             switch (keyExchange)
             {
-                case KeyExchangeAlgorithm.ECDHE_RSA:
-                    this.tlsSigner = new TlsRsaSigner();
-                    break;
-                case KeyExchangeAlgorithm.ECDHE_ECDSA:
-                    this.tlsSigner = new TlsECDsaSigner();
-                    break;
-                case KeyExchangeAlgorithm.ECDH_RSA:
-                case KeyExchangeAlgorithm.ECDH_ECDSA:
-                    this.tlsSigner = null;
-                    break;
-                default:
-                    throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+            case KeyExchangeAlgorithm.ECDHE_RSA:
+                this.mTlsSigner = new TlsRsaSigner();
+                break;
+            case KeyExchangeAlgorithm.ECDHE_ECDSA:
+                this.mTlsSigner = new TlsECDsaSigner();
+                break;
+            case KeyExchangeAlgorithm.ECDH_RSA:
+            case KeyExchangeAlgorithm.ECDH_ECDSA:
+                this.mTlsSigner = null;
+                break;
+            default:
+                throw new InvalidOperationException("unsupported key exchange algorithm");
             }
 
-            this.context = context;
-            this.keyExchange = keyExchange;
+            this.mNamedCurves = namedCurves;
+            this.mClientECPointFormats = clientECPointFormats;
+            this.mServerECPointFormats = serverECPointFormats;
         }
 
-        public virtual void SkipServerCertificate()
+        public override void Init(TlsContext context)
+        {
+            base.Init(context);
+
+            if (this.mTlsSigner != null)
+            {
+                this.mTlsSigner.Init(context);
+            }
+        }
+
+        public override void SkipServerCredentials()
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        public override void ProcessServerCertificate(Certificate serverCertificate)
         {
+            if (serverCertificate.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
             X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
-            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
 
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
-                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+                this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
             }
             catch (Exception e)
             {
                 throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
             }
 
-            if (tlsSigner == null)
+            if (mTlsSigner == null)
             {
                 try
                 {
-                    this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+                    this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey((ECPublicKeyParameters) this.mServerPublicKey);
                 }
                 catch (InvalidCastException e)
                 {
@@ -83,69 +94,63 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
             else
             {
-                if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
-                {
+                if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
                     throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-                }
 
                 TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
             }
-            
-            // TODO
-            /*
-            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
-            * signing algorithm for the certificate must be the same as the algorithm for the
-            * certificate key."
-            */
-        }
-        
-        public virtual void SkipServerKeyExchange()
-        {
-            // do nothing
+
+            base.ProcessServerCertificate(serverCertificate);
         }
 
-        public virtual void ProcessServerKeyExchange(Stream input)
+        public override bool RequiresServerKeyExchange
         {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            get
+            {
+                switch (mKeyExchange)
+                {
+                case KeyExchangeAlgorithm.ECDHE_ECDSA:
+                case KeyExchangeAlgorithm.ECDHE_RSA:
+                case KeyExchangeAlgorithm.ECDH_anon:
+                    return true;
+                default:
+                    return false;
+                }
+            }
         }
 
-        public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+        public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             /*
-             * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
-             * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
-             * prohibited because the use of a long-term ECDH client key would jeopardize the
-             * forward secrecy property of these algorithms.
+             * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+             * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+             * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+             * these algorithms.
              */
             byte[] types = certificateRequest.CertificateTypes;
-            foreach (byte type in types)
+            for (int i = 0; i < types.Length; ++i)
             {
-                switch (type)
+                switch (types[i])
                 {
-                    case ClientCertificateType.rsa_sign:
-                    case ClientCertificateType.dss_sign:
-                    case ClientCertificateType.ecdsa_sign:
-                    case ClientCertificateType.rsa_fixed_ecdh:
-                    case ClientCertificateType.ecdsa_fixed_ecdh:
-                        break;
-                    default:
-                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                case ClientCertificateType.rsa_sign:
+                case ClientCertificateType.dss_sign:
+                case ClientCertificateType.ecdsa_sign:
+                case ClientCertificateType.rsa_fixed_ecdh:
+                case ClientCertificateType.ecdsa_fixed_ecdh:
+                    break;
+                default:
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                 }
             }
         }
 
-        public virtual void SkipClientCredentials()
-        {
-            this.agreementCredentials = null;
-        }
-
-        public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+        public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
             if (clientCredentials is TlsAgreementCredentials)
             {
-                // TODO Validate client cert has matching parameters (see 'AreOnSameCurve')?
+                // TODO Validate client cert has matching parameters (see 'TlsEccUtilities.AreOnSameCurve')?
 
-                this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+                this.mAgreementCredentials = (TlsAgreementCredentials)clientCredentials;
             }
             else if (clientCredentials is TlsSignerCredentials)
             {
@@ -157,80 +162,50 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        public virtual void GenerateClientKeyExchange(Stream output)
+        public override void GenerateClientKeyExchange(Stream output)
         {
-            if (agreementCredentials == null)
+            if (mAgreementCredentials == null)
             {
-                GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output);
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                    mServerECPointFormats, mECAgreePublicKey.Parameters, output);
             }
         }
 
-        public virtual byte[] GeneratePremasterSecret()
+        public override void ProcessClientCertificate(Certificate clientCertificate)
         {
-            if (agreementCredentials != null)
-            {
-                return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey);
-            }
-
-            return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
-        }
-
-        protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
-        {
-            // TODO Move to ECDomainParameters.Equals() or other utility method?
-            return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H);
+            // TODO Extract the public key
+            // TODO If the certificate is 'fixed', take the public key as mECAgreeClientPublicKey
         }
 
-        protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters)
+        public override void ProcessClientKeyExchange(Stream input)
         {
-            // TODO Add support for compressed encoding and SPF extension
-
-            /*
-             * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format.
-             * Here, the format MUST conform to what the server has requested through a
-             * Supported Point Formats Extension if this extension was used, and MUST be
-             * uncompressed if this extension was not used.
-             */
-            return keyParameters.Q.GetEncoded();
-        }
+            if (mECAgreePublicKey != null)
+            {
+                // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
+                return;
+            }
 
-        protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams)
-        {
-            ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
-            ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams,
-                context.SecureRandom);
-            keyPairGenerator.Init(keyGenerationParameters);
-            return keyPairGenerator.GenerateKeyPair();
-        }
+            byte[] point = TlsUtilities.ReadOpaque8(input);
 
-        protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output)
-        {
-            AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams);
-            this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private;
+            ECDomainParameters curve_params = this.mECAgreePrivateKey.Parameters;
 
-            byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public);
-            TlsUtilities.WriteOpaque8(keData, output);
+            this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+                mServerECPointFormats, curve_params, point));
         }
 
-        protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey,
-            ECPrivateKeyParameters privateKey)
+        public override byte[] GeneratePremasterSecret()
         {
-            ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
-            basicAgreement.Init(privateKey);
-            BigInteger agreementValue = basicAgreement.CalculateAgreement(publicKey);
+            if (mAgreementCredentials != null)
+            {
+                return mAgreementCredentials.GenerateAgreement(mECAgreePublicKey);
+            }
 
-            /*
-             * RFC 4492 5.10. Note that this octet string (Z in IEEE 1363 terminology) as output by
-             * FE2OSP, the Field Element to Octet String Conversion Primitive, has constant length for
-             * any given field; leading zeros found in this octet string MUST NOT be truncated.
-             */
-            return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
-        }
+            if (mECAgreePrivateKey != null)
+            {
+                return TlsEccUtilities.CalculateECDHBasicAgreement(mECAgreePublicKey, mECAgreePrivateKey);
+            }
 
-        protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
-        {
-            // TODO Check RFC 4492 for validation
-            return key;
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index 5f66dbf59..0644bd44d 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -2,91 +2,186 @@ using System;
 using System.Collections;
 using System.IO;
 
-using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math.EC;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    /**
-    * ECDHE key exchange (see RFC 4492)
-    */
-    internal class TlsECDheKeyExchange : TlsECDHKeyExchange
+    /// <summary>(D)TLS ECDHE key exchange (see RFC 4492).</summary>
+    public class TlsECDheKeyExchange
+        :   TlsECDHKeyExchange
     {
-        internal TlsECDheKeyExchange(TlsContext context, int keyExchange)
-            : base(context, keyExchange)
-        {
-        }
+        protected TlsSignerCredentials mServerCredentials = null;
 
-        public override void SkipServerKeyExchange()
+        public TlsECDheKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, int[] namedCurves,
+            byte[] clientECPointFormats, byte[] serverECPointFormats)
+            :   base(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats)
         {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public override void ProcessServerKeyExchange(Stream input)
+        public override void ProcessServerCredentials(TlsCredentials serverCredentials)
         {
-            SecurityParameters securityParameters = context.SecurityParameters;
+            if (!(serverCredentials is TlsSignerCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
 
-            ISigner signer = InitSigner(tlsSigner, securityParameters);
-            Stream sigIn = new SignerStream(input, signer, null);
+            ProcessServerCertificate(serverCredentials.Certificate);
 
-            byte curveType = TlsUtilities.ReadUint8(sigIn);
-            ECDomainParameters curve_params;
+            this.mServerCredentials = (TlsSignerCredentials)serverCredentials;
+        }
 
-            //  Currently, we only support named curves
-            if (curveType == ECCurveType.named_curve)
+        public override byte[] GenerateServerKeyExchange()
+        {
+            /*
+             * First we try to find a supported named curve from the client's list.
+             */
+            int namedCurve = -1;
+            if (mNamedCurves == null)
             {
-                int namedCurve = TlsUtilities.ReadUint16(sigIn);
-
-                // TODO Check namedCurve is one we offered?
+                // TODO Let the peer choose the default named curve
+                namedCurve = NamedCurve.secp256r1;
+            }
+            else
+            {
+                for (int i = 0; i < mNamedCurves.Length; ++i)
+                {
+                    int entry = mNamedCurves[i];
+                    if (NamedCurve.IsValid(entry) && TlsEccUtilities.IsSupportedNamedCurve(entry))
+                    {
+                        namedCurve = entry;
+                        break;
+                    }
+                }
+            }
 
+            ECDomainParameters curve_params = null;
+            if (namedCurve >= 0)
+            {
                 curve_params = TlsEccUtilities.GetParametersForNamedCurve(namedCurve);
             }
             else
             {
-                // TODO Add support for explicit curve parameters (read from sigIn)
+                /*
+                 * If no named curves are suitable, check if the client supports explicit curves.
+                 */
+                if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+                {
+                    curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.secp256r1);
+                }
+                else if (Arrays.Contains(mNamedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+                {
+                    curve_params = TlsEccUtilities.GetParametersForNamedCurve(NamedCurve.sect283r1);
+                }
+            }
 
-                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            if (curve_params == null)
+            {
+                /*
+                 * NOTE: We shouldn't have negotiated ECDHE key exchange since we apparently can't find
+                 * a suitable curve.
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
 
-            byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn);
+            AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(context.SecureRandom, curve_params);
+            this.mECAgreePrivateKey = (ECPrivateKeyParameters)kp.Private;
 
-            byte[] sigByte = TlsUtilities.ReadOpaque16(input);
-            if (!signer.VerifySignature(sigByte))
+            DigestInputBuffer buf = new DigestInputBuffer();
+
+            if (namedCurve < 0)
             {
-                throw new TlsFatalAlert(AlertDescription.decrypt_error);
+                TlsEccUtilities.WriteExplicitECParameters(mClientECPointFormats, curve_params, buf);
+            }
+            else
+            {
+                TlsEccUtilities.WriteNamedECParameters(namedCurve, buf);
             }
 
-            // TODO Check curve_params not null
+            ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
+            TlsEccUtilities.WriteECPoint(mClientECPointFormats, ecPublicKey.Q, buf);
 
-            ECPoint Q = curve_params.Curve.DecodePoint(publicBytes);
+            /*
+             * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+             */
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+            IDigest d;
+
+            if (TlsUtilities.IsTlsV12(context))
+            {
+                signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm;
+                if (signatureAndHashAlgorithm == null)
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
 
-            this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params));
+                d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
+            }
+            else
+            {
+                signatureAndHashAlgorithm = null;
+                d = new CombinedHash();
+            }
+
+            SecurityParameters securityParameters = context.SecurityParameters;
+            d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+            d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+            buf.UpdateDigest(d);
+
+            byte[] hash = DigestUtilities.DoFinal(d);
+
+            byte[] signature = mServerCredentials.GenerateCertificateSignature(hash);
+
+            DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+            signed_params.Encode(buf);
+
+            return buf.ToArray();
         }
-        
+
+        public override void ProcessServerKeyExchange(Stream input)
+        {
+            SecurityParameters securityParameters = context.SecurityParameters;
+
+            SignerInputBuffer buf = new SignerInputBuffer();
+            Stream teeIn = new TeeInputStream(input, buf);
+
+            ECDomainParameters curve_params = TlsEccUtilities.ReadECParameters(mNamedCurves, mClientECPointFormats, teeIn);
+
+            byte[] point = TlsUtilities.ReadOpaque8(teeIn);
+
+            DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+
+            ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+            buf.UpdateSigner(signer);
+            if (!signer.VerifySignature(signed_params.Signature))
+                throw new TlsFatalAlert(AlertDescription.decrypt_error);
+
+            this.mECAgreePublicKey = TlsEccUtilities.ValidateECPublicKey(TlsEccUtilities.DeserializeECPublicKey(
+                mClientECPointFormats, curve_params, point));
+        }
+
         public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             /*
-             * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
-             * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
-             * prohibited because the use of a long-term ECDH client key would jeopardize the
-             * forward secrecy property of these algorithms.
+             * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
+             * ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is prohibited because
+             * the use of a long-term ECDH client key would jeopardize the forward secrecy property of
+             * these algorithms.
              */
             byte[] types = certificateRequest.CertificateTypes;
-            foreach (byte type in types)
+            for (int i = 0; i < types.Length; ++i)
             {
-                switch (type)
+                switch (types[i])
                 {
-                    case ClientCertificateType.rsa_sign:
-                    case ClientCertificateType.dss_sign:
-                    case ClientCertificateType.ecdsa_sign:
-                        break;
-                    default:
-                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                case ClientCertificateType.rsa_sign:
+                case ClientCertificateType.dss_sign:
+                case ClientCertificateType.ecdsa_sign:
+                    break;
+                default:
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                 }
             }
         }
-        
+
         public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
             if (clientCredentials is TlsSignerCredentials)
@@ -99,9 +194,10 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+        protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+            SecurityParameters securityParameters)
         {
-            ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+            ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
             signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
             signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
             return signer;
diff --git a/crypto/src/crypto/tls/TlsKeyExchange.cs b/crypto/src/crypto/tls/TlsKeyExchange.cs
index 5102edbec..6731f6f63 100644
--- a/crypto/src/crypto/tls/TlsKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -3,36 +3,52 @@ using System.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	/// <summary>
-	/// A generic interface for key exchange implementations in TLS 1.0.
-	/// </summary>
-	public interface TlsKeyExchange
-	{
-		/// <exception cref="IOException"/>
-		void SkipServerCertificate();
-
-		/// <exception cref="IOException"/>
-		void ProcessServerCertificate(Certificate serverCertificate);
-
-		/// <exception cref="IOException"/>
-		void SkipServerKeyExchange();
-
-		/// <exception cref="IOException"/>
-		void ProcessServerKeyExchange(Stream input);
-
-		/// <exception cref="IOException"/>
-		void ValidateCertificateRequest(CertificateRequest certificateRequest);
-
-		/// <exception cref="IOException"/>
-		void SkipClientCredentials();
-
-		/// <exception cref="IOException"/>
-		void ProcessClientCredentials(TlsCredentials clientCredentials);
-		
-		/// <exception cref="IOException"/>
-		void GenerateClientKeyExchange(Stream output);
-
-		/// <exception cref="IOException"/>
-		byte[] GeneratePremasterSecret();
-	}
+    /// <summary>
+    /// A generic interface for key exchange implementations in (D)TLS.
+    /// </summary>
+    public interface TlsKeyExchange
+    {
+        void Init(TlsContext context);
+
+        /// <exception cref="IOException"/>
+        void SkipServerCredentials();
+
+        /// <exception cref="IOException"/>
+        void ProcessServerCredentials(TlsCredentials serverCredentials);
+
+        /// <exception cref="IOException"/>
+        void ProcessServerCertificate(Certificate serverCertificate);
+
+        bool RequiresServerKeyExchange { get; }
+
+        /// <exception cref="IOException"/>
+        byte[] GenerateServerKeyExchange();
+
+        /// <exception cref="IOException"/>
+        void SkipServerKeyExchange();
+
+        /// <exception cref="IOException"/>
+        void ProcessServerKeyExchange(Stream input);
+
+        /// <exception cref="IOException"/>
+        void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+        /// <exception cref="IOException"/>
+        void SkipClientCredentials();
+
+        /// <exception cref="IOException"/>
+        void ProcessClientCredentials(TlsCredentials clientCredentials);
+
+        /// <exception cref="IOException"/>
+        void ProcessClientCertificate(Certificate clientCertificate);
+
+        /// <exception cref="IOException"/>
+        void GenerateClientKeyExchange(Stream output);
+
+        /// <exception cref="IOException"/>
+        void ProcessClientKeyExchange(Stream input);
+
+        /// <exception cref="IOException"/>
+        byte[] GeneratePremasterSecret();
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 3dec5ade0..21b3c8d47 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -486,7 +486,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                             if (connection_state == CS_SERVER_HELLO_RECEIVED)
                             {
                                 // There was no server certificate message; check it's OK
-                                this.keyExchange.SkipServerCertificate();
+                                this.keyExchange.SkipServerCredentials();
                                 this.authentication = null;
 
                                 // There was no server key exchange message; check it's OK
@@ -605,7 +605,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                             if (connection_state == CS_SERVER_HELLO_RECEIVED)
                             {
                                 // There was no server certificate message; check it's OK
-                                this.keyExchange.SkipServerCertificate();
+                                this.keyExchange.SkipServerCredentials();
                                 this.authentication = null;
                             }
 
diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index 24bf433dd..cd13e3438 100644
--- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -1,68 +1,118 @@
 using System;
+using System.Collections;
 using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    internal class TlsPskKeyExchange
-        : TlsKeyExchange
+    /// <summary>(D)TLS PSK key exchange (RFC 4279).</summary>
+    public class TlsPskKeyExchange
+        :   AbstractTlsKeyExchange
     {
-        protected TlsContext context;
-        protected int keyExchange;
-        protected TlsPskIdentity pskIdentity;
+        protected TlsPskIdentity mPskIdentity;
+        protected DHParameters mDHParameters;
+        protected int[] mNamedCurves;
+        protected byte[] mClientECPointFormats, mServerECPointFormats;
 
-        protected byte[] psk_identity_hint = null;
+        protected byte[] mPskIdentityHint = null;
 
-        protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
-        protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+        protected DHPrivateKeyParameters mDHAgreePrivateKey = null;
+        protected DHPublicKeyParameters mDHAgreePublicKey = null;
 
-        protected AsymmetricKeyParameter serverPublicKey = null;
-        protected RsaKeyParameters rsaServerPublicKey = null;
-        protected byte[] premasterSecret;
+        protected AsymmetricKeyParameter mServerPublicKey = null;
+        protected RsaKeyParameters mRsaServerPublicKey = null;
+        protected TlsEncryptionCredentials mServerCredentials = null;
+        protected byte[] mPremasterSecret;
 
-        internal TlsPskKeyExchange(TlsContext context, int keyExchange,
-            TlsPskIdentity pskIdentity)
+        public TlsPskKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsPskIdentity pskIdentity,
+            DHParameters dhParameters, int[] namedCurves, byte[] clientECPointFormats, byte[] serverECPointFormats)
+            :   base(keyExchange, supportedSignatureAlgorithms)
         {
             switch (keyExchange)
             {
-                case KeyExchangeAlgorithm.PSK:
-                case KeyExchangeAlgorithm.RSA_PSK:
-                case KeyExchangeAlgorithm.DHE_PSK:
-                    break;
-                default:
-                    throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+            case KeyExchangeAlgorithm.DHE_PSK:
+            case KeyExchangeAlgorithm.ECDHE_PSK:
+            case KeyExchangeAlgorithm.PSK:
+            case KeyExchangeAlgorithm.RSA_PSK:
+                break;
+            default:
+                throw new InvalidOperationException("unsupported key exchange algorithm");
             }
 
-            this.context = context;
-            this.keyExchange = keyExchange;
-            this.pskIdentity = pskIdentity;
+            this.mPskIdentity = pskIdentity;
+            this.mDHParameters = dhParameters;
+            this.mNamedCurves = namedCurves;
+            this.mClientECPointFormats = clientECPointFormats;
+            this.mServerECPointFormats = serverECPointFormats;
         }
 
-        public virtual void SkipServerCertificate()
+        public override void SkipServerCredentials()
         {
-            if (keyExchange == KeyExchangeAlgorithm.RSA_PSK)
-            {
+            if (mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
-            }
         }
 
-        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        public override void ProcessServerCredentials(TlsCredentials serverCredentials)
         {
-            if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
+            if (!(serverCredentials is TlsEncryptionCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            ProcessServerCertificate(serverCredentials.Certificate);
+
+            this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials;
+        }
+
+        public override byte[] GenerateServerKeyExchange()
+        {
+            // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys
+            this.mPskIdentityHint = null;
+
+            if (this.mPskIdentityHint == null && !RequiresServerKeyExchange)
+                return null;
+
+            MemoryStream buf = new MemoryStream();
+
+            if (this.mPskIdentityHint == null)
             {
-                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                TlsUtilities.WriteOpaque16(TlsUtilities.EmptyBytes, buf);
+            }
+            else
+            {
+                TlsUtilities.WriteOpaque16(this.mPskIdentityHint, buf);
             }
 
+            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            {
+                if (this.mDHParameters == null)
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
+
+                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+                    this.mDHParameters, buf);
+            }
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+            {
+                // TODO[RFC 5489]
+            }
+
+            return buf.ToArray();
+        }
+
+        public override void ProcessServerCertificate(Certificate serverCertificate)
+        {
+            if (mKeyExchange != KeyExchangeAlgorithm.RSA_PSK)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            if (serverCertificate.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
             X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
-            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
 
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
-                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+                this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
             }
             catch (Exception e)
             {
@@ -70,107 +120,92 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
 
             // Sanity check the PublicKeyFactory
-            if (this.serverPublicKey.IsPrivate)
-            {
+            if (this.mServerPublicKey.IsPrivate)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
 
-            this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
+            this.mRsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.mServerPublicKey);
 
             TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
 
-            // TODO
-            /*
-            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
-            * signing algorithm for the certificate must be the same as the algorithm for the
-            * certificate key."
-            */
+            base.ProcessServerCertificate(serverCertificate);
         }
 
-        public virtual void SkipServerKeyExchange()
+        public override bool RequiresServerKeyExchange
         {
-            if (keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            get
             {
-                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                switch (mKeyExchange)
+                {
+                case KeyExchangeAlgorithm.DHE_PSK:
+                case KeyExchangeAlgorithm.ECDHE_PSK:
+                    return true;
+                default:
+                    return false;
+                }
             }
-
-            this.psk_identity_hint = TlsUtilities.EmptyBytes;
         }
 
-        public virtual void ProcessServerKeyExchange(Stream input)
+        public override void ProcessServerKeyExchange(Stream input)
         {
-            this.psk_identity_hint = TlsUtilities.ReadOpaque16(input);
+            this.mPskIdentityHint = TlsUtilities.ReadOpaque16(input);
 
-            if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
-                byte[] pBytes = TlsUtilities.ReadOpaque16(input);
-                byte[] gBytes = TlsUtilities.ReadOpaque16(input);
-                byte[] YsBytes = TlsUtilities.ReadOpaque16(input);
+                ServerDHParams serverDHParams = ServerDHParams.Parse(input);
 
-                BigInteger p = new BigInteger(1, pBytes);
-                BigInteger g = new BigInteger(1, gBytes);
-                BigInteger Ys = new BigInteger(1, YsBytes);
-                
-                this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(
-                    new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+                this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(serverDHParams.PublicKey);
             }
-            else if (this.psk_identity_hint.Length == 0)
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                // TODO Should we enforce that this message should have been skipped if hint is empty?
-                //throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                // TODO[RFC 5489]
             }
         }
 
-        public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+        public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public virtual void SkipClientCredentials()
-        {
-            // OK
-        }
-
-        public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+        public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        public virtual void GenerateClientKeyExchange(Stream output)
+        public override void GenerateClientKeyExchange(Stream output)
         {
-            if (psk_identity_hint == null)
+            if (mPskIdentityHint == null)
             {
-                pskIdentity.SkipIdentityHint();
+                mPskIdentity.SkipIdentityHint();
             }
             else
             {
-                pskIdentity.NotifyIdentityHint(psk_identity_hint);
+                mPskIdentity.NotifyIdentityHint(mPskIdentityHint);
             }
 
-            byte[] psk_identity = pskIdentity.GetPskIdentity();
+            byte[] psk_identity = mPskIdentity.GetPskIdentity();
 
             TlsUtilities.WriteOpaque16(psk_identity, output);
 
-            if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
-                this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
-                    context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output);
+                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                    mDHAgreePublicKey.Parameters, output);
             }
-            else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
                 // TODO[RFC 5489]
                 throw new TlsFatalAlert(AlertDescription.internal_error);
             }
-            else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+            else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
             {
-                this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
-                    context, this.rsaServerPublicKey, output);
+                this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context,
+                    this.mRsaServerPublicKey, output);
             }
         }
 
-        public virtual byte[] GeneratePremasterSecret()
+        public override byte[] GeneratePremasterSecret()
         {
-            byte[] psk = pskIdentity.GetPsk();
+            byte[] psk = mPskIdentity.GetPsk();
             byte[] other_secret = GenerateOtherSecret(psk.Length);
 
             MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length);
@@ -181,14 +216,25 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual byte[] GenerateOtherSecret(int pskLength)
         {
-            if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
+            {
+                if (mDHAgreePrivateKey != null)
+                {
+                    return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey);
+                }
+
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+                // TODO[RFC 5489]
+                throw new TlsFatalAlert(AlertDescription.internal_error);
             }
 
-            if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+            if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
             {
-                return this.premasterSecret;
+                return this.mPremasterSecret;
             }
 
             return new byte[pskLength];
@@ -197,12 +243,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
         {
             // TODO What is the minimum bit length required?
-            //			key.Modulus.BitLength;
+            // key.Modulus.BitLength;
 
             if (!key.Exponent.IsProbablePrime(2))
-            {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-            }
 
             return key;
         }
diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index 160afa5c9..3a0a49154 100644
--- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -1,45 +1,57 @@
 using System;
+using System.Collections;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Encodings;
 using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    /// <summary>
-    /// TLS 1.0 RSA key exchange.
-    /// </summary>
-    internal class TlsRsaKeyExchange
-        : TlsKeyExchange
+    /// <summary>(D)TLS and SSLv3 RSA key exchange.</summary>
+    public class TlsRsaKeyExchange
+        :   AbstractTlsKeyExchange
     {
-        protected TlsContext context;
-
         protected AsymmetricKeyParameter serverPublicKey = null;
 
         protected RsaKeyParameters rsaServerPublicKey = null;
 
+        protected TlsEncryptionCredentials serverCredentials = null;
+
         protected byte[] premasterSecret;
 
-        internal TlsRsaKeyExchange(TlsContext context)
+        public TlsRsaKeyExchange(IList supportedSignatureAlgorithms)
+            :   base(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms)
         {
-            this.context = context;
         }
 
-        public virtual void SkipServerCertificate()
+        public override void SkipServerCredentials()
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+        {
+            if (!(serverCredentials is TlsEncryptionCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            ProcessServerCertificate(serverCredentials.Certificate);
+
+            this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+        }
+
+        public override void ProcessServerCertificate(Certificate serverCertificate)
         {
+            if (serverCertificate.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+
             X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
-            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
 
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
                 this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
@@ -51,111 +63,76 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             // Sanity check the PublicKeyFactory
             if (this.serverPublicKey.IsPrivate)
-            {
                 throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
 
             this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
 
             TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
 
-            // TODO
-            /*
-            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
-            * signing algorithm for the certificate must be the same as the algorithm for the
-            * certificate key."
-            */
+            base.ProcessServerCertificate(serverCertificate);
         }
 
-        public virtual void SkipServerKeyExchange()
-        {
-            // OK
-        }
-
-        public virtual void ProcessServerKeyExchange(Stream input)
-        {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
-        }
-
-        public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+        public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             byte[] types = certificateRequest.CertificateTypes;
-            foreach (byte type in types)
+            for (int i = 0; i < types.Length; ++i)
             {
-                switch (type)
+                switch (types[i])
                 {
-                    case ClientCertificateType.rsa_sign:
-                    case ClientCertificateType.dss_sign:
-                    case ClientCertificateType.ecdsa_sign:
-                        break;
-                    default:
-                        throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                case ClientCertificateType.rsa_sign:
+                case ClientCertificateType.dss_sign:
+                case ClientCertificateType.ecdsa_sign:
+                    break;
+                default:
+                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                 }
             }
         }
 
-        public virtual void SkipClientCredentials()
+        public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
-            // OK
+            if (!(clientCredentials is TlsSignerCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+        public override void GenerateClientKeyExchange(Stream output)
         {
-            if (!(clientCredentials is TlsSignerCredentials))
-            {
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
+            this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, rsaServerPublicKey, output);
         }
-        
-        public virtual void GenerateClientKeyExchange(Stream output)
+
+        public override void ProcessClientKeyExchange(Stream input)
         {
-            this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output);
+            byte[] encryptedPreMasterSecret;
+            if (TlsUtilities.IsSsl(context))
+            {
+                // TODO Do any SSLv3 clients actually include the length?
+                encryptedPreMasterSecret = Streams.ReadAll(input);
+            }
+            else
+            {
+                encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
+            }
+
+            this.premasterSecret = serverCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
         }
 
-        public virtual byte[] GeneratePremasterSecret()
+        public override byte[] GeneratePremasterSecret()
         {
+            if (this.premasterSecret == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
             byte[] tmp = this.premasterSecret;
             this.premasterSecret = null;
             return tmp;
         }
 
-        // Would be needed to process RSA_EXPORT server key exchange
-//	    protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer)
-//	    {
-//	        Stream sigIn = input;
-//	        if (signer != null)
-//	        {
-//	            sigIn = new SignerStream(input, signer, null);
-//	        }
-//
-//	        byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn);
-//	        byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn);
-//
-//	        if (signer != null)
-//	        {
-//	            byte[] sigByte = TlsUtilities.ReadOpaque16(input);
-//
-//	            if (!signer.VerifySignature(sigByte))
-//	            {
-//	                handler.FailWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
-//	            }
-//	        }
-//
-//	        BigInteger modulus = new BigInteger(1, modulusBytes);
-//	        BigInteger exponent = new BigInteger(1, exponentBytes);
-//
-//	        this.rsaServerPublicKey = ValidateRSAPublicKey(new RsaKeyParameters(false, modulus, exponent));
-//	    }
-
         protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
         {
             // TODO What is the minimum bit length required?
-//			key.Modulus.BitLength;
+            // key.Modulus.BitLength;
 
             if (!key.Exponent.IsProbablePrime(2))
-            {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-            }
 
             return key;
         }
diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index ff1bdac86..f42f7456d 100644
--- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -1,189 +1,175 @@
 using System;
+using System.Collections;
 using System.IO;
 
-using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Agreement;
 using Org.BouncyCastle.Crypto.Agreement.Srp;
-using Org.BouncyCastle.Crypto.Digests;
-using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    /// <summary>
-    /// TLS 1.1 SRP key exchange.
-    /// </summary>
-    internal class TlsSrpKeyExchange
-        : TlsKeyExchange
+    /// <summary>(D)TLS SRP key exchange (RFC 5054).</summary>
+    public class TlsSrpKeyExchange
+        :   AbstractTlsKeyExchange
     {
-        protected TlsContext context;
-        protected int keyExchange;
-        protected TlsSigner tlsSigner;
-        protected byte[] identity;
-        protected byte[] password;
+        protected TlsSigner mTlsSigner;
+        protected byte[] mIdentity;
+        protected byte[] mPassword;
 
-        protected AsymmetricKeyParameter serverPublicKey = null;
+        protected AsymmetricKeyParameter mServerPublicKey = null;
 
-        protected byte[] s = null;
-        protected BigInteger B = null;
-        protected Srp6Client srpClient = new Srp6Client();
+        protected byte[] mS = null;
+        protected BigInteger mB = null;
+        protected Srp6Client mSrpClient = new Srp6Client();
 
-        internal TlsSrpKeyExchange(TlsContext context, int keyExchange,
-            byte[] identity, byte[] password)
+        public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, byte[] password)
+            :   base(keyExchange, supportedSignatureAlgorithms)
         {
             switch (keyExchange)
             {
-                case KeyExchangeAlgorithm.SRP:
-                    this.tlsSigner = null;
-                    break;
-                case KeyExchangeAlgorithm.SRP_RSA:
-                    this.tlsSigner = new TlsRsaSigner();
-                    break;
-                case KeyExchangeAlgorithm.SRP_DSS:
-                    this.tlsSigner = new TlsDssSigner();
-                    break;
-                default:
-                    throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+            case KeyExchangeAlgorithm.SRP:
+                this.mTlsSigner = null;
+                break;
+            case KeyExchangeAlgorithm.SRP_RSA:
+                this.mTlsSigner = new TlsRsaSigner();
+                break;
+            case KeyExchangeAlgorithm.SRP_DSS:
+                this.mTlsSigner = new TlsDssSigner();
+                break;
+            default:
+                throw new InvalidOperationException("unsupported key exchange algorithm");
             }
 
-            this.context = context;
-            this.keyExchange = keyExchange;
-            this.identity = identity;
-            this.password = password;
+            this.mIdentity = identity;
+            this.mPassword = password;
         }
 
-        public virtual void SkipServerCertificate()
+        public override void Init(TlsContext context)
         {
-            if (tlsSigner != null)
+            base.Init(context);
+
+            if (this.mTlsSigner != null)
             {
-                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                this.mTlsSigner.Init(context);
             }
         }
 
-        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        public override void SkipServerCredentials()
         {
-            if (tlsSigner == null)
-            {
+            if (mTlsSigner != null)
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
-            }
+        }
+
+        public override void ProcessServerCertificate(Certificate serverCertificate)
+        {
+            if (mTlsSigner == null)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            if (serverCertificate.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
 
             X509CertificateStructure x509Cert = serverCertificate.GetCertificateAt(0);
-            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
 
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
-                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+                this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
             }
             catch (Exception e)
             {
                 throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
             }
 
-            if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
-            {
+            if (!mTlsSigner.IsValidPublicKey(this.mServerPublicKey))
                 throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-            }
 
             TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
 
-            // TODO
-            /*
-            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
-            * signing algorithm for the certificate must be the same as the algorithm for the
-            * certificate key."
-            */
+            base.ProcessServerCertificate(serverCertificate);
         }
 
-        public virtual void SkipServerKeyExchange()
+        public override bool RequiresServerKeyExchange
         {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            get { return true; }
         }
 
-        public virtual void ProcessServerKeyExchange(Stream input)
+        public override void ProcessServerKeyExchange(Stream input)
         {
             SecurityParameters securityParameters = context.SecurityParameters;
 
-            Stream sigIn = input;
-            ISigner signer = null;
+            SignerInputBuffer buf = null;
+            Stream teeIn = input;
 
-            if (tlsSigner != null)
+            if (mTlsSigner != null)
             {
-                signer = InitSigner(tlsSigner, securityParameters);
-                sigIn = new SignerStream(input, signer, null);
+                buf = new SignerInputBuffer();
+                teeIn = new TeeInputStream(input, buf);
             }
 
-            byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn);
-            byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
-            byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn);
-            byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn);
+            byte[] NBytes = TlsUtilities.ReadOpaque16(teeIn);
+            byte[] gBytes = TlsUtilities.ReadOpaque16(teeIn);
+            byte[] sBytes = TlsUtilities.ReadOpaque8(teeIn);
+            byte[] BBytes = TlsUtilities.ReadOpaque16(teeIn);
 
-            if (signer != null)
+            if (buf != null)
             {
-                byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+                DigitallySigned signed_params = DigitallySigned.Parse(context, input);
 
-                if (!signer.VerifySignature(sigByte))
-                {
+                ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
+                buf.UpdateSigner(signer);
+                if (!signer.VerifySignature(signed_params.Signature))
                     throw new TlsFatalAlert(AlertDescription.decrypt_error);
-                }
             }
 
             BigInteger N = new BigInteger(1, NBytes);
             BigInteger g = new BigInteger(1, gBytes);
 
             // TODO Validate group parameters (see RFC 5054)
-            //throw new TlsFatalAlert(AlertDescription.insufficient_security);
+    //        throw new TlsFatalAlert(AlertDescription.insufficient_security);
 
-            this.s = sBytes;
+            this.mS = sBytes;
 
             /*
-            * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter"
-            * alert if B % N = 0.
-            */
+             * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
+             * B % N = 0.
+             */
             try
             {
-                this.B = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
+                this.mB = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
             }
             catch (CryptoException e)
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
             }
 
-            this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom);
+            this.mSrpClient.Init(N, g, TlsUtilities.CreateHash(HashAlgorithm.sha1), context.SecureRandom);
         }
 
-        public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+        public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        public virtual void SkipClientCredentials()
-        {
-            // OK
-        }
-        
-        public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+        public override void ProcessClientCredentials(TlsCredentials clientCredentials)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        public virtual void GenerateClientKeyExchange(Stream output)
+        public override void GenerateClientKeyExchange(Stream output)
         {
-            byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s,
-                this.identity, this.password));
-            TlsUtilities.WriteOpaque16(keData, output);
+            BigInteger A = mSrpClient.GenerateClientCredentials(mS, this.mIdentity, this.mPassword);
+            TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(A), output);
         }
 
-        public virtual byte[] GeneratePremasterSecret()
+        public override byte[] GeneratePremasterSecret()
         {
             try
             {
                 // TODO Check if this needs to be a fixed size
-                return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B));
+                return BigIntegers.AsUnsignedByteArray(mSrpClient.CalculateSecret(mB));
             }
             catch (CryptoException e)
             {
@@ -191,9 +177,10 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+        protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
+            SecurityParameters securityParameters)
         {
-            ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+            ISigner signer = tlsSigner.CreateVerifyer(algorithm, this.mServerPublicKey);
             signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
             signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
             return signer;