summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 01:44:59 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2015-03-09 01:44:59 +0700
commitf7a9c0a501b6af92d3e729e08c73b5499a500c24 (patch)
treec2601bd90ae759a90a15ea41a7e82988dc11abd5 /crypto
parentPort TLS-PSK tests from Java (diff)
downloadBouncyCastle.NET-ed25519-f7a9c0a501b6af92d3e729e08c73b5499a500c24.tar.xz
Porting from Java build
- SRP6 evidence messages and standard groups
- TLS_SRP server-side support added
- TLS_DHE server-side fixes
- Improved support for DSA/ECDSA signing in TLS
Diffstat (limited to 'crypto')
-rw-r--r--crypto/crypto.csproj60
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Client.cs73
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Server.cs75
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs159
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6Utilities.cs70
-rw-r--r--crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs8
-rw-r--r--crypto/src/crypto/parameters/Srp6GroupParameters.cs27
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs22
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs4
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs9
-rw-r--r--crypto/src/crypto/tls/DefaultTlsServer.cs86
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs70
-rw-r--r--crypto/src/crypto/tls/DtlsClientProtocol.cs19
-rw-r--r--crypto/src/crypto/tls/PskTlsServer.cs2
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs6
-rw-r--r--crypto/src/crypto/tls/ServerSrpParams.cs75
-rw-r--r--crypto/src/crypto/tls/SessionParameters.cs20
-rw-r--r--crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs69
-rw-r--r--crypto/src/crypto/tls/SrpTlsClient.cs24
-rw-r--r--crypto/src/crypto/tls/SrpTlsServer.cs162
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs16
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs41
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs33
-rw-r--r--crypto/src/crypto/tls/TlsDsaSigner.cs3
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs2
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs26
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs2
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs19
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs28
-rw-r--r--crypto/src/crypto/tls/TlsSrpGroupVerifier.cs17
-rw-r--r--crypto/src/crypto/tls/TlsSrpIdentityManager.cs21
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs164
-rw-r--r--crypto/src/crypto/tls/TlsSrpLoginParameters.cs36
-rw-r--r--crypto/src/crypto/tls/TlsSrpUtilities.cs33
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs41
-rw-r--r--crypto/test/src/crypto/test/SRP6Test.cs49
-rw-r--r--crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs120
-rw-r--r--crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs113
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsClient.cs23
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsServer.cs49
-rw-r--r--crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs80
41 files changed, 1693 insertions, 263 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 84844e827..26af5666f 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -3154,6 +3154,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\agreement\srp\SRP6StandardGroups.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\agreement\srp\SRP6Utilities.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4174,6 +4179,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\parameters\Srp6GroupParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\parameters\TweakableBlockCipherParameters.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4479,6 +4489,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\DefaultTlsSrpGroupVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\DefaultTlsSignerCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4684,6 +4699,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\ServerSrpParams.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\ServerName.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4719,11 +4739,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SimulatedTlsSrpIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SrpTlsClient.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\SrpTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\SrtpProtectionProfile.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4984,6 +5014,21 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsSrpGroupVerifier.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSrpIdentityManager.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsSrpLoginParameters.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsSrpKeyExchange.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11043,6 +11088,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\MockSrpTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockSrpTlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\MockTlsClient.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11073,6 +11128,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\TlsSrpProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsServerTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/agreement/srp/SRP6Client.cs b/crypto/src/crypto/agreement/srp/SRP6Client.cs
index 309736564..f075d7ad1 100644
--- a/crypto/src/crypto/agreement/srp/SRP6Client.cs
+++ b/crypto/src/crypto/agreement/srp/SRP6Client.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 
@@ -24,7 +25,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	    protected BigInteger u;
 	    protected BigInteger S;
 
-	    protected IDigest digest;
+        protected BigInteger M1;
+	    protected BigInteger M2;
+	    protected BigInteger Key;
+
+        protected IDigest digest;
 	    protected SecureRandom random;
 
 	    public Srp6Client()
@@ -46,6 +51,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	        this.random = random;
 	    }
 
+        public virtual void Init(Srp6GroupParameters group, IDigest digest, SecureRandom random)
+        {
+            Init(group.N, group.G, digest, random);
+        }
+
 	    /**
 	     * Generates client's credentials given the client's salt, identity and password
 	     * @param salt The salt used in the client's verifier.
@@ -89,5 +99,66 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	        BigInteger tmp = g.ModPow(x, N).Multiply(k).Mod(N);
 	        return B.Subtract(tmp).Mod(N).ModPow(exp, N);
 	    }
+    
+        /**
+	     * Computes the client evidence message M1 using the previously received values.
+	     * To be called after calculating the secret S.
+	     * @return M1: the client side generated evidence message
+	     * @throws CryptoException
+	     */
+	    public virtual BigInteger CalculateClientEvidenceMessage()
+	    {
+		    // Verify pre-requirements
+		    if (this.pubA == null || this.B == null || this.S == null)
+		    {
+			    throw new CryptoException("Impossible to compute M1: " +
+					    "some data are missing from the previous operations (A,B,S)");
+		    }
+		    // compute the client evidence message 'M1'
+		    this.M1 = Srp6Utilities.CalculateM1(digest, N, pubA, B, S);  
+		    return M1;
+	    }
+
+        /** Authenticates the server evidence message M2 received and saves it only if correct.
+	     * @param M2: the server side generated evidence message
+	     * @return A boolean indicating if the server message M2 was the expected one.
+	     * @throws CryptoException
+	     */
+	    public virtual bool VerifyServerEvidenceMessage(BigInteger serverM2)
+	    {
+		    // Verify pre-requirements
+		    if (this.pubA == null || this.M1 == null || this.S == null)
+		    {
+			    throw new CryptoException("Impossible to compute and verify M2: " +
+					    "some data are missing from the previous operations (A,M1,S)");
+		    }
+
+		    // Compute the own server evidence message 'M2'
+		    BigInteger computedM2 = Srp6Utilities.CalculateM2(digest, N, pubA, M1, S);
+		    if (computedM2.Equals(serverM2))
+		    {
+			    this.M2 = serverM2;
+			    return true;
+		    }
+		    return false;
+	    }
+
+	    /**
+	     * Computes the final session key as a result of the SRP successful mutual authentication
+	     * To be called after verifying the server evidence message M2.
+	     * @return Key: the mutually authenticated symmetric session key
+	     * @throws CryptoException
+	     */
+	    public virtual BigInteger CalculateSessionKey()
+	    {
+		    // Verify pre-requirements (here we enforce a previous calculation of M1 and M2)
+		    if (this.S == null || this.M1 == null || this.M2 == null)
+		    {
+			    throw new CryptoException("Impossible to compute Key: " +
+					    "some data are missing from the previous operations (S,M1,M2)");
+		    }
+		    this.Key = Srp6Utilities.CalculateKey(digest, N, S);
+		    return Key;
+	    }
 	}
 }
diff --git a/crypto/src/crypto/agreement/srp/SRP6Server.cs b/crypto/src/crypto/agreement/srp/SRP6Server.cs
index 35b96d488..fd0c9f1cb 100644
--- a/crypto/src/crypto/agreement/srp/SRP6Server.cs
+++ b/crypto/src/crypto/agreement/srp/SRP6Server.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Security;
 
@@ -26,6 +27,9 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 
 	    protected BigInteger u;
 	    protected BigInteger S;
+        protected BigInteger M1;
+        protected BigInteger M2;
+        protected BigInteger Key;
 
 	    public Srp6Server()
 	    {
@@ -49,6 +53,11 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	        this.digest = digest;
 	    }
 
+        public virtual void Init(Srp6GroupParameters group, BigInteger v, IDigest digest, SecureRandom random)
+        {
+            Init(group.N, group.G, v, digest, random);
+        }
+
 	    /**
 	     * Generates the server's credentials that are to be sent to the client.
 	     * @return The server's public value to the client
@@ -82,9 +91,73 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	    	return Srp6Utilities.GeneratePrivateValue(digest, N, g, random);    	
 	    }
 
-		private BigInteger CalculateS()
+        private BigInteger CalculateS()
 	    {
 			return v.ModPow(u, N).Multiply(A).Mod(N).ModPow(privB, N);
 	    }
+
+        /** 
+	     * Authenticates the received client evidence message M1 and saves it only if correct.
+	     * To be called after calculating the secret S.
+	     * @param M1: the client side generated evidence message
+	     * @return A boolean indicating if the client message M1 was the expected one.
+	     * @throws CryptoException 
+	     */
+	    public virtual bool VerifyClientEvidenceMessage(BigInteger clientM1)
+	    {
+		    // Verify pre-requirements
+		    if (this.A == null || this.pubB == null || this.S == null)
+		    {
+			    throw new CryptoException("Impossible to compute and verify M1: " +
+					    "some data are missing from the previous operations (A,B,S)");
+		    }
+
+		    // Compute the own client evidence message 'M1'
+		    BigInteger computedM1 = Srp6Utilities.CalculateM1(digest, N, A, pubB, S);
+		    if (computedM1.Equals(clientM1))
+		    {
+			    this.M1 = clientM1;
+			    return true;
+		    }
+		    return false;
+	    }
+
+        /**
+	     * Computes the server evidence message M2 using the previously verified values.
+	     * To be called after successfully verifying the client evidence message M1.
+	     * @return M2: the server side generated evidence message
+	     * @throws CryptoException
+	     */
+	    public virtual BigInteger CalculateServerEvidenceMessage()
+	    {
+		    // Verify pre-requirements
+		    if (this.A == null || this.M1 == null || this.S == null)
+		    {
+			    throw new CryptoException("Impossible to compute M2: " +
+					    "some data are missing from the previous operations (A,M1,S)");
+		    }
+
+            // Compute the server evidence message 'M2'
+		    this.M2 = Srp6Utilities.CalculateM2(digest, N, A, M1, S);  
+		    return M2;
+	    }
+
+        /**
+	     * Computes the final session key as a result of the SRP successful mutual authentication
+	     * To be called after calculating the server evidence message M2.
+	     * @return Key: the mutual authenticated symmetric session key
+	     * @throws CryptoException
+	     */
+	    public virtual BigInteger CalculateSessionKey()
+	    {
+		    // Verify pre-requirements
+		    if (this.S == null || this.M1 == null || this.M2 == null)
+		    {
+			    throw new CryptoException("Impossible to compute Key: " +
+					    "some data are missing from the previous operations (S,M1,M2)");
+		    }
+		    this.Key = Srp6Utilities.CalculateKey(digest, N, S);
+		    return Key;
+	    }
 	}
 }
diff --git a/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs b/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs
new file mode 100644
index 000000000..36f4aba11
--- /dev/null
+++ b/crypto/src/crypto/agreement/srp/SRP6StandardGroups.cs
@@ -0,0 +1,159 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Agreement.Srp
+{
+    public class Srp6StandardGroups
+    {
+        private static BigInteger FromHex(string hex)
+        {
+            return new BigInteger(1, Hex.Decode(hex));
+        }
+
+        private static Srp6GroupParameters FromNG(string hexN, string hexG)
+        {
+            return new Srp6GroupParameters(FromHex(hexN), FromHex(hexG));
+        }
+
+        /*
+         * RFC 5054
+         */
+        private const string rfc5054_1024_N = "EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
+            + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
+            + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
+            + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A" + "FD5138FE8376435B9FC61D2FC0EB06E3";
+        private const string rfc5054_1024_g = "02";
+        public static readonly Srp6GroupParameters rfc5054_1024 = FromNG(rfc5054_1024_N, rfc5054_1024_g);
+
+        private const string rfc5054_1536_N = "9DEF3CAFB939277AB1F12A8617A47BBBDBA51DF499AC4C80BEEEA961"
+            + "4B19CC4D5F4F5F556E27CBDE51C6A94BE4607A291558903BA0D0F843"
+            + "80B655BB9A22E8DCDF028A7CEC67F0D08134B1C8B97989149B609E0B"
+            + "E3BAB63D47548381DBC5B1FC764E3F4B53DD9DA1158BFD3E2B9C8CF5"
+            + "6EDF019539349627DB2FD53D24B7C48665772E437D6C7F8CE442734A"
+            + "F7CCB7AE837C264AE3A9BEB87F8A2FE9B8B5292E5A021FFF5E91479E"
+            + "8CE7A28C2442C6F315180F93499A234DCF76E3FED135F9BB";
+        private const string rfc5054_1536_g = "02";
+        public static readonly Srp6GroupParameters rfc5054_1536 = FromNG(rfc5054_1536_N, rfc5054_1536_g);
+
+        private const string rfc5054_2048_N = "AC6BDB41324A9A9BF166DE5E1389582FAF72B6651987EE07FC319294"
+            + "3DB56050A37329CBB4A099ED8193E0757767A13DD52312AB4B03310D"
+            + "CD7F48A9DA04FD50E8083969EDB767B0CF6095179A163AB3661A05FB"
+            + "D5FAAAE82918A9962F0B93B855F97993EC975EEAA80D740ADBF4FF74"
+            + "7359D041D5C33EA71D281E446B14773BCA97B43A23FB801676BD207A"
+            + "436C6481F1D2B9078717461A5B9D32E688F87748544523B524B0D57D"
+            + "5EA77A2775D2ECFA032CFBDBF52FB3786160279004E57AE6AF874E73"
+            + "03CE53299CCC041C7BC308D82A5698F3A8D0C38271AE35F8E9DBFBB6"
+            + "94B5C803D89F7AE435DE236D525F54759B65E372FCD68EF20FA7111F" + "9E4AFF73";
+        private const string rfc5054_2048_g = "02";
+        public static readonly Srp6GroupParameters rfc5054_2048 = FromNG(rfc5054_2048_N, rfc5054_2048_g);
+
+        private const string rfc5054_3072_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+            + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+            + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+            + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+            + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+            + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+            + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+            + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+            + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+            + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+            + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+            + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+            + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC" + "E0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF";
+        private const string rfc5054_3072_g = "05";
+        public static readonly Srp6GroupParameters rfc5054_3072 = FromNG(rfc5054_3072_N, rfc5054_3072_g);
+
+        private const string rfc5054_4096_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+            + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+            + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+            + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+            + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+            + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+            + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+            + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+            + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+            + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+            + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+            + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+            + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+            + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+            + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+            + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+            + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+            + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199" + "FFFFFFFFFFFFFFFF";
+        private const string rfc5054_4096_g = "05";
+        public static readonly Srp6GroupParameters rfc5054_4096 = FromNG(rfc5054_4096_N, rfc5054_4096_g);
+
+        private const string rfc5054_6144_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+            + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+            + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+            + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+            + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+            + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+            + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+            + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+            + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+            + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+            + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+            + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+            + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+            + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+            + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+            + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+            + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+            + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+            + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+            + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+            + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+            + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+            + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+            + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+            + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+            + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+            + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E" + "6DCC4024FFFFFFFFFFFFFFFF";
+        private const string rfc5054_6144_g = "05";
+        public static readonly Srp6GroupParameters rfc5054_6144 = FromNG(rfc5054_6144_N, rfc5054_6144_g);
+
+        private const string rfc5054_8192_N = "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E08"
+            + "8A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B"
+            + "302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9"
+            + "A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE6"
+            + "49286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8"
+            + "FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D"
+            + "670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C"
+            + "180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718"
+            + "3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D"
+            + "04507A33A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7D"
+            + "B3970F85A6E1E4C7ABF5AE8CDB0933D71E8C94E04A25619DCEE3D226"
+            + "1AD2EE6BF12FFA06D98A0864D87602733EC86A64521F2B18177B200C"
+            + "BBE117577A615D6C770988C0BAD946E208E24FA074E5AB3143DB5BFC"
+            + "E0FD108E4B82D120A92108011A723C12A787E6D788719A10BDBA5B26"
+            + "99C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8DBBBC2DB"
+            + "04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2"
+            + "233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127"
+            + "D5B05AA993B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934028492"
+            + "36C3FAB4D27C7026C1D4DCB2602646DEC9751E763DBA37BDF8FF9406"
+            + "AD9E530EE5DB382F413001AEB06A53ED9027D831179727B0865A8918"
+            + "DA3EDBEBCF9B14ED44CE6CBACED4BB1BDB7F1447E6CC254B33205151"
+            + "2BD7AF426FB8F401378CD2BF5983CA01C64B92ECF032EA15D1721D03"
+            + "F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E59E7C97F"
+            + "BEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA"
+            + "CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58B"
+            + "B7C5DA76F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632"
+            + "387FE8D76E3C0468043E8F663F4860EE12BF2D5B0B7474D6E694F91E"
+            + "6DBE115974A3926F12FEE5E438777CB6A932DF8CD8BEC4D073B931BA"
+            + "3BC832B68D9DD300741FA7BF8AFC47ED2576F6936BA424663AAB639C"
+            + "5AE4F5683423B4742BF1C978238F16CBE39D652DE3FDB8BEFC848AD9"
+            + "22222E04A4037C0713EB57A81A23F0C73473FC646CEA306B4BCBC886"
+            + "2F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6"
+            + "6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC5"
+            + "0846851DF9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268"
+            + "359046F4EB879F924009438B481C6CD7889A002ED5EE382BC9190DA6"
+            + "FC026E479558E4475677E9AA9E3050E2765694DFC81F56E880B96E71" + "60C980DD98EDD3DFFFFFFFFFFFFFFFFF";
+        private const string rfc5054_8192_g = "13";
+        public static readonly Srp6GroupParameters rfc5054_8192 = FromNG(rfc5054_8192_N, rfc5054_8192_g);
+    }
+}
diff --git a/crypto/src/crypto/agreement/srp/SRP6Utilities.cs b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
index 4e790f572..ef6d8f24c 100644
--- a/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
+++ b/crypto/src/crypto/agreement/srp/SRP6Utilities.cs
@@ -54,7 +54,75 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 		    return val;
 		}
 
-		private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2)
+        /** 
+         * Computes the client evidence message (M1) according to the standard routine:
+         * M1 = H( A | B | S )
+         * @param digest The Digest used as the hashing function H
+         * @param N Modulus used to get the pad length
+         * @param A The public client value
+         * @param B The public server value
+         * @param S The secret calculated by both sides
+         * @return M1 The calculated client evidence message
+         */
+        public static BigInteger CalculateM1(IDigest digest, BigInteger N, BigInteger A, BigInteger B, BigInteger S)
+        {
+            BigInteger M1 = HashPaddedTriplet(digest, N, A, B, S);
+            return M1;
+        }
+
+        /** 
+         * Computes the server evidence message (M2) according to the standard routine:
+         * M2 = H( A | M1 | S )
+         * @param digest The Digest used as the hashing function H
+         * @param N Modulus used to get the pad length
+         * @param A The public client value
+         * @param M1 The client evidence message
+         * @param S The secret calculated by both sides
+         * @return M2 The calculated server evidence message
+         */
+        public static BigInteger CalculateM2(IDigest digest, BigInteger N, BigInteger A, BigInteger M1, BigInteger S)
+        {
+            BigInteger M2 = HashPaddedTriplet(digest, N, A, M1, S);
+            return M2;
+        }
+
+        /**
+         * Computes the final Key according to the standard routine: Key = H(S)
+         * @param digest The Digest used as the hashing function H
+         * @param N Modulus used to get the pad length
+         * @param S The secret calculated by both sides
+         * @return
+         */
+        public static BigInteger CalculateKey(IDigest digest, BigInteger N, BigInteger S)
+        {
+            int padLength = (N.BitLength + 7) / 8;
+            byte[] _S = GetPadded(S, padLength);
+            digest.BlockUpdate(_S, 0, _S.Length);
+
+            byte[] output = new byte[digest.GetDigestSize()];
+            digest.DoFinal(output, 0);
+            return new BigInteger(1, output);
+        }
+
+        private static BigInteger HashPaddedTriplet(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2, BigInteger n3)
+        {
+            int padLength = (N.BitLength + 7) / 8;
+
+            byte[] n1_bytes = GetPadded(n1, padLength);
+            byte[] n2_bytes = GetPadded(n2, padLength);
+            byte[] n3_bytes = GetPadded(n3, padLength);
+
+            digest.BlockUpdate(n1_bytes, 0, n1_bytes.Length);
+            digest.BlockUpdate(n2_bytes, 0, n2_bytes.Length);
+            digest.BlockUpdate(n3_bytes, 0, n3_bytes.Length);
+
+            byte[] output = new byte[digest.GetDigestSize()];
+            digest.DoFinal(output, 0);
+
+            return new BigInteger(1, output);
+        }
+
+        private static BigInteger HashPaddedPair(IDigest digest, BigInteger N, BigInteger n1, BigInteger n2)
 		{
 	    	int padLength = (N.BitLength + 7) / 8;
 
diff --git a/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
index 264833b4d..956973598 100644
--- a/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
+++ b/crypto/src/crypto/agreement/srp/SRP6VerifierGenerator.cs
@@ -1,5 +1,6 @@
 using System;
 
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Crypto.Agreement.Srp
@@ -31,7 +32,12 @@ namespace Org.BouncyCastle.Crypto.Agreement.Srp
 	        this.digest = digest;
 	    }
 
-	    /**
+        public virtual void Init(Srp6GroupParameters group, IDigest digest)
+        {
+            Init(group.N, group.G, digest);
+        }
+
+        /**
 	     * Creates a new SRP verifier
 	     * @param salt The salt to use, generally should be large and random
 	     * @param identity The user's identifying information (eg. username)
diff --git a/crypto/src/crypto/parameters/Srp6GroupParameters.cs b/crypto/src/crypto/parameters/Srp6GroupParameters.cs
new file mode 100644
index 000000000..6762dd31d
--- /dev/null
+++ b/crypto/src/crypto/parameters/Srp6GroupParameters.cs
@@ -0,0 +1,27 @@
+using System;
+
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Parameters
+{
+    public sealed class Srp6GroupParameters
+    {
+        private readonly BigInteger n, g;
+
+        public Srp6GroupParameters(BigInteger N, BigInteger g)
+        {
+            this.n = N;
+            this.g = g;
+        }
+
+        public BigInteger G
+        {
+            get { return g; }
+        }
+
+        public BigInteger N
+        {
+            get { return n; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
index 771bc004b..569979288 100644
--- a/crypto/src/crypto/tls/AbstractTlsClient.cs
+++ b/crypto/src/crypto/tls/AbstractTlsClient.cs
@@ -117,27 +117,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             {
                 // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
 
-                byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
-                    HashAlgorithm.sha224, HashAlgorithm.sha1 };
-
-                // TODO Sort out ECDSA signatures and add them as the preferred option here
-                byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa };
-
-                this.mSupportedSignatureAlgorithms = Platform.CreateArrayList();
-                for (int i = 0; i < hashAlgorithms.Length; ++i)
-                {
-                    for (int j = 0; j < signatureAlgorithms.Length; ++j)
-                    {
-                        this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i],
-                            signatureAlgorithms[j]));
-                    }
-                }
-
-                /*
-                 * RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
-                 */
-                this.mSupportedSignatureAlgorithms.Add(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
-                    SignatureAlgorithm.dsa));
+                this.mSupportedSignatureAlgorithms = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
 
                 clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(clientExtensions);
 
diff --git a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
index 155ac94d8..c9ec06107 100644
--- a/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
+++ b/crypto/src/crypto/tls/AbstractTlsKeyExchange.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected readonly int mKeyExchange;
         protected IList mSupportedSignatureAlgorithms;
 
-        protected TlsContext context;
+        protected TlsContext mContext;
 
         protected AbstractTlsKeyExchange(int keyExchange, IList supportedSignatureAlgorithms)
         {
@@ -20,7 +20,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public virtual void Init(TlsContext context)
         {
-            this.context = context;
+            this.mContext = context;
 
             ProtocolVersion clientVersion = context.ClientVersion;
 
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index ec98413b7..f07080cbd 100644
--- a/crypto/src/crypto/tls/DefaultTlsClient.cs
+++ b/crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -28,9 +28,18 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             return new int[]
             {
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
                 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_DHE_DSS_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_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,
diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs
index 75f6d8d88..156eff8af 100644
--- a/crypto/src/crypto/tls/DefaultTlsServer.cs
+++ b/crypto/src/crypto/tls/DefaultTlsServer.cs
@@ -20,6 +20,16 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
         }
 
+        protected virtual TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        protected virtual TlsSignerCredentials GetECDsaSignerCredentials()
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
         protected virtual TlsEncryptionCredentials GetRsaEncryptionCredentials()
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -45,6 +55,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                 CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
                 CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                 CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+                CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
                 CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
                 CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                 CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256,
@@ -58,6 +74,76 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             switch (mSelectedCipherSuite)
             {
+            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_DES_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+            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_DES_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return GetDsaSignerCredentials();
+
+            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_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            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_CCM:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
+            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_CCM:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
+            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 GetECDsaSignerCredentials();
+
             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:
diff --git a/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs b/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs
new file mode 100644
index 000000000..cc933bff9
--- /dev/null
+++ b/crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class DefaultTlsSrpGroupVerifier
+        :   TlsSrpGroupVerifier
+    {
+        protected static readonly IList DefaultGroups = Platform.CreateArrayList();
+
+        static DefaultTlsSrpGroupVerifier()
+        {
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_1024);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_1536);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_2048);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_3072);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_4096);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_6144);
+            DefaultGroups.Add(Srp6StandardGroups.rfc5054_8192);
+        }
+
+        // Vector is (SRP6GroupParameters)
+        protected readonly IList mGroups;
+
+        /**
+         * Accept only the group parameters specified in RFC 5054 Appendix A.
+         */
+        public DefaultTlsSrpGroupVerifier()
+            :   this(DefaultGroups)
+        {
+        }
+
+        /**
+         * Specify a custom set of acceptable group parameters.
+         * 
+         * @param groups a {@link Vector} of acceptable {@link SRP6GroupParameters}
+         */
+        public DefaultTlsSrpGroupVerifier(IList groups)
+        {
+            this.mGroups = groups;
+        }
+
+        public virtual bool Accept(Srp6GroupParameters group)
+        {
+            foreach (Srp6GroupParameters entry in mGroups)
+            {
+                if (AreGroupsEqual(group, entry))
+                {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        protected virtual bool AreGroupsEqual(Srp6GroupParameters a, Srp6GroupParameters b)
+        {
+            return a == b || (AreParametersEqual(a.N, b.N) && AreParametersEqual(a.G, b.G));
+        }
+
+        protected virtual bool AreParametersEqual(BigInteger a, BigInteger b)
+        {
+            return a == b || a.Equals(b);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/DtlsClientProtocol.cs b/crypto/src/crypto/tls/DtlsClientProtocol.cs
index 2aa4df692..200bb1946 100644
--- a/crypto/src/crypto/tls/DtlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs
@@ -317,23 +317,17 @@ namespace Org.BouncyCastle.Crypto.Tls
                 /*
                  * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                  */
-                SignatureAndHashAlgorithm signatureAndHashAlgorithm;
-                byte[] hash;
+                SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
+                    state.clientContext, signerCredentials);
 
-                if (TlsUtilities.IsTlsV12(state.clientContext))
+                byte[] hash;
+                if (signatureAndHashAlgorithm == null)
                 {
-                    signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
-                    if (signatureAndHashAlgorithm == null)
-                    {
-                        throw new TlsFatalAlert(AlertDescription.internal_error);
-                    }
-
-                    hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
+                    hash = securityParameters.SessionHash;
                 }
                 else
                 {
-                    signatureAndHashAlgorithm = null;
-                    hash = securityParameters.SessionHash;
+                    hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
                 }
 
                 byte[] signature = signerCredentials.GenerateCertificateSignature(hash);
@@ -375,6 +369,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                     .SetMasterSecret(securityParameters.masterSecret)
                     .SetPeerCertificate(serverCertificate)
                     .SetPskIdentity(securityParameters.pskIdentity)
+                    .SetSrpIdentity(securityParameters.srpIdentity)
                     .Build();
 
                 state.tlsSession = TlsUtilities.ImportSession(state.tlsSession.SessionID, state.sessionParameters);
diff --git a/crypto/src/crypto/tls/PskTlsServer.cs b/crypto/src/crypto/tls/PskTlsServer.cs
index bdb8b74a5..335c9de86 100644
--- a/crypto/src/crypto/tls/PskTlsServer.cs
+++ b/crypto/src/crypto/tls/PskTlsServer.cs
@@ -338,7 +338,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        protected TlsKeyExchange CreatePskKeyExchange(int keyExchange)
+        protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
         {
             return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, null, mPskIdentityManager,
                 GetDHParameters(), mNamedCurves, mClientECPointFormats, mServerECPointFormats);
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 0f48ee23e..3b851587d 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -16,6 +16,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         internal byte[] serverRandom = null;
         internal byte[] sessionHash = null;
         internal byte[] pskIdentity = null;
+        internal byte[] srpIdentity = null;
 
         // TODO Keep these internal, since it's maybe not the ideal place for them
         internal short maxFragmentLength = -1;
@@ -93,5 +94,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             get { return pskIdentity; }
         }
+
+        public virtual byte[] SrpIdentity
+        {
+            get { return srpIdentity; }
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/ServerSrpParams.cs b/crypto/src/crypto/tls/ServerSrpParams.cs
new file mode 100644
index 000000000..556ac5310
--- /dev/null
+++ b/crypto/src/crypto/tls/ServerSrpParams.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class ServerSrpParams
+    {
+        protected BigInteger m_N, m_g, m_B;
+        protected byte[] m_s;
+
+        public ServerSrpParams(BigInteger N, BigInteger g, byte[] s, BigInteger B)
+        {
+            this.m_N = N;
+            this.m_g = g;
+            this.m_s = Arrays.Clone(s);
+            this.m_B = B;
+        }
+
+        public virtual BigInteger B
+        {
+            get { return m_B; }
+        }
+
+        public virtual BigInteger G
+        {
+            get { return m_g; }
+        }
+
+        public virtual BigInteger N
+        {
+            get { return m_N; }
+        }
+
+        public virtual byte[] S
+        {
+            get { return m_s; }
+        }
+
+        /**
+         * Encode this {@link ServerSRPParams} to an {@link OutputStream}.
+         * 
+         * @param output
+         *            the {@link OutputStream} to encode to.
+         * @throws IOException
+         */
+        public virtual void Encode(Stream output)
+        {
+            TlsSrpUtilities.WriteSrpParameter(m_N, output);
+            TlsSrpUtilities.WriteSrpParameter(m_g, output);
+            TlsUtilities.WriteOpaque8(m_s, output);
+            TlsSrpUtilities.WriteSrpParameter(m_B, output);
+        }
+
+        /**
+         * Parse a {@link ServerSRPParams} from an {@link InputStream}.
+         * 
+         * @param input
+         *            the {@link InputStream} to parse from.
+         * @return a {@link ServerSRPParams} object.
+         * @throws IOException
+         */
+        public static ServerSrpParams Parse(Stream input)
+        {
+            BigInteger N = TlsSrpUtilities.ReadSrpParameter(input);
+            BigInteger g = TlsSrpUtilities.ReadSrpParameter(input);
+            byte[] s = TlsUtilities.ReadOpaque8(input);
+            BigInteger B = TlsSrpUtilities.ReadSrpParameter(input);
+
+            return new ServerSrpParams(N, g, s, B);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/SessionParameters.cs b/crypto/src/crypto/tls/SessionParameters.cs
index b17e931d7..a1eb5f27c 100644
--- a/crypto/src/crypto/tls/SessionParameters.cs
+++ b/crypto/src/crypto/tls/SessionParameters.cs
@@ -15,6 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             private byte[] mMasterSecret = null;
             private Certificate mPeerCertificate = null;
             private byte[] mPskIdentity = null;
+            private byte[] mSrpIdentity = null;
             private byte[] mEncodedServerExtensions = null;
 
             public Builder()
@@ -27,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 Validate(this.mCompressionAlgorithm >= 0, "compressionAlgorithm");
                 Validate(this.mMasterSecret != null, "masterSecret");
                 return new SessionParameters(mCipherSuite, (byte)mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                    mPskIdentity, mEncodedServerExtensions);
+                    mPskIdentity, mSrpIdentity, mEncodedServerExtensions);
             }
 
             public Builder SetCipherSuite(int cipherSuite)
@@ -60,6 +61,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                 return this;
             }
 
+            public Builder SetSrpIdentity(byte[] srpIdentity)
+            {
+                this.mSrpIdentity = srpIdentity;
+                return this;
+            }
+
             public Builder SetServerExtensions(IDictionary serverExtensions)
             {
                 if (serverExtensions == null)
@@ -87,16 +94,18 @@ namespace Org.BouncyCastle.Crypto.Tls
         private byte[] mMasterSecret;
         private Certificate mPeerCertificate;
         private byte[] mPskIdentity;
+        private byte[] mSrpIdentity;
         private byte[] mEncodedServerExtensions;
 
         private SessionParameters(int cipherSuite, byte compressionAlgorithm, byte[] masterSecret,
-            Certificate peerCertificate, byte[] pskIdentity, byte[] encodedServerExtensions)
+            Certificate peerCertificate, byte[] pskIdentity, byte[] srpIdentity, byte[] encodedServerExtensions)
         {
             this.mCipherSuite = cipherSuite;
             this.mCompressionAlgorithm = compressionAlgorithm;
             this.mMasterSecret = Arrays.Clone(masterSecret);
             this.mPeerCertificate = peerCertificate;
             this.mPskIdentity = Arrays.Clone(pskIdentity);
+            this.mSrpIdentity = Arrays.Clone(srpIdentity);
             this.mEncodedServerExtensions = encodedServerExtensions;
         }
 
@@ -111,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public SessionParameters Copy()
         {
             return new SessionParameters(mCipherSuite, mCompressionAlgorithm, mMasterSecret, mPeerCertificate,
-                mPskIdentity, mEncodedServerExtensions);
+                mPskIdentity, mSrpIdentity, mEncodedServerExtensions);
         }
 
         public int CipherSuite
@@ -139,6 +148,11 @@ namespace Org.BouncyCastle.Crypto.Tls
             get { return mPskIdentity; }
         }
 
+        public byte[] SrpIdentity
+        {
+            get { return mSrpIdentity; }
+        }
+
         public IDictionary ReadServerExtensions()
         {
             if (mEncodedServerExtensions == null)
diff --git a/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs b/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs
new file mode 100644
index 000000000..3e9737cd7
--- /dev/null
+++ b/crypto/src/crypto/tls/SimulatedTlsSrpIdentityManager.cs
@@ -0,0 +1,69 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    /**
+     * An implementation of {@link TlsSRPIdentityManager} that simulates the existence of "unknown" identities
+     * to obscure the fact that there is no verifier for them. 
+     */
+    public class SimulatedTlsSrpIdentityManager
+        :   TlsSrpIdentityManager
+    {
+        private static readonly byte[] PREFIX_PASSWORD = Strings.ToByteArray("password");
+        private static readonly byte[] PREFIX_SALT = Strings.ToByteArray("salt");
+
+        /**
+         * Create a {@link SimulatedTlsSRPIdentityManager} that implements the algorithm from RFC 5054 2.5.1.3
+         *
+         * @param group the {@link SRP6GroupParameters} defining the group that SRP is operating in
+         * @param seedKey the secret "seed key" referred to in RFC 5054 2.5.1.3
+         * @return an instance of {@link SimulatedTlsSRPIdentityManager}
+         */
+        public static SimulatedTlsSrpIdentityManager GetRfc5054Default(Srp6GroupParameters group, byte[] seedKey)
+        {
+            Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator();
+            verifierGenerator.Init(group, TlsUtilities.CreateHash(HashAlgorithm.sha1));
+
+            HMac mac = new HMac(TlsUtilities.CreateHash(HashAlgorithm.sha1));
+            mac.Init(new KeyParameter(seedKey));
+
+            return new SimulatedTlsSrpIdentityManager(group, verifierGenerator, mac);
+        }
+
+        protected readonly Srp6GroupParameters mGroup;
+        protected readonly Srp6VerifierGenerator mVerifierGenerator;
+        protected readonly IMac mMac;
+
+        public SimulatedTlsSrpIdentityManager(Srp6GroupParameters group, Srp6VerifierGenerator verifierGenerator, IMac mac)
+        {
+            this.mGroup = group;
+            this.mVerifierGenerator = verifierGenerator;
+            this.mMac = mac;
+        }
+
+        public virtual TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+        {
+            mMac.BlockUpdate(PREFIX_SALT, 0, PREFIX_SALT.Length);
+            mMac.BlockUpdate(identity, 0, identity.Length);
+
+            byte[] salt = new byte[mMac.GetMacSize()];
+            mMac.DoFinal(salt, 0);
+
+            mMac.BlockUpdate(PREFIX_PASSWORD, 0, PREFIX_PASSWORD.Length);
+            mMac.BlockUpdate(identity, 0, identity.Length);
+
+            byte[] password = new byte[mMac.GetMacSize()];
+            mMac.DoFinal(password, 0);
+
+            BigInteger verifier = mVerifierGenerator.GenerateVerifier(salt, identity, password);
+
+            return new TlsSrpLoginParameters(mGroup, verifier, salt);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/SrpTlsClient.cs b/crypto/src/crypto/tls/SrpTlsClient.cs
index 5d82ed470..bed6a8f0c 100644
--- a/crypto/src/crypto/tls/SrpTlsClient.cs
+++ b/crypto/src/crypto/tls/SrpTlsClient.cs
@@ -6,20 +6,29 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    public abstract class SrpTlsClient
+    public class SrpTlsClient
         :   AbstractTlsClient
     {
+        protected TlsSrpGroupVerifier mGroupVerifier;
+
         protected byte[] mIdentity;
         protected byte[] mPassword;
 
         public SrpTlsClient(byte[] identity, byte[] password)
-            : this(new DefaultTlsCipherFactory(), identity, password)
+            :   this(new DefaultTlsCipherFactory(), new DefaultTlsSrpGroupVerifier(), identity, password)
         {
         }
 
         public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+            :   this(cipherFactory, new DefaultTlsSrpGroupVerifier(), identity, password)
+        {
+        }
+
+        public SrpTlsClient(TlsCipherFactory cipherFactory, TlsSrpGroupVerifier groupVerifier,
+            byte[] identity, byte[] password)
             :   base(cipherFactory)
         {
+            this.mGroupVerifier = groupVerifier;
             this.mIdentity = Arrays.Clone(identity);
             this.mPassword = Arrays.Clone(password);
         }
@@ -86,6 +95,15 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public override TlsAuthentication GetAuthentication()
+        {
+            /*
+             * Note: This method is not called unless a server certificate is sent, which may be the
+             * case e.g. for SRP_DSS or SRP_RSA key exchange.
+             */
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
         public override TlsCipher GetCipher()
         {
             switch (mSelectedCipherSuite)
@@ -117,7 +135,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
         {
-            return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mIdentity, mPassword);
+            return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mGroupVerifier, mIdentity, mPassword);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/SrpTlsServer.cs b/crypto/src/crypto/tls/SrpTlsServer.cs
new file mode 100644
index 000000000..1bd6b3af4
--- /dev/null
+++ b/crypto/src/crypto/tls/SrpTlsServer.cs
@@ -0,0 +1,162 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class SrpTlsServer
+        :   AbstractTlsServer
+    {
+        protected TlsSrpIdentityManager mSrpIdentityManager;
+
+        protected byte[] mSrpIdentity = null;
+        protected TlsSrpLoginParameters mLoginParameters = null;
+
+        public SrpTlsServer(TlsSrpIdentityManager srpIdentityManager)
+            :   this(new DefaultTlsCipherFactory(), srpIdentityManager)
+        {
+        }
+
+        public SrpTlsServer(TlsCipherFactory cipherFactory, TlsSrpIdentityManager srpIdentityManager)
+            :   base(cipherFactory)
+        {
+            this.mSrpIdentityManager = srpIdentityManager;
+        }
+
+        protected virtual TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        protected virtual TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return new int[]
+            {
+                CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
+                CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
+                CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
+                CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
+                CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA
+            };
+        }
+
+        public override void ProcessClientExtensions(IDictionary clientExtensions)
+        {
+            base.ProcessClientExtensions(clientExtensions);
+
+            this.mSrpIdentity = TlsSrpUtilities.GetSrpExtension(clientExtensions);
+        }
+
+        public override int GetSelectedCipherSuite()
+        {
+            int cipherSuite = base.GetSelectedCipherSuite();
+
+            if (TlsSrpUtilities.IsSrpCipherSuite(cipherSuite))
+            {
+                if (mSrpIdentity != null)
+                {
+                    this.mLoginParameters = mSrpIdentityManager.GetLoginParameters(mSrpIdentity);
+                }
+
+                if (mLoginParameters == null)
+                    throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
+            }
+
+            return cipherSuite;
+        }
+
+        public override TlsCredentials GetCredentials()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+                return null;
+
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return GetDsaSignerCredentials();
+
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+                return GetRsaSignerCredentials();
+
+            default:
+                /* Note: internal error here; selected a key exchange we don't implement! */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public override TlsKeyExchange GetKeyExchange()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public override TlsCipher GetCipher()
+        {
+            switch (mSelectedCipherSuite)
+            {
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
+
+            default:
+                /*
+                 * Note: internal error here; the TlsProtocol implementation verifies that the
+                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
+                 * we now can't produce an implementation, we shouldn't have offered it!
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        protected virtual TlsKeyExchange CreateSrpKeyExchange(int keyExchange)
+        {
+            return new TlsSrpKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mSrpIdentity, mLoginParameters);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsClientProtocol.cs b/crypto/src/crypto/tls/TlsClientProtocol.cs
index 0c35e62af..1bd0653a6 100644
--- a/crypto/src/crypto/tls/TlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -373,21 +373,17 @@ namespace Org.BouncyCastle.Crypto.Tls
                         /*
                          * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                          */
-                        SignatureAndHashAlgorithm signatureAndHashAlgorithm;
-                        byte[] hash;
+                        SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
+                            Context, signerCredentials);
 
-                        if (TlsUtilities.IsTlsV12(Context))
+                        byte[] hash;
+                        if (signatureAndHashAlgorithm == null)
                         {
-                            signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
-                            if (signatureAndHashAlgorithm == null)
-                                throw new TlsFatalAlert(AlertDescription.internal_error);
-
-                            hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
+                            hash = mSecurityParameters.SessionHash;
                         }
                         else
                         {
-                            signatureAndHashAlgorithm = null;
-                            hash = mSecurityParameters.SessionHash;
+                            hash = prepareFinishHash.GetFinalHash(signatureAndHashAlgorithm.Hash);
                         }
 
                         byte[] signature = signerCredentials.GenerateCertificateSignature(hash);
diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
index b831249a6..211249fcc 100644
--- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -4,6 +4,7 @@ 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
@@ -16,12 +17,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected DHParameters mDHParameters;
 
         protected AsymmetricKeyParameter mServerPublicKey;
-        protected DHPublicKeyParameters mDHAgreeServerPublicKey;
         protected TlsAgreementCredentials mAgreementCredentials;
-        protected DHPrivateKeyParameters mDHAgreeClientPrivateKey;
 
-        protected DHPrivateKeyParameters mDHAgreeServerPrivateKey;
-        protected DHPublicKeyParameters mDHAgreeClientPublicKey;
+        protected DHPrivateKeyParameters mDHAgreePrivateKey;
+        protected DHPublicKeyParameters mDHAgreePublicKey;
 
         public TlsDHKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, DHParameters dhParameters)
             :   base(keyExchange, supportedSignatureAlgorithms)
@@ -81,7 +80,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             {
                 try
                 {
-                    this.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey);
+                    this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey((DHPublicKeyParameters)this.mServerPublicKey);
                 }
                 catch (InvalidCastException e)
                 {
@@ -165,26 +164,40 @@ namespace Org.BouncyCastle.Crypto.Tls
              */
             if (mAgreementCredentials == null)
             {
-                this.mDHAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
-                    mDHAgreeServerPublicKey.Parameters, output);
+                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom,
+                    mDHParameters, output);
             }
         }
 
-        public override byte[] GeneratePremasterSecret()
+        public override void ProcessClientCertificate(Certificate clientCertificate)
         {
-            if (mAgreementCredentials != null)
+            // TODO Extract the public key
+            // TODO If the certificate is 'fixed', take the public key as dhAgreeClientPublicKey
+        }
+
+        public override void ProcessClientKeyExchange(Stream input)
+        {
+            if (mDHAgreePublicKey != null)
             {
-                return mAgreementCredentials.GenerateAgreement(mDHAgreeServerPublicKey);
+                // For dss_fixed_dh and rsa_fixed_dh, the key arrived in the client certificate
+                return;
             }
 
-            if (mDHAgreeServerPrivateKey != null)
+            BigInteger Yc = TlsDHUtilities.ReadDHParameter(input);
+
+            this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(new DHPublicKeyParameters(Yc, mDHParameters));
+        }
+
+        public override byte[] GeneratePremasterSecret()
+        {
+            if (mAgreementCredentials != null)
             {
-                return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeClientPublicKey, mDHAgreeServerPrivateKey);
+                return mAgreementCredentials.GenerateAgreement(mDHAgreePublicKey);
             }
 
-            if (mDHAgreeClientPrivateKey != null)
+            if (mDHAgreePrivateKey != null)
             {
-                return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreeServerPublicKey, mDHAgreeClientPrivateKey);
+                return TlsDHUtilities.CalculateDHBasicAgreement(mDHAgreePublicKey, mDHAgreePrivateKey);
             }
 
             throw new TlsFatalAlert(AlertDescription.internal_error);
diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index 3c05bb6f0..419d4e442 100644
--- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -36,30 +36,18 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             DigestInputBuffer buf = new DigestInputBuffer();
 
-            this.mDHAgreeServerPrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+            this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom,
                 this.mDHParameters, buf);
 
             /*
              * 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);
-
-                d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
-            }
-            else
-            {
-                signatureAndHashAlgorithm = null;
-                d = new CombinedHash();
-            }
-
-            SecurityParameters securityParameters = context.SecurityParameters;
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
+                mContext, mServerCredentials);
+
+            IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm);
+
+            SecurityParameters securityParameters = mContext.SecurityParameters;
             d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
             d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
             buf.UpdateDigest(d);
@@ -76,21 +64,22 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override void ProcessServerKeyExchange(Stream input)
         {
-            SecurityParameters securityParameters = context.SecurityParameters;
+            SecurityParameters securityParameters = mContext.SecurityParameters;
 
             SignerInputBuffer buf = new SignerInputBuffer();
             Stream teeIn = new TeeInputStream(input, buf);
 
             ServerDHParams dhParams = ServerDHParams.Parse(teeIn);
 
-            DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+            DigitallySigned signed_params = DigitallySigned.Parse(mContext, 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.mDHAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey);
+            this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(dhParams.PublicKey);
+            this.mDHParameters = mDHAgreePublicKey.Parameters;
         }
 
         protected virtual ISigner InitVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm,
diff --git a/crypto/src/crypto/tls/TlsDsaSigner.cs b/crypto/src/crypto/tls/TlsDsaSigner.cs
index a5ac55974..f0c1e9451 100644
--- a/crypto/src/crypto/tls/TlsDsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -64,8 +64,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext))
                 throw new InvalidOperationException();
 
-            // TODO For TLS 1.2+, lift the SHA-1 restriction here
-            if (algorithm != null && (algorithm.Hash != HashAlgorithm.sha1 || algorithm.Signature != SignatureAlgorithm))
+            if (algorithm != null && algorithm.Signature != SignatureAlgorithm)
                 throw new InvalidOperationException();
 
             byte hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.Hash;
diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
index 42dc2f2ef..992be4aca 100644
--- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -166,7 +166,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             if (mAgreementCredentials == null)
             {
-                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom,
                     mServerECPointFormats, mECAgreePublicKey.Parameters, output);
             }
         }
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index b99db0c18..b681aada3 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -36,30 +36,18 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             DigestInputBuffer buf = new DigestInputBuffer();
 
-            this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom, mNamedCurves,
+            this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom, mNamedCurves,
                 mClientECPointFormats, buf);
 
             /*
              * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
              */
-            SignatureAndHashAlgorithm signatureAndHashAlgorithm;
-            IDigest d;
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
+                mContext, mServerCredentials);
 
-            if (TlsUtilities.IsTlsV12(context))
-            {
-                signatureAndHashAlgorithm = mServerCredentials.SignatureAndHashAlgorithm;
-                if (signatureAndHashAlgorithm == null)
-                    throw new TlsFatalAlert(AlertDescription.internal_error);
-
-                d = TlsUtilities.CreateHash(signatureAndHashAlgorithm.Hash);
-            }
-            else
-            {
-                signatureAndHashAlgorithm = null;
-                d = new CombinedHash();
-            }
+            IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm);
 
-            SecurityParameters securityParameters = context.SecurityParameters;
+            SecurityParameters securityParameters = mContext.SecurityParameters;
             d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
             d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
             buf.UpdateDigest(d);
@@ -76,7 +64,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override void ProcessServerKeyExchange(Stream input)
         {
-            SecurityParameters securityParameters = context.SecurityParameters;
+            SecurityParameters securityParameters = mContext.SecurityParameters;
 
             SignerInputBuffer buf = new SignerInputBuffer();
             Stream teeIn = new TeeInputStream(input, buf);
@@ -85,7 +73,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             byte[] point = TlsUtilities.ReadOpaque8(teeIn);
 
-            DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+            DigitallySigned signed_params = DigitallySigned.Parse(mContext, input);
 
             ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
             buf.UpdateSigner(signer);
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index 1a6e7a3bc..4ea72cd57 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -168,6 +168,8 @@ namespace Org.BouncyCastle.Crypto.Tls
                             .SetCompressionAlgorithm(this.mSecurityParameters.compressionAlgorithm)
                             .SetMasterSecret(this.mSecurityParameters.masterSecret)
                             .SetPeerCertificate(this.mPeerCertificate)
+                            .SetPskIdentity(this.mSecurityParameters.pskIdentity)
+                            .SetSrpIdentity(this.mSecurityParameters.srpIdentity)
                             // TODO Consider filtering extensions that aren't relevant to resumed sessions
                             .SetServerExtensions(this.mServerExtensions)
                             .Build();
diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index a8d0867ef..0af7f7a69 100644
--- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -99,12 +99,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                 if (this.mDHParameters == null)
                     throw new TlsFatalAlert(AlertDescription.internal_error);
 
-                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom,
                     this.mDHParameters, buf);
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(context.SecureRandom,
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralServerKeyExchange(mContext.SecureRandom,
                     mNamedCurves, mClientECPointFormats, buf);
             }
 
@@ -165,6 +165,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 ServerDHParams serverDHParams = ServerDHParams.Parse(input);
 
                 this.mDHAgreePublicKey = TlsDHUtilities.ValidateDHPublicKey(serverDHParams.PublicKey);
+                this.mDHParameters = mDHAgreePublicKey.Parameters;
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
@@ -208,21 +209,21 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             TlsUtilities.WriteOpaque16(psk_identity, output);
 
-            context.SecurityParameters.pskIdentity = psk_identity;
+            mContext.SecurityParameters.pskIdentity = psk_identity;
 
             if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
-                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
-                    mDHAgreePublicKey.Parameters, output);
+                this.mDHAgreePrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom,
+                    mDHParameters, output);
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
             {
-                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(context.SecureRandom,
+                this.mECAgreePrivateKey = TlsEccUtilities.GenerateEphemeralClientKeyExchange(mContext.SecureRandom,
                     mServerECPointFormats, mECAgreePublicKey.Parameters, output);
             }
             else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
             {
-                this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context,
+                this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(mContext,
                     this.mRsaServerPublicKey, output);
             }
         }
@@ -235,7 +236,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             if (mPsk == null)
                 throw new TlsFatalAlert(AlertDescription.unknown_psk_identity);
 
-            context.SecurityParameters.pskIdentity = psk_identity;
+            mContext.SecurityParameters.pskIdentity = psk_identity;
 
             if (this.mKeyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
@@ -255,7 +256,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             else if (this.mKeyExchange == KeyExchangeAlgorithm.RSA_PSK)
             {
                 byte[] encryptedPreMasterSecret;
-                if (TlsUtilities.IsSsl(context))
+                if (TlsUtilities.IsSsl(mContext))
                 {
                     // TODO Do any SSLv3 clients actually include the length?
                     encryptedPreMasterSecret = Streams.ReadAll(input);
diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index 3a0a49154..b02d56486 100644
--- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -16,13 +16,13 @@ namespace Org.BouncyCastle.Crypto.Tls
     public class TlsRsaKeyExchange
         :   AbstractTlsKeyExchange
     {
-        protected AsymmetricKeyParameter serverPublicKey = null;
+        protected AsymmetricKeyParameter mServerPublicKey = null;
 
-        protected RsaKeyParameters rsaServerPublicKey = null;
+        protected RsaKeyParameters mRsaServerPublicKey = null;
 
-        protected TlsEncryptionCredentials serverCredentials = null;
+        protected TlsEncryptionCredentials mServerCredentials = null;
 
-        protected byte[] premasterSecret;
+        protected byte[] mPremasterSecret;
 
         public TlsRsaKeyExchange(IList supportedSignatureAlgorithms)
             :   base(KeyExchangeAlgorithm.RSA, supportedSignatureAlgorithms)
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             ProcessServerCertificate(serverCredentials.Certificate);
 
-            this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+            this.mServerCredentials = (TlsEncryptionCredentials)serverCredentials;
         }
 
         public override void ProcessServerCertificate(Certificate serverCertificate)
@@ -54,7 +54,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
             try
             {
-                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+                this.mServerPublicKey = PublicKeyFactory.CreateKey(keyInfo);
             }
             catch (Exception e)
             {
@@ -62,10 +62,10 @@ 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);
 
@@ -97,13 +97,13 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override void GenerateClientKeyExchange(Stream output)
         {
-            this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, rsaServerPublicKey, output);
+            this.mPremasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(mContext, mRsaServerPublicKey, output);
         }
 
         public override void ProcessClientKeyExchange(Stream input)
         {
             byte[] encryptedPreMasterSecret;
-            if (TlsUtilities.IsSsl(context))
+            if (TlsUtilities.IsSsl(mContext))
             {
                 // TODO Do any SSLv3 clients actually include the length?
                 encryptedPreMasterSecret = Streams.ReadAll(input);
@@ -113,16 +113,16 @@ namespace Org.BouncyCastle.Crypto.Tls
                 encryptedPreMasterSecret = TlsUtilities.ReadOpaque16(input);
             }
 
-            this.premasterSecret = serverCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
+            this.mPremasterSecret = mServerCredentials.DecryptPreMasterSecret(encryptedPreMasterSecret);
         }
 
         public override byte[] GeneratePremasterSecret()
         {
-            if (this.premasterSecret == null)
+            if (this.mPremasterSecret == null)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
 
-            byte[] tmp = this.premasterSecret;
-            this.premasterSecret = null;
+            byte[] tmp = this.mPremasterSecret;
+            this.mPremasterSecret = null;
             return tmp;
         }
 
diff --git a/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs b/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs
new file mode 100644
index 000000000..185f2f50a
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpGroupVerifier.cs
@@ -0,0 +1,17 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsSrpGroupVerifier
+    {
+        /**
+         * Check whether the given SRP group parameters are acceptable for use.
+         * 
+         * @param group the {@link SRP6GroupParameters} to check
+         * @return true if (and only if) the specified group parameters are acceptable
+         */
+        bool Accept(Srp6GroupParameters group);
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSrpIdentityManager.cs b/crypto/src/crypto/tls/TlsSrpIdentityManager.cs
new file mode 100644
index 000000000..080a0dc16
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpIdentityManager.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsSrpIdentityManager
+    {
+        /**
+         * Lookup the {@link TlsSRPLoginParameters} corresponding to the specified identity.
+         * 
+         * NOTE: To avoid "identity probing", unknown identities SHOULD be handled as recommended in RFC
+         * 5054 2.5.1.3. {@link SimulatedTlsSRPIdentityManager} is provided for this purpose.
+         * 
+         * @param identity
+         *            the SRP identity sent by the connecting client
+         * @return the {@link TlsSRPLoginParameters} for the specified identity, or else 'simulated'
+         *         parameters if the identity is not recognized. A null value is also allowed, but not
+         *         recommended.
+         */
+        TlsSrpLoginParameters GetLoginParameters(byte[] identity);
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index f42f7456d..ce8e4834a 100644
--- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -16,36 +16,64 @@ namespace Org.BouncyCastle.Crypto.Tls
     public class TlsSrpKeyExchange
         :   AbstractTlsKeyExchange
     {
+        protected static TlsSigner CreateSigner(int keyExchange)
+        {
+            switch (keyExchange)
+            {
+                case KeyExchangeAlgorithm.SRP:
+                    return null;
+                case KeyExchangeAlgorithm.SRP_RSA:
+                    return new TlsRsaSigner();
+                case KeyExchangeAlgorithm.SRP_DSS:
+                    return new TlsDssSigner();
+                default:
+                    throw new ArgumentException("unsupported key exchange algorithm");
+            }
+        }
+
         protected TlsSigner mTlsSigner;
+        protected TlsSrpGroupVerifier mGroupVerifier;
         protected byte[] mIdentity;
         protected byte[] mPassword;
 
         protected AsymmetricKeyParameter mServerPublicKey = null;
 
-        protected byte[] mS = null;
-        protected BigInteger mB = null;
-        protected Srp6Client mSrpClient = new Srp6Client();
+        protected Srp6GroupParameters mSrpGroup = null;
+        protected Srp6Client mSrpClient = null;
+        protected Srp6Server mSrpServer = null;
+        protected BigInteger mSrpPeerCredentials = null;
+        protected BigInteger mSrpVerifier = null;
+        protected byte[] mSrpSalt = null;
+
+        protected TlsSignerCredentials mServerCredentials = null;
 
+        [Obsolete("Use constructor taking an explicit 'groupVerifier' argument")]
         public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity, byte[] password)
-            :   base(keyExchange, supportedSignatureAlgorithms)
+            :   this(keyExchange, supportedSignatureAlgorithms, new DefaultTlsSrpGroupVerifier(), identity, password)
         {
-            switch (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");
-            }
+        }
 
+        public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, TlsSrpGroupVerifier groupVerifier,
+            byte[] identity, byte[] password)
+            :   base(keyExchange, supportedSignatureAlgorithms)
+        {
+            this.mTlsSigner = CreateSigner(keyExchange);
+            this.mGroupVerifier = groupVerifier;
             this.mIdentity = identity;
             this.mPassword = password;
+            this.mSrpClient = new Srp6Client();
+        }
+
+        public TlsSrpKeyExchange(int keyExchange, IList supportedSignatureAlgorithms, byte[] identity,
+            TlsSrpLoginParameters loginParameters)
+            :   base(keyExchange, supportedSignatureAlgorithms)
+        {
+            this.mTlsSigner = CreateSigner(keyExchange);
+            this.mIdentity = identity;
+            this.mSrpServer = new Srp6Server();
+            this.mSrpGroup = loginParameters.Group;
+            this.mSrpVerifier = loginParameters.Verifier;
+            this.mSrpSalt = loginParameters.Salt;
         }
 
         public override void Init(TlsContext context)
@@ -91,14 +119,62 @@ namespace Org.BouncyCastle.Crypto.Tls
             base.ProcessServerCertificate(serverCertificate);
         }
 
+        public override void ProcessServerCredentials(TlsCredentials serverCredentials)
+        {
+            if ((mKeyExchange == KeyExchangeAlgorithm.SRP) || !(serverCredentials is TlsSignerCredentials))
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            ProcessServerCertificate(serverCredentials.Certificate);
+
+            this.mServerCredentials = (TlsSignerCredentials)serverCredentials;
+        }
+
         public override bool RequiresServerKeyExchange
         {
             get { return true; }
         }
 
+        public override byte[] GenerateServerKeyExchange()
+        {
+            mSrpServer.Init(mSrpGroup, mSrpVerifier, TlsUtilities.CreateHash(HashAlgorithm.sha1), mContext.SecureRandom);
+            BigInteger B = mSrpServer.GenerateServerCredentials();
+
+            ServerSrpParams srpParams = new ServerSrpParams(mSrpGroup.N, mSrpGroup.G, mSrpSalt, B);
+
+            DigestInputBuffer buf = new DigestInputBuffer();
+
+            srpParams.Encode(buf);
+
+            if (mServerCredentials != null)
+            {
+                /*
+                 * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+                 */
+                SignatureAndHashAlgorithm signatureAndHashAlgorithm = TlsUtilities.GetSignatureAndHashAlgorithm(
+                    mContext, mServerCredentials);
+
+                IDigest d = TlsUtilities.CreateHash(signatureAndHashAlgorithm);
+
+                SecurityParameters securityParameters = mContext.SecurityParameters;
+                d.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+                d.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+                buf.UpdateDigest(d);
+
+                byte[] hash = new byte[d.GetDigestSize()];
+                d.DoFinal(hash, 0);
+
+                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;
+            SecurityParameters securityParameters = mContext.SecurityParameters;
 
             SignerInputBuffer buf = null;
             Stream teeIn = input;
@@ -109,14 +185,11 @@ namespace Org.BouncyCastle.Crypto.Tls
                 teeIn = new TeeInputStream(input, buf);
             }
 
-            byte[] NBytes = TlsUtilities.ReadOpaque16(teeIn);
-            byte[] gBytes = TlsUtilities.ReadOpaque16(teeIn);
-            byte[] sBytes = TlsUtilities.ReadOpaque8(teeIn);
-            byte[] BBytes = TlsUtilities.ReadOpaque16(teeIn);
+            ServerSrpParams srpParams = ServerSrpParams.Parse(teeIn);
 
             if (buf != null)
             {
-                DigitallySigned signed_params = DigitallySigned.Parse(context, input);
+                DigitallySigned signed_params = DigitallySigned.Parse(mContext, input);
 
                 ISigner signer = InitVerifyer(mTlsSigner, signed_params.Algorithm, securityParameters);
                 buf.UpdateSigner(signer);
@@ -124,13 +197,12 @@ namespace Org.BouncyCastle.Crypto.Tls
                     throw new TlsFatalAlert(AlertDescription.decrypt_error);
             }
 
-            BigInteger N = new BigInteger(1, NBytes);
-            BigInteger g = new BigInteger(1, gBytes);
+            this.mSrpGroup = new Srp6GroupParameters(srpParams.N, srpParams.G);
 
-            // TODO Validate group parameters (see RFC 5054)
-    //        throw new TlsFatalAlert(AlertDescription.insufficient_security);
+            if (!mGroupVerifier.Accept(mSrpGroup))
+                throw new TlsFatalAlert(AlertDescription.insufficient_security);
 
-            this.mS = sBytes;
+            this.mSrpSalt = srpParams.S;
 
             /*
              * RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter" alert if
@@ -138,14 +210,14 @@ namespace Org.BouncyCastle.Crypto.Tls
              */
             try
             {
-                this.mB = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
+                this.mSrpPeerCredentials = Srp6Utilities.ValidatePublicValue(mSrpGroup.N, srpParams.B);
             }
             catch (CryptoException e)
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
             }
 
-            this.mSrpClient.Init(N, g, TlsUtilities.CreateHash(HashAlgorithm.sha1), context.SecureRandom);
+            this.mSrpClient.Init(mSrpGroup, TlsUtilities.CreateHash(HashAlgorithm.sha1), mContext.SecureRandom);
         }
 
         public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
@@ -160,16 +232,40 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override void GenerateClientKeyExchange(Stream output)
         {
-            BigInteger A = mSrpClient.GenerateClientCredentials(mS, this.mIdentity, this.mPassword);
-            TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(A), output);
+            BigInteger A = mSrpClient.GenerateClientCredentials(mSrpSalt, mIdentity, mPassword);
+            TlsSrpUtilities.WriteSrpParameter(A, output);
+
+            mContext.SecurityParameters.srpIdentity = Arrays.Clone(mIdentity);
+        }
+
+        public override void ProcessClientKeyExchange(Stream input)
+        {
+            /*
+             * RFC 5054 2.5.4: The server MUST abort the handshake with an "illegal_parameter" alert if
+             * A % N = 0.
+             */
+            try
+            {
+                this.mSrpPeerCredentials = Srp6Utilities.ValidatePublicValue(mSrpGroup.N, TlsSrpUtilities.ReadSrpParameter(input));
+            }
+            catch (CryptoException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter, e);
+            }
+
+            mContext.SecurityParameters.srpIdentity = Arrays.Clone(mIdentity);
         }
 
         public override byte[] GeneratePremasterSecret()
         {
             try
             {
+                BigInteger S = mSrpServer != null
+                    ?   mSrpServer.CalculateSecret(mSrpPeerCredentials)
+                    :   mSrpClient.CalculateSecret(mSrpPeerCredentials);
+
                 // TODO Check if this needs to be a fixed size
-                return BigIntegers.AsUnsignedByteArray(mSrpClient.CalculateSecret(mB));
+                return BigIntegers.AsUnsignedByteArray(S);
             }
             catch (CryptoException e)
             {
diff --git a/crypto/src/crypto/tls/TlsSrpLoginParameters.cs b/crypto/src/crypto/tls/TlsSrpLoginParameters.cs
new file mode 100644
index 000000000..5ae4641f6
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsSrpLoginParameters.cs
@@ -0,0 +1,36 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class TlsSrpLoginParameters
+    {
+        protected readonly Srp6GroupParameters mGroup;
+        protected readonly BigInteger mVerifier;
+        protected readonly byte[] mSalt;
+
+        public TlsSrpLoginParameters(Srp6GroupParameters group, BigInteger verifier, byte[] salt)
+        {
+            this.mGroup = group;
+            this.mVerifier = verifier;
+            this.mSalt = salt;
+        }
+
+        public virtual Srp6GroupParameters Group
+        {
+            get { return mGroup; }
+        }
+
+        public virtual byte[] Salt
+        {
+            get { return mSalt; }
+        }
+
+        public virtual BigInteger Verifier
+        {
+            get { return mVerifier; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSrpUtilities.cs b/crypto/src/crypto/tls/TlsSrpUtilities.cs
index bbb6ac280..873189dc6 100644
--- a/crypto/src/crypto/tls/TlsSrpUtilities.cs
+++ b/crypto/src/crypto/tls/TlsSrpUtilities.cs
@@ -2,6 +2,9 @@
 using System.Collections;
 using System.IO;
 
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public abstract class TlsSrpUtilities
@@ -37,5 +40,35 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             return identity;
         }
+
+        public static BigInteger ReadSrpParameter(Stream input)
+        {
+            return new BigInteger(1, TlsUtilities.ReadOpaque16(input));
+        }
+
+        public static void WriteSrpParameter(BigInteger x, Stream output)
+        {
+            TlsUtilities.WriteOpaque16(BigIntegers.AsUnsignedByteArray(x), output);
+        }
+
+        public static bool IsSrpCipherSuite(int cipherSuite)
+        {
+            switch (cipherSuite)
+            {
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+                return true;
+
+            default:
+                return false;
+            }
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 485ecb760..be91e6c4b 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -21,6 +21,9 @@ namespace Org.BouncyCastle.Crypto.Tls
     public abstract class TlsUtilities
     {
         public static readonly byte[] EmptyBytes = new byte[0];
+        public static readonly short[] EmptyShorts = new short[0];
+        public static readonly int[] EmptyInts = new int[0];
+        public static readonly long[] EmptyLongs = new long[0];
 
         public static void CheckUint8(int i)
         {
@@ -589,6 +592,37 @@ namespace Org.BouncyCastle.Crypto.Tls
             return extensions == null ? null : (byte[])extensions[extensionType];
         }
 
+        public static IList GetDefaultSupportedSignatureAlgorithms()
+        {
+            byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha1, HashAlgorithm.sha224, HashAlgorithm.sha256,
+                HashAlgorithm.sha384, HashAlgorithm.sha512 };
+            byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa, SignatureAlgorithm.dsa,
+                SignatureAlgorithm.ecdsa };
+
+            IList result = Platform.CreateArrayList();
+            for (int i = 0; i < signatureAlgorithms.Length; ++i)
+            {
+                for (int j = 0; j < hashAlgorithms.Length; ++j)
+                {
+                    result.Add(new SignatureAndHashAlgorithm(hashAlgorithms[j], signatureAlgorithms[i]));
+                }
+            }
+            return result;
+        }
+
+        public static SignatureAndHashAlgorithm GetSignatureAndHashAlgorithm(TlsContext context,
+            TlsSignerCredentials signerCredentials)
+        {
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+            if (IsTlsV12(context))
+            {
+                signatureAndHashAlgorithm = signerCredentials.SignatureAndHashAlgorithm;
+                if (signatureAndHashAlgorithm == null)
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+            return signatureAndHashAlgorithm;
+        }
+
         public static bool HasExpectedEmptyExtensionData(IDictionary extensions, int extensionType,
             byte alertDescription)
         {
@@ -941,6 +975,13 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public static IDigest CreateHash(SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+        {
+            return signatureAndHashAlgorithm == null
+                ?   new CombinedHash()
+                :   CreateHash(signatureAndHashAlgorithm.Hash);
+        }
+
         public static IDigest CloneHash(byte hashAlgorithm, IDigest hash)
         {
             switch (hashAlgorithm)
diff --git a/crypto/test/src/crypto/test/SRP6Test.cs b/crypto/test/src/crypto/test/SRP6Test.cs
index 3b80e2c16..6b64df924 100644
--- a/crypto/test/src/crypto/test/SRP6Test.cs
+++ b/crypto/test/src/crypto/test/SRP6Test.cs
@@ -23,15 +23,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        return new BigInteger(1, Hex.Decode(hex));
 	    }
 
-		// 1024 bit example prime from RFC5054 and corresponding generator
-		private static readonly BigInteger N_1024 = FromHex("EEAF0AB9ADB38DD69C33F80AFA8FC5E86072618775FF3C0B9EA2314C"
-	            + "9C256576D674DF7496EA81D3383B4813D692C6E0E0D5D8E250B98BE4"
-	            + "8E495C1D6089DAD15DC7D7B46154D6B6CE8EF4AD69B15D4982559B29"
-	            + "7BCF1885C529F566660E57EC68EDBC3C05726CC02FD4CBF4976EAA9A"
-	            + "FD5138FE8376435B9FC61D2FC0EB06E3");
-		private static readonly BigInteger g_1024 = BigInteger.Two;
-
-		private readonly SecureRandom random = new SecureRandom();
+        private readonly SecureRandom random = new SecureRandom();
 
 	    public override string Name
 	    {
@@ -42,9 +34,9 @@ namespace Org.BouncyCastle.Crypto.Tests
 	    {
 	    	rfc5054AppendixBTestVectors();
 
-	        testMutualVerification(N_1024, g_1024);
-	        testClientCatchesBadB(N_1024, g_1024);
-	        testServerCatchesBadA(N_1024, g_1024);
+            testMutualVerification(Srp6StandardGroups.rfc5054_1024);
+            testClientCatchesBadB(Srp6StandardGroups.rfc5054_1024);
+            testServerCatchesBadA(Srp6StandardGroups.rfc5054_1024);
 
 			testWithRandomParams(256);
 			testWithRandomParams(384);
@@ -56,8 +48,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 	    	byte[] I = Encoding.UTF8.GetBytes("alice");
 	    	byte[] P = Encoding.UTF8.GetBytes("password123");
 	    	byte[] s = Hex.Decode("BEB25379D1A8581EB5A727673A2441EE");
-	    	BigInteger N = N_1024;
-	    	BigInteger g = g_1024;
+            BigInteger N = Srp6StandardGroups.rfc5054_1024.N;
+            BigInteger g = Srp6StandardGroups.rfc5054_1024.G;
 	    	BigInteger a = FromHex("60975527035CF2AD1989806F0407210BC81EDC04E2762A56AFD529DDDA2D4393");
 	    	BigInteger b = FromHex("E487CB59D31AC550471E81F00F6928E01DDA08E974A004F49E61F5D105284D20");
 
@@ -148,13 +140,10 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        paramGen.Init(bits, 25, random);
 	        DHParameters parameters = paramGen.GenerateParameters();
 
-	        BigInteger g = parameters.G;
-	        BigInteger p = parameters.P;
-
-	        testMutualVerification(p, g);
+            testMutualVerification(new Srp6GroupParameters(parameters.P, parameters.G));
 		}
 
-	    private void testMutualVerification(BigInteger N, BigInteger g)
+        private void testMutualVerification(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -162,16 +151,16 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
-	        gen.Init(N, g, new Sha256Digest());
+	        gen.Init(group, new Sha256Digest());
 	        BigInteger v = gen.GenerateVerifier(s, I, P);
 
 	        Srp6Client client = new Srp6Client();
-	        client.Init(N, g, new Sha256Digest(), random);
+	        client.Init(group, new Sha256Digest(), random);
 
 	        Srp6Server server = new Srp6Server();
-	        server.Init(N, g, v, new Sha256Digest(), random);
+	        server.Init(group, v, new Sha256Digest(), random);
 
-	        BigInteger A = client.GenerateClientCredentials(s, I, P);
+            BigInteger A = client.GenerateClientCredentials(s, I, P);
 	        BigInteger B = server.GenerateServerCredentials();
 
 	        BigInteger clientS = client.CalculateSecret(B);
@@ -183,7 +172,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        }
 	    }
 
-	    private void testClientCatchesBadB(BigInteger N, BigInteger g)
+        private void testClientCatchesBadB(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -191,7 +180,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6Client client = new Srp6Client();
-	        client.Init(N, g, new Sha256Digest(), random);
+	        client.Init(group, new Sha256Digest(), random);
 
 	        client.GenerateClientCredentials(s, I, P);
 
@@ -207,7 +196,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 	        try
 	        {
-	        	client.CalculateSecret(N);
+	        	client.CalculateSecret(group.N);
 	        	Fail("Client failed to detect invalid value for 'B'");
 	        }
 	        catch (CryptoException)
@@ -216,7 +205,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        }
 	    }
 
-	    private void testServerCatchesBadA(BigInteger N, BigInteger g)
+        private void testServerCatchesBadA(Srp6GroupParameters group)
 	    {
 	        byte[] I = Encoding.UTF8.GetBytes("username");
 	        byte[] P = Encoding.UTF8.GetBytes("password");
@@ -224,11 +213,11 @@ namespace Org.BouncyCastle.Crypto.Tests
 	        random.NextBytes(s);
 
 	        Srp6VerifierGenerator gen = new Srp6VerifierGenerator();
-	        gen.Init(N, g, new Sha256Digest());
+	        gen.Init(group, new Sha256Digest());
 	        BigInteger v = gen.GenerateVerifier(s, I, P);
 
 	        Srp6Server server = new Srp6Server();
-	        server.Init(N, g, v, new Sha256Digest(), random);
+	        server.Init(group, v, new Sha256Digest(), random);
 
 	        server.GenerateServerCredentials();
 
@@ -244,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 	        try
 	        {
-	        	server.CalculateSecret(N);
+	        	server.CalculateSecret(group.N);
 	        	Fail("Client failed to detect invalid value for 'A'");
 	        }
 	        catch (CryptoException)
diff --git a/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
new file mode 100644
index 000000000..7225d84ce
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockSrpTlsClient.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockSrpTlsClient
+        :   SrpTlsClient
+    {
+        internal TlsSession mSession;
+
+        internal MockSrpTlsClient(TlsSession session, byte[] identity, byte[] password)
+            :   base(identity, password)
+        {
+            this.mSession = session;
+        }
+
+        public override TlsSession GetSessionToResume()
+        {
+            return this.mSession;
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP client raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            TlsSession newSession = mContext.ResumableSession;
+            if (newSession != null)
+            {
+                byte[] newSessionID = newSession.SessionID;
+                string hex = Hex.ToHexString(newSessionID);
+
+                if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+                {
+                    Console.WriteLine("Resumed session: " + hex);
+                }
+                else
+                {
+                    Console.WriteLine("Established session: " + hex);
+                }
+
+                this.mSession = newSession;
+            }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("TLS-SRP client negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        internal class MyTlsAuthentication
+            : ServerOnlyTlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public override void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("TLS-SRP client received server certificate chain of length " + chain.Length);
+                for (int i = 0; i != chain.Length; i++)
+                {
+                    X509CertificateStructure entry = chain[i];
+                    // TODO Create Fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    Fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+                        + entry.Subject + ")");
+                }
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs b/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs
new file mode 100644
index 000000000..c15f63e0b
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockSrpTlsServer.cs
@@ -0,0 +1,113 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockSrpTlsServer
+        :   SrpTlsServer
+    {
+        internal static readonly Srp6GroupParameters TEST_GROUP = Srp6StandardGroups.rfc5054_1024;
+        internal static readonly byte[] TEST_IDENTITY = Strings.ToUtf8ByteArray("client");
+        internal static readonly byte[] TEST_PASSWORD = Strings.ToUtf8ByteArray("password");
+        internal static readonly byte[] TEST_SALT = Strings.ToUtf8ByteArray("salt");
+        internal static readonly byte[] TEST_SEED_KEY = Strings.ToUtf8ByteArray("seed_key");
+
+        internal MockSrpTlsServer()
+            :   base(new MyIdentityManager())
+        {
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP server raised alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+            if (message != null)
+            {
+                output.WriteLine("> " + message);
+            }
+            if (cause != null)
+            {
+                output.WriteLine(cause);
+            }
+        }
+
+        public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("TLS-SRP server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            byte[] srpIdentity = mContext.SecurityParameters.SrpIdentity;
+            if (srpIdentity != null)
+            {
+                string name = Strings.FromUtf8ByteArray(srpIdentity);
+                Console.WriteLine("TLS-SRP server completed handshake for SRP identity: " + name);
+            }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.TLSv12; }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            Console.WriteLine("TLS-SRP server negotiated " + serverVersion);
+
+            return serverVersion;
+        }
+
+        protected override TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+                "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+
+        internal class MyIdentityManager
+            :   TlsSrpIdentityManager
+        {
+            protected SimulatedTlsSrpIdentityManager unknownIdentityManager = SimulatedTlsSrpIdentityManager.GetRfc5054Default(
+                TEST_GROUP, TEST_SEED_KEY);
+
+            public virtual TlsSrpLoginParameters GetLoginParameters(byte[] identity)
+            {
+                if (Arrays.AreEqual(TEST_IDENTITY, identity))
+                {
+                    Srp6VerifierGenerator verifierGenerator = new Srp6VerifierGenerator();
+                    verifierGenerator.Init(TEST_GROUP, TlsUtilities.CreateHash(HashAlgorithm.sha1));
+
+                    BigInteger verifier = verifierGenerator.GenerateVerifier(TEST_SALT, identity, TEST_PASSWORD);
+
+                    return new TlsSrpLoginParameters(TEST_GROUP, verifier, TEST_SALT);
+                }
+
+                return unknownIdentityManager.GetLoginParameters(identity);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
index 803989f59..fd673c3b9 100644
--- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -133,27 +133,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
                     return null;
 
-                SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
-                IList sigAlgs = certificateRequest.SupportedSignatureAlgorithms;
-                if (sigAlgs != null)
-                {
-                    foreach (SignatureAndHashAlgorithm sigAlg in sigAlgs)
-                    {
-                        if (sigAlg.Signature == SignatureAlgorithm.rsa)
-                        {
-                            signatureAndHashAlgorithm = sigAlg;
-                            break;
-                        }
-                    }
-
-                    if (signatureAndHashAlgorithm == null)
-                    {
-                        return null;
-                    }
-                }
-
-                return TlsTestUtilities.LoadSignerCredentials(mContext, new string[] { "x509-client.pem", "x509-ca.pem" },
-                    "x509-client-key.pem", signatureAndHashAlgorithm);
+                return TlsTestUtilities.LoadSignerCredentials(mContext, certificateRequest.SupportedSignatureAlgorithms,
+                    SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
             }
         };
     }
diff --git a/crypto/test/src/crypto/tls/test/MockTlsServer.cs b/crypto/test/src/crypto/tls/test/MockTlsServer.cs
index 14d6b9839..8fce95d63 100644
--- a/crypto/test/src/crypto/tls/test/MockTlsServer.cs
+++ b/crypto/test/src/crypto/tls/test/MockTlsServer.cs
@@ -61,29 +61,19 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
 
         public override CertificateRequest GetCertificateRequest()
         {
-            IList serverSigAlgs = null;
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
 
+            IList serverSigAlgs = null;
             if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
             {
-                byte[] hashAlgorithms = new byte[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
-                    HashAlgorithm.sha224, HashAlgorithm.sha1 };
-                byte[] signatureAlgorithms = new byte[]{ SignatureAlgorithm.rsa };
-
-                serverSigAlgs = new ArrayList();
-                for (int i = 0; i < hashAlgorithms.Length; ++i)
-                {
-                    for (int j = 0; j < signatureAlgorithms.Length; ++j)
-                    {
-                        serverSigAlgs.Add(new SignatureAndHashAlgorithm(hashAlgorithms[i],
-                            signatureAlgorithms[j]));
-                    }
-                }
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
             }
 
             IList certificateAuthorities = new ArrayList();
             certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
 
-            return new CertificateRequest(new byte[]{ ClientCertificateType.rsa_sign }, serverSigAlgs, certificateAuthorities);
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
         }
 
         public override void NotifyClientCertificate(Certificate clientCertificate)
@@ -101,37 +91,14 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
 
         protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
         {
-            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{"x509-server.pem", "x509-ca.pem"},
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{ "x509-server.pem", "x509-ca.pem" },
                 "x509-server-key.pem");
         }
 
         protected override TlsSignerCredentials GetRsaSignerCredentials()
         {
-            /*
-             * TODO Note that this code fails to provide default value for the client supported
-             * algorithms if it wasn't sent.
-             */
-            SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
-            IList sigAlgs = mSupportedSignatureAlgorithms;
-            if (sigAlgs != null)
-            {
-                foreach (SignatureAndHashAlgorithm sigAlg in sigAlgs)
-                {
-                    if (sigAlg.Signature == SignatureAlgorithm.rsa)
-                    {
-                        signatureAndHashAlgorithm = sigAlg;
-                        break;
-                    }
-                }
-
-                if (signatureAndHashAlgorithm == null)
-                {
-                    return null;
-                }
-            }
-
-            return TlsTestUtilities.LoadSignerCredentials(mContext, new string[]{"x509-server.pem", "x509-ca.pem"},
-                "x509-server-key.pem", signatureAndHashAlgorithm);
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
         }
     }
 }
diff --git a/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs
new file mode 100644
index 000000000..32e126ff2
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs
@@ -0,0 +1,80 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class TlsSrpProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe, secureRandom);
+
+            Server server = new Server(serverProtocol);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            MockSrpTlsClient client = new MockSrpTlsClient(null, MockSrpTlsServer.TEST_IDENTITY, MockSrpTlsServer.TEST_PASSWORD);
+            clientProtocol.Connect(client);
+
+            // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+            int length = 1000;
+
+            byte[] data = new byte[length];
+            secureRandom.NextBytes(data);
+
+            Stream output = clientProtocol.Stream;
+            output.Write(data, 0, data.Length);
+
+            byte[] echo = new byte[data.Length];
+            int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+            Assert.AreEqual(count, data.Length);
+            Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+            output.Close();
+
+            serverThread.Join();
+        }
+
+        internal class Server
+        {
+            private readonly TlsServerProtocol mServerProtocol;
+
+            internal Server(TlsServerProtocol serverProtocol)
+            {
+                this.mServerProtocol = serverProtocol;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockSrpTlsServer server = new MockSrpTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}