summary refs log tree commit diff
path: root/crypto
diff options
context:
space:
mode:
authorJeffrey Stedfast <jeff@xamarin.com>2015-03-14 09:21:16 -0400
committerJeffrey Stedfast <jeff@xamarin.com>2015-03-14 09:21:16 -0400
commit5f446e15a61331939d37d0a62c72384e19b8a806 (patch)
tree6685f0f70604121f59b258d62e3efa63256053a6 /crypto
parentMerge branch 'master' into vs2010 (diff)
parentUpdate TLS for draft-ietf-tls-session-hash-04 (diff)
downloadBouncyCastle.NET-ed25519-5f446e15a61331939d37d0a62c72384e19b8a806.tar.xz
Merge branch 'master' into vs2010
Diffstat (limited to 'crypto')
-rw-r--r--crypto/BouncyCastle.Android.csproj14
-rw-r--r--crypto/BouncyCastle.csproj14
-rw-r--r--crypto/BouncyCastle.iOS.csproj14
-rw-r--r--crypto/crypto.csproj218
-rw-r--r--crypto/src/bcpg/S2k.cs2
-rw-r--r--crypto/src/crypto/BufferedAeadBlockCipher.cs2
-rw-r--r--crypto/src/crypto/BufferedBlockCipher.cs24
-rw-r--r--crypto/src/crypto/Check.cs25
-rw-r--r--crypto/src/crypto/OutputLengthException.cs28
-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/engines/AesEngine.cs25
-rw-r--r--crypto/src/crypto/engines/AesFastEngine.cs25
-rw-r--r--crypto/src/crypto/engines/AesLightEngine.cs25
-rw-r--r--crypto/src/crypto/engines/BlowfishEngine.cs15
-rw-r--r--crypto/src/crypto/engines/CamelliaEngine.cs21
-rw-r--r--crypto/src/crypto/engines/CamelliaLightEngine.cs21
-rw-r--r--crypto/src/crypto/engines/Cast5Engine.cs11
-rw-r--r--crypto/src/crypto/engines/ChaChaEngine.cs3
-rw-r--r--crypto/src/crypto/engines/DesEdeEngine.cs7
-rw-r--r--crypto/src/crypto/engines/DesEdeWrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/DesEngine.cs11
-rw-r--r--crypto/src/crypto/engines/ElGamalEngine.cs10
-rw-r--r--crypto/src/crypto/engines/GOST28147Engine.cs27
-rw-r--r--crypto/src/crypto/engines/HC128Engine.cs19
-rw-r--r--crypto/src/crypto/engines/HC256Engine.cs19
-rw-r--r--crypto/src/crypto/engines/ISAACEngine.cs17
-rw-r--r--crypto/src/crypto/engines/IdeaEngine.cs26
-rw-r--r--crypto/src/crypto/engines/IesEngine.cs18
-rw-r--r--crypto/src/crypto/engines/NaccacheSternEngine.cs16
-rw-r--r--crypto/src/crypto/engines/NoekeonEngine.cs21
-rw-r--r--crypto/src/crypto/engines/NullEngine.cs21
-rw-r--r--crypto/src/crypto/engines/RC2Engine.cs22
-rw-r--r--crypto/src/crypto/engines/RC2WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RC4Engine.cs25
-rw-r--r--crypto/src/crypto/engines/RC532Engine.cs13
-rw-r--r--crypto/src/crypto/engines/RC564Engine.cs13
-rw-r--r--crypto/src/crypto/engines/RC6Engine.cs22
-rw-r--r--crypto/src/crypto/engines/RFC3211WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RFC3394WrapEngine.cs8
-rw-r--r--crypto/src/crypto/engines/RSABlindedEngine.cs10
-rw-r--r--crypto/src/crypto/engines/RSABlindingEngine.cs10
-rw-r--r--crypto/src/crypto/engines/RSACoreEngine.cs12
-rw-r--r--crypto/src/crypto/engines/RijndaelEngine.cs30
-rw-r--r--crypto/src/crypto/engines/RsaEngine.cs10
-rw-r--r--crypto/src/crypto/engines/SEEDEngine.cs21
-rw-r--r--crypto/src/crypto/engines/Salsa20Engine.cs27
-rw-r--r--crypto/src/crypto/engines/SerpentEngine.cs22
-rw-r--r--crypto/src/crypto/engines/SkipjackEngine.cs26
-rw-r--r--crypto/src/crypto/engines/TEAEngine.cs21
-rw-r--r--crypto/src/crypto/engines/ThreefishEngine.cs12
-rw-r--r--crypto/src/crypto/engines/TwofishEngine.cs9
-rw-r--r--crypto/src/crypto/engines/VMPCEngine.cs11
-rw-r--r--crypto/src/crypto/engines/XSalsa20Engine.cs2
-rw-r--r--crypto/src/crypto/engines/XTEAEngine.cs21
-rw-r--r--crypto/src/crypto/generators/RsaKeyPairGenerator.cs177
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs127
-rw-r--r--crypto/src/crypto/modes/CtsBlockCipher.cs2
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs55
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs13
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs5
-rw-r--r--crypto/src/crypto/modes/gcm/GcmUtilities.cs23
-rw-r--r--crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs9
-rw-r--r--crypto/src/crypto/parameters/Srp6GroupParameters.cs27
-rw-r--r--crypto/src/crypto/signers/DsaDigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/ECGOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/ECNRSigner.cs8
-rw-r--r--crypto/src/crypto/signers/GOST3410DigestSigner.cs14
-rw-r--r--crypto/src/crypto/signers/GOST3410Signer.cs8
-rw-r--r--crypto/src/crypto/signers/GenericSigner.cs14
-rw-r--r--crypto/src/crypto/signers/Iso9796d2PssSigner.cs4
-rw-r--r--crypto/src/crypto/signers/Iso9796d2Signer.cs6
-rw-r--r--crypto/src/crypto/signers/PssSigner.cs2
-rw-r--r--crypto/src/crypto/signers/RsaDigestSigner.cs15
-rw-r--r--crypto/src/crypto/signers/X931Signer.cs234
-rw-r--r--crypto/src/crypto/tls/AbstractTlsClient.cs30
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs9
-rw-r--r--crypto/src/crypto/tls/AbstractTlsKeyExchange.cs4
-rw-r--r--crypto/src/crypto/tls/AbstractTlsServer.cs8
-rw-r--r--crypto/src/crypto/tls/DefaultTlsClient.cs408
-rw-r--r--crypto/src/crypto/tls/DefaultTlsServer.cs504
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSrpGroupVerifier.cs70
-rw-r--r--crypto/src/crypto/tls/DtlsClientProtocol.cs52
-rw-r--r--crypto/src/crypto/tls/DtlsReliableHandshake.cs8
-rw-r--r--crypto/src/crypto/tls/DtlsServerProtocol.cs6
-rw-r--r--crypto/src/crypto/tls/ExporterLabel.cs2
-rw-r--r--crypto/src/crypto/tls/ExtensionType.cs2
-rw-r--r--crypto/src/crypto/tls/PskTlsClient.cs216
-rw-r--r--crypto/src/crypto/tls/PskTlsServer.cs310
-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.cs69
-rw-r--r--crypto/src/crypto/tls/SrpTlsServer.cs121
-rw-r--r--crypto/src/crypto/tls/TlsClientProtocol.cs59
-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/TlsEccUtilities.cs2
-rw-r--r--crypto/src/crypto/tls/TlsProtocol.cs34
-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/TlsServerProtocol.cs11
-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.cs563
-rw-r--r--crypto/src/pkix/PkixParameters.cs4
-rw-r--r--crypto/src/security/SignerUtilities.cs20
-rw-r--r--crypto/test/UnitTests.csproj32
-rw-r--r--crypto/test/data/tls/x509-client-dsa.pem32
-rw-r--r--crypto/test/data/tls/x509-client-ecdsa.pem17
-rw-r--r--crypto/test/data/tls/x509-client-key-dsa.pem15
-rw-r--r--crypto/test/data/tls/x509-client-key-ecdsa.pem6
-rw-r--r--crypto/test/data/tls/x509-server-dsa.pem32
-rw-r--r--crypto/test/data/tls/x509-server-ecdsa.pem17
-rw-r--r--crypto/test/data/tls/x509-server-key-dsa.pem15
-rw-r--r--crypto/test/data/tls/x509-server-key-ecdsa.pem6
-rw-r--r--crypto/test/lib/nunit.core.dllbin90112 -> 139264 bytes
-rw-r--r--crypto/test/lib/nunit.core.interfaces.dllbin40960 -> 61440 bytes
-rw-r--r--crypto/test/lib/nunit.framework.dllbin81920 -> 139264 bytes
-rw-r--r--crypto/test/src/asn1/test/AllTests.cs28
-rw-r--r--crypto/test/src/cms/test/AllTests.cs33
-rw-r--r--crypto/test/src/crypto/io/test/AllTests.cs29
-rw-r--r--crypto/test/src/crypto/test/AllTests.cs37
-rw-r--r--crypto/test/src/crypto/test/CCMTest.cs47
-rw-r--r--crypto/test/src/crypto/test/RegressionTest.cs3
-rw-r--r--crypto/test/src/crypto/test/SRP6Test.cs49
-rw-r--r--crypto/test/src/crypto/test/X931SignerTest.cs145
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs102
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestCase.cs153
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestSuite.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs86
-rw-r--r--crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs110
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsClient.cs150
-rw-r--r--crypto/test/src/crypto/tls/test/MockDtlsServer.cs100
-rw-r--r--crypto/test/src/crypto/tls/test/MockPskTlsClient.cs132
-rw-r--r--crypto/test/src/crypto/tls/test/MockPskTlsServer.cs105
-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.cs54
-rw-r--r--crypto/test/src/crypto/tls/test/MockTlsServer.cs49
-rw-r--r--crypto/test/src/crypto/tls/test/NetworkStream.cs101
-rw-r--r--crypto/test/src/crypto/tls/test/PipedStream.cs134
-rw-r--r--crypto/test/src/crypto/tls/test/PskTlsClientTest.cs79
-rw-r--r--crypto/test/src/crypto/tls/test/TlsProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsSrpProtocolTest.cs80
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestCase.cs164
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs262
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestConfig.cs101
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs194
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestSuite.cs119
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestUtilities.cs29
-rw-r--r--crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs84
-rw-r--r--crypto/test/src/math/ec/test/AllTests.cs23
-rw-r--r--crypto/test/src/math/test/AllTests.cs21
-rw-r--r--crypto/test/src/ocsp/test/AllTests.cs29
-rw-r--r--crypto/test/src/openpgp/examples/test/AllTests.cs32
-rw-r--r--crypto/test/src/openssl/test/AllTests.cs37
-rw-r--r--crypto/test/src/tsp/test/AllTests.cs27
-rw-r--r--crypto/test/src/util/io/pem/test/AllTests.cs35
170 files changed, 6462 insertions, 2525 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index db03b75be..8a201b824 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -614,6 +614,7 @@
     <Compile Include="src\crypto\BufferedCipherBase.cs" />
     <Compile Include="src\crypto\BufferedIesCipher.cs" />
     <Compile Include="src\crypto\BufferedStreamCipher.cs" />
+    <Compile Include="src\crypto\Check.cs" />
     <Compile Include="src\crypto\CipherKeyGenerator.cs" />
     <Compile Include="src\crypto\CryptoException.cs" />
     <Compile Include="src\crypto\DataLengthException.cs" />
@@ -635,6 +636,7 @@
     <Compile Include="src\crypto\InvalidCipherTextException.cs" />
     <Compile Include="src\crypto\KeyGenerationParameters.cs" />
     <Compile Include="src\crypto\MaxBytesExceededException.cs" />
+    <Compile Include="src\crypto\OutputLengthException.cs" />
     <Compile Include="src\crypto\PbeParametersGenerator.cs" />
     <Compile Include="src\crypto\StreamBlockCipher.cs" />
     <Compile Include="src\crypto\agreement\DHAgreement.cs" />
@@ -650,6 +652,7 @@
     <Compile Include="src\crypto\agreement\kdf\ECDHKekGenerator.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Client.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Server.cs" />
+    <Compile Include="src\crypto\agreement\srp\SRP6StandardGroups.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Utilities.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6VerifierGenerator.cs" />
     <Compile Include="src\crypto\digests\GOST3411Digest.cs" />
@@ -854,6 +857,7 @@
     <Compile Include="src\crypto\parameters\RsaKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\RsaPrivateCrtKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\SkeinParameters.cs" />
+    <Compile Include="src\crypto\parameters\Srp6GroupParameters.cs" />
     <Compile Include="src\crypto\parameters\TweakableBlockCipherParameters.cs" />
     <Compile Include="src\crypto\prng\CryptoApiRandomGenerator.cs" />
     <Compile Include="src\crypto\prng\DigestRandomGenerator.cs" />
@@ -876,6 +880,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsClient.cs" />
@@ -889,6 +894,7 @@
     <Compile Include="src\crypto\tls\AbstractTlsSignerCredentials.cs" />
     <Compile Include="src\crypto\tls\AlertDescription.cs" />
     <Compile Include="src\crypto\tls\AlertLevel.cs" />
+    <Compile Include="src\crypto\tls\BasicTlsPskIdentity.cs" />
     <Compile Include="src\crypto\tls\BulkCipherAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ByteQueue.cs" />
     <Compile Include="src\crypto\tls\CertChainType.cs" />
@@ -915,6 +921,7 @@
     <Compile Include="src\crypto\tls\DefaultTlsEncryptionCredentials.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsServer.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\DefaultTlsSrpGroupVerifier.cs" />
     <Compile Include="src\crypto\tls\DeferredHash.cs" />
     <Compile Include="src\crypto\tls\DigestInputBuffer.cs" />
     <Compile Include="src\crypto\tls\DigitallySigned.cs" />
@@ -951,17 +958,21 @@
     <Compile Include="src\crypto\tls\PrfAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ProtocolVersion.cs" />
     <Compile Include="src\crypto\tls\PskTlsClient.cs" />
+    <Compile Include="src\crypto\tls\PskTlsServer.cs" />
     <Compile Include="src\crypto\tls\RecordStream.cs" />
     <Compile Include="src\crypto\tls\SecurityParameters.cs" />
     <Compile Include="src\crypto\tls\ServerDHParams.cs" />
     <Compile Include="src\crypto\tls\ServerName.cs" />
     <Compile Include="src\crypto\tls\ServerNameList.cs" />
     <Compile Include="src\crypto\tls\ServerOnlyTlsAuthentication.cs" />
+    <Compile Include="src\crypto\tls\ServerSrpParams.cs" />
     <Compile Include="src\crypto\tls\SessionParameters.cs" />
     <Compile Include="src\crypto\tls\SignatureAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignatureAndHashAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignerInputBuffer.cs" />
+    <Compile Include="src\crypto\tls\SimulatedTlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\SrpTlsClient.cs" />
+    <Compile Include="src\crypto\tls\SrpTlsServer.cs" />
     <Compile Include="src\crypto\tls\SrtpProtectionProfile.cs" />
     <Compile Include="src\crypto\tls\Ssl3Mac.cs" />
     <Compile Include="src\crypto\tls\SupplementalDataEntry.cs" />
@@ -1014,7 +1025,10 @@
     <Compile Include="src\crypto\tls\TlsSessionImpl.cs" />
     <Compile Include="src\crypto\tls\TlsSigner.cs" />
     <Compile Include="src\crypto\tls\TlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpGroupVerifier.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\TlsSrpKeyExchange.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpLoginParameters.cs" />
     <Compile Include="src\crypto\tls\TlsSrpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsSrtpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsStream.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 1a43de126..ee05e159a 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -608,6 +608,7 @@
     <Compile Include="src\crypto\BufferedCipherBase.cs" />
     <Compile Include="src\crypto\BufferedIesCipher.cs" />
     <Compile Include="src\crypto\BufferedStreamCipher.cs" />
+    <Compile Include="src\crypto\Check.cs" />
     <Compile Include="src\crypto\CipherKeyGenerator.cs" />
     <Compile Include="src\crypto\CryptoException.cs" />
     <Compile Include="src\crypto\DataLengthException.cs" />
@@ -629,6 +630,7 @@
     <Compile Include="src\crypto\InvalidCipherTextException.cs" />
     <Compile Include="src\crypto\KeyGenerationParameters.cs" />
     <Compile Include="src\crypto\MaxBytesExceededException.cs" />
+    <Compile Include="src\crypto\OutputLengthException.cs" />
     <Compile Include="src\crypto\PbeParametersGenerator.cs" />
     <Compile Include="src\crypto\StreamBlockCipher.cs" />
     <Compile Include="src\crypto\agreement\DHAgreement.cs" />
@@ -644,6 +646,7 @@
     <Compile Include="src\crypto\agreement\kdf\ECDHKekGenerator.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Client.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Server.cs" />
+    <Compile Include="src\crypto\agreement\srp\SRP6StandardGroups.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Utilities.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6VerifierGenerator.cs" />
     <Compile Include="src\crypto\digests\GOST3411Digest.cs" />
@@ -848,6 +851,7 @@
     <Compile Include="src\crypto\parameters\RsaKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\RsaPrivateCrtKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\SkeinParameters.cs" />
+    <Compile Include="src\crypto\parameters\Srp6GroupParameters.cs" />
     <Compile Include="src\crypto\parameters\TweakableBlockCipherParameters.cs" />
     <Compile Include="src\crypto\prng\CryptoApiRandomGenerator.cs" />
     <Compile Include="src\crypto\prng\DigestRandomGenerator.cs" />
@@ -870,6 +874,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsClient.cs" />
@@ -883,6 +888,7 @@
     <Compile Include="src\crypto\tls\AbstractTlsSignerCredentials.cs" />
     <Compile Include="src\crypto\tls\AlertDescription.cs" />
     <Compile Include="src\crypto\tls\AlertLevel.cs" />
+    <Compile Include="src\crypto\tls\BasicTlsPskIdentity.cs" />
     <Compile Include="src\crypto\tls\BulkCipherAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ByteQueue.cs" />
     <Compile Include="src\crypto\tls\CertChainType.cs" />
@@ -909,6 +915,7 @@
     <Compile Include="src\crypto\tls\DefaultTlsEncryptionCredentials.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsServer.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\DefaultTlsSrpGroupVerifier.cs" />
     <Compile Include="src\crypto\tls\DeferredHash.cs" />
     <Compile Include="src\crypto\tls\DigestInputBuffer.cs" />
     <Compile Include="src\crypto\tls\DigitallySigned.cs" />
@@ -945,17 +952,21 @@
     <Compile Include="src\crypto\tls\PrfAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ProtocolVersion.cs" />
     <Compile Include="src\crypto\tls\PskTlsClient.cs" />
+    <Compile Include="src\crypto\tls\PskTlsServer.cs" />
     <Compile Include="src\crypto\tls\RecordStream.cs" />
     <Compile Include="src\crypto\tls\SecurityParameters.cs" />
     <Compile Include="src\crypto\tls\ServerDHParams.cs" />
     <Compile Include="src\crypto\tls\ServerName.cs" />
     <Compile Include="src\crypto\tls\ServerNameList.cs" />
     <Compile Include="src\crypto\tls\ServerOnlyTlsAuthentication.cs" />
+    <Compile Include="src\crypto\tls\ServerSrpParams.cs" />
     <Compile Include="src\crypto\tls\SessionParameters.cs" />
     <Compile Include="src\crypto\tls\SignatureAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignatureAndHashAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignerInputBuffer.cs" />
+    <Compile Include="src\crypto\tls\SimulatedTlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\SrpTlsClient.cs" />
+    <Compile Include="src\crypto\tls\SrpTlsServer.cs" />
     <Compile Include="src\crypto\tls\SrtpProtectionProfile.cs" />
     <Compile Include="src\crypto\tls\Ssl3Mac.cs" />
     <Compile Include="src\crypto\tls\SupplementalDataEntry.cs" />
@@ -1008,7 +1019,10 @@
     <Compile Include="src\crypto\tls\TlsSessionImpl.cs" />
     <Compile Include="src\crypto\tls\TlsSigner.cs" />
     <Compile Include="src\crypto\tls\TlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpGroupVerifier.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\TlsSrpKeyExchange.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpLoginParameters.cs" />
     <Compile Include="src\crypto\tls\TlsSrpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsSrtpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsStream.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 45a7dfaab..7e6830fe6 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -609,6 +609,7 @@
     <Compile Include="src\crypto\BufferedCipherBase.cs" />
     <Compile Include="src\crypto\BufferedIesCipher.cs" />
     <Compile Include="src\crypto\BufferedStreamCipher.cs" />
+    <Compile Include="src\crypto\Check.cs" />
     <Compile Include="src\crypto\CipherKeyGenerator.cs" />
     <Compile Include="src\crypto\CryptoException.cs" />
     <Compile Include="src\crypto\DataLengthException.cs" />
@@ -630,6 +631,7 @@
     <Compile Include="src\crypto\InvalidCipherTextException.cs" />
     <Compile Include="src\crypto\KeyGenerationParameters.cs" />
     <Compile Include="src\crypto\MaxBytesExceededException.cs" />
+    <Compile Include="src\crypto\OutputLengthException.cs" />
     <Compile Include="src\crypto\PbeParametersGenerator.cs" />
     <Compile Include="src\crypto\StreamBlockCipher.cs" />
     <Compile Include="src\crypto\agreement\DHAgreement.cs" />
@@ -645,6 +647,7 @@
     <Compile Include="src\crypto\agreement\kdf\ECDHKekGenerator.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Client.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Server.cs" />
+    <Compile Include="src\crypto\agreement\srp\SRP6StandardGroups.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6Utilities.cs" />
     <Compile Include="src\crypto\agreement\srp\SRP6VerifierGenerator.cs" />
     <Compile Include="src\crypto\digests\GOST3411Digest.cs" />
@@ -849,6 +852,7 @@
     <Compile Include="src\crypto\parameters\RsaKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\RsaPrivateCrtKeyParameters.cs" />
     <Compile Include="src\crypto\parameters\SkeinParameters.cs" />
+    <Compile Include="src\crypto\parameters\Srp6GroupParameters.cs" />
     <Compile Include="src\crypto\parameters\TweakableBlockCipherParameters.cs" />
     <Compile Include="src\crypto\prng\CryptoApiRandomGenerator.cs" />
     <Compile Include="src\crypto\prng\DigestRandomGenerator.cs" />
@@ -871,6 +875,7 @@
     <Compile Include="src\crypto\signers\PssSigner.cs" />
     <Compile Include="src\crypto\signers\RandomDsaKCalculator.cs" />
     <Compile Include="src\crypto\signers\RsaDigestSigner.cs" />
+    <Compile Include="src\crypto\signers\X931Signer.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsAgreementCredentials.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsCipherFactory.cs" />
     <Compile Include="src\crypto\tls\AbstractTlsClient.cs" />
@@ -884,6 +889,7 @@
     <Compile Include="src\crypto\tls\AbstractTlsSignerCredentials.cs" />
     <Compile Include="src\crypto\tls\AlertDescription.cs" />
     <Compile Include="src\crypto\tls\AlertLevel.cs" />
+    <Compile Include="src\crypto\tls\BasicTlsPskIdentity.cs" />
     <Compile Include="src\crypto\tls\BulkCipherAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ByteQueue.cs" />
     <Compile Include="src\crypto\tls\CertChainType.cs" />
@@ -910,6 +916,7 @@
     <Compile Include="src\crypto\tls\DefaultTlsEncryptionCredentials.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsServer.cs" />
     <Compile Include="src\crypto\tls\DefaultTlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\DefaultTlsSrpGroupVerifier.cs" />
     <Compile Include="src\crypto\tls\DeferredHash.cs" />
     <Compile Include="src\crypto\tls\DigestInputBuffer.cs" />
     <Compile Include="src\crypto\tls\DigitallySigned.cs" />
@@ -946,17 +953,21 @@
     <Compile Include="src\crypto\tls\PrfAlgorithm.cs" />
     <Compile Include="src\crypto\tls\ProtocolVersion.cs" />
     <Compile Include="src\crypto\tls\PskTlsClient.cs" />
+    <Compile Include="src\crypto\tls\PskTlsServer.cs" />
     <Compile Include="src\crypto\tls\RecordStream.cs" />
     <Compile Include="src\crypto\tls\SecurityParameters.cs" />
     <Compile Include="src\crypto\tls\ServerDHParams.cs" />
     <Compile Include="src\crypto\tls\ServerName.cs" />
     <Compile Include="src\crypto\tls\ServerNameList.cs" />
     <Compile Include="src\crypto\tls\ServerOnlyTlsAuthentication.cs" />
+    <Compile Include="src\crypto\tls\ServerSrpParams.cs" />
     <Compile Include="src\crypto\tls\SessionParameters.cs" />
     <Compile Include="src\crypto\tls\SignatureAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignatureAndHashAlgorithm.cs" />
     <Compile Include="src\crypto\tls\SignerInputBuffer.cs" />
+    <Compile Include="src\crypto\tls\SimulatedTlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\SrpTlsClient.cs" />
+    <Compile Include="src\crypto\tls\SrpTlsServer.cs" />
     <Compile Include="src\crypto\tls\SrtpProtectionProfile.cs" />
     <Compile Include="src\crypto\tls\Ssl3Mac.cs" />
     <Compile Include="src\crypto\tls\SupplementalDataEntry.cs" />
@@ -1009,7 +1020,10 @@
     <Compile Include="src\crypto\tls\TlsSessionImpl.cs" />
     <Compile Include="src\crypto\tls\TlsSigner.cs" />
     <Compile Include="src\crypto\tls\TlsSignerCredentials.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpGroupVerifier.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpIdentityManager.cs" />
     <Compile Include="src\crypto\tls\TlsSrpKeyExchange.cs" />
+    <Compile Include="src\crypto\tls\TlsSrpLoginParameters.cs" />
     <Compile Include="src\crypto\tls\TlsSrpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsSrtpUtilities.cs" />
     <Compile Include="src\crypto\tls\TlsStream.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index fdd5c152b..35474aebb 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -2964,6 +2964,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\Check.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\CipherKeyGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3069,6 +3074,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\OutputLengthException.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\PBEParametersGenerator.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -3144,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"
@@ -4164,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"
@@ -4274,6 +4294,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\signers\X931Signer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AbstractTlsAgreementCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4469,6 +4494,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"
@@ -4674,6 +4704,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"
@@ -4709,11 +4744,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"
@@ -4974,6 +5019,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"
@@ -9904,11 +9964,23 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\tls\x509-ca.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\tls\x509-ca-key.pem"
                     BuildAction = "EmbeddedResource"
                 />
                 <File
-                    RelPath = "test\data\tls\x509-ca.pem"
+                    RelPath = "test\data\tls\x509-client.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-client-ecdsa.pem"
                     BuildAction = "EmbeddedResource"
                 />
                 <File
@@ -9916,11 +9988,11 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
-                    RelPath = "test\data\tls\x509-client.pem"
+                    RelPath = "test\data\tls\x509-client-key-dsa.pem"
                     BuildAction = "EmbeddedResource"
                 />
                 <File
-                    RelPath = "test\data\tls\x509-server-key.pem"
+                    RelPath = "test\data\tls\x509-client-key-ecdsa.pem"
                     BuildAction = "EmbeddedResource"
                 />
                 <File
@@ -9928,6 +10000,26 @@
                     BuildAction = "EmbeddedResource"
                 />
                 <File
+                    RelPath = "test\data\tls\x509-server-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key-dsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
+                    RelPath = "test\data\tls\x509-server-key-ecdsa.pem"
+                    BuildAction = "EmbeddedResource"
+                />
+                <File
                     RelPath = "test\data\tls\keystores\client_store.dsa"
                     BuildAction = "EmbeddedResource"
                 />
@@ -10981,6 +11073,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\test\X931SignerTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\test\XSalsa20Test.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -10991,6 +11088,61 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\DtlsProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestCase.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestSuite.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\LoggingDatagramTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDatagramAssociation.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockDtlsServer.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockPskTlsClient.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\MockPskTlsServer.cs"
+                    SubType = "Code"
+                    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"
@@ -11001,21 +11153,81 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\NetworkStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\PipedStream.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\PskTlsClientTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsClientTest.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\TlsProtocolTest.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsPskProtocolTest.cs"
+                    SubType = "Code"
+                    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"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestCase.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestClientImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestConfig.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestServerImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestSuite.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsTestUtilities.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\UnreliableDatagramTransport.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\math\ec\test\AllTests.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/bcpg/S2k.cs b/crypto/src/bcpg/S2k.cs
index de08c016c..f6d306890 100644
--- a/crypto/src/bcpg/S2k.cs
+++ b/crypto/src/bcpg/S2k.cs
@@ -16,6 +16,8 @@ namespace Org.BouncyCastle.Bcpg
         public const int Salted = 1;
         public const int SaltedAndIterated = 3;
         public const int GnuDummyS2K = 101;
+        public const int GnuProtectionModeNoPrivateKey = 1;
+        public const int GnuProtectionModeDivertToCard = 2;
 
         internal int type;
         internal HashAlgorithmTag algorithm;
diff --git a/crypto/src/crypto/BufferedAeadBlockCipher.cs b/crypto/src/crypto/BufferedAeadBlockCipher.cs
index 04bcb88ed..7ba41090f 100644
--- a/crypto/src/crypto/BufferedAeadBlockCipher.cs
+++ b/crypto/src/crypto/BufferedAeadBlockCipher.cs
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Crypto
 		}
 
 		/**
-		* process a single byte, producing an output block if neccessary.
+		* process a single byte, producing an output block if necessary.
 		*
 		* @param in the input byte.
 		* @param out the space for any output that might be produced.
diff --git a/crypto/src/crypto/BufferedBlockCipher.cs b/crypto/src/crypto/BufferedBlockCipher.cs
index 3a98798a2..c87d2daf9 100644
--- a/crypto/src/crypto/BufferedBlockCipher.cs
+++ b/crypto/src/crypto/BufferedBlockCipher.cs
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Crypto
 		}
 
 		/**
-		* process a single byte, producing an output block if neccessary.
+		* process a single byte, producing an output block if necessary.
 		*
 		* @param in the input byte.
 		* @param out the space for any output that might be produced.
@@ -223,13 +223,10 @@ namespace Org.BouncyCastle.Crypto
 
 			if (outLength > 0)
 			{
-				if ((outOff + outLength) > output.Length)
-				{
-					throw new DataLengthException("output buffer too short");
-				}
+                Check.OutputLength(output, outOff, outLength, "output buffer too short");
 			}
 
-			int resultLen = 0;
+            int resultLen = 0;
 			int gapLen = buf.Length - bufOff;
 			if (length > gapLen)
 			{
@@ -339,17 +336,10 @@ namespace Org.BouncyCastle.Crypto
 			{
 				if (bufOff != 0)
 				{
-					if (!cipher.IsPartialBlockOkay)
-					{
-						throw new DataLengthException("data not block size aligned");
-					}
-	
-					if (outOff + bufOff > output.Length)
-					{
-						throw new DataLengthException("output buffer too short for DoFinal()");
-					}
-	
-					// NB: Can't copy directly, or we may write too much output
+                    Check.DataLength(!cipher.IsPartialBlockOkay, "data not block size aligned");
+                    Check.OutputLength(output, outOff, bufOff, "output buffer too short for DoFinal()");
+
+                    // NB: Can't copy directly, or we may write too much output
 					cipher.ProcessBlock(buf, 0, buf, 0);
 					Array.Copy(buf, 0, output, outOff, bufOff);
 				}
diff --git a/crypto/src/crypto/Check.cs b/crypto/src/crypto/Check.cs
new file mode 100644
index 000000000..96a05c64b
--- /dev/null
+++ b/crypto/src/crypto/Check.cs
@@ -0,0 +1,25 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+    internal class Check
+    {
+        internal static void DataLength(bool condition, string msg)
+        {
+            if (condition)
+                throw new DataLengthException(msg);
+        }
+
+        internal static void DataLength(byte[] buf, int off, int len, string msg)
+        {
+            if (off + len > buf.Length)
+                throw new DataLengthException(msg);
+        }
+
+        internal static void OutputLength(byte[] buf, int off, int len, string msg)
+        {
+            if (off + len > buf.Length)
+                throw new OutputLengthException(msg);
+        }
+    }
+}
diff --git a/crypto/src/crypto/OutputLengthException.cs b/crypto/src/crypto/OutputLengthException.cs
new file mode 100644
index 000000000..e1cf44925
--- /dev/null
+++ b/crypto/src/crypto/OutputLengthException.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto
+{
+#if !(NETCF_1_0 || NETCF_2_0 || SILVERLIGHT)
+    [Serializable]
+#endif
+    public class OutputLengthException
+        : DataLengthException
+    {
+        public OutputLengthException()
+        {
+        }
+
+        public OutputLengthException(
+            string message)
+            : base(message)
+        {
+        }
+
+        public OutputLengthException(
+            string message,
+            Exception exception)
+            : base(message, exception)
+        {
+        }
+    }
+}
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/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs
index 0cdd868fa..9d7f76c05 100644
--- a/crypto/src/crypto/engines/AesEngine.cs
+++ b/crypto/src/crypto/engines/AesEngine.cs
@@ -363,7 +363,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -377,41 +377,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.forEncryption = forEncryption;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "AES"; }
         }
 
-        public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
         {
             get { return false; }
         }
 
-        public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
             int		outOff)
         {
             if (WorkingKey == null)
-            {
                 throw new InvalidOperationException("AES engine not initialised");
-            }
 
-            if ((inOff + (32 / 2)) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + (32 / 2)) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, 16, "input buffer too short");
+            Check.OutputLength(output, outOff, 16, "output buffer too short");
 
             UnPackBlock(input, inOff);
 
@@ -429,7 +420,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
         }
 
diff --git a/crypto/src/crypto/engines/AesFastEngine.cs b/crypto/src/crypto/engines/AesFastEngine.cs
index 38d3a5841..a1b544568 100644
--- a/crypto/src/crypto/engines/AesFastEngine.cs
+++ b/crypto/src/crypto/engines/AesFastEngine.cs
@@ -695,7 +695,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -709,41 +709,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.forEncryption = forEncryption;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "AES"; }
         }
 
-        public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
         {
             get { return false; }
         }
 
-        public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[] input,
             int inOff,
             byte[] output,
             int outOff)
         {
             if (WorkingKey == null)
-            {
                 throw new InvalidOperationException("AES engine not initialised");
-            }
 
-            if ((inOff + (32 / 2)) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + (32 / 2)) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, 16, "input buffer too short");
+            Check.OutputLength(output, outOff, 16, "output buffer too short");
 
             UnPackBlock(input, inOff);
 
@@ -761,7 +752,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
         }
 
diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs
index 54f2d2e88..a6b9e3bd4 100644
--- a/crypto/src/crypto/engines/AesLightEngine.cs
+++ b/crypto/src/crypto/engines/AesLightEngine.cs
@@ -258,7 +258,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -272,41 +272,32 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.forEncryption = forEncryption;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "AES"; }
         }
 
-        public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
         {
             get { return false; }
         }
 
-        public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
             int		outOff)
         {
             if (WorkingKey == null)
-            {
                 throw new InvalidOperationException("AES engine not initialised");
-            }
 
-            if ((inOff + (32 / 2)) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + (32 / 2)) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, 16, "input buffer too short");
+            Check.OutputLength(output, outOff, 16, "output buffer too short");
 
             UnPackBlock(input, inOff);
 
@@ -324,7 +315,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
         }
 
diff --git a/crypto/src/crypto/engines/BlowfishEngine.cs b/crypto/src/crypto/engines/BlowfishEngine.cs
index 8f80f712e..7b50e832f 100644
--- a/crypto/src/crypto/engines/BlowfishEngine.cs
+++ b/crypto/src/crypto/engines/BlowfishEngine.cs
@@ -296,7 +296,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         //====================================
 
         private static readonly int    ROUNDS = 16;
-        private const int    BLOCK_SIZE = 8;  // bytes = 64 bits
+        private const int              BLOCK_SIZE = 8;  // bytes = 64 bits
         private static readonly int    SBOX_SK = 256;
         private static readonly int    P_SZ = ROUNDS+2;
 
@@ -353,19 +353,10 @@ namespace Org.BouncyCastle.Crypto.Engines
             int		outOff)
         {
             if (workingKey == null)
-            {
                 throw new InvalidOperationException("Blowfish not initialised");
-            }
 
-            if ((inOff + BLOCK_SIZE) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + BLOCK_SIZE) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
             if (encrypting)
             {
diff --git a/crypto/src/crypto/engines/CamelliaEngine.cs b/crypto/src/crypto/engines/CamelliaEngine.cs
index 8f4a442e9..71bd1b0dc 100644
--- a/crypto/src/crypto/engines/CamelliaEngine.cs
+++ b/crypto/src/crypto/engines/CamelliaEngine.cs
@@ -611,7 +611,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -623,22 +623,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 			initialised = true;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Camellia"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BLOCK_SIZE;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
@@ -646,12 +646,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Camellia engine not initialised");
-			if ((inOff + BLOCK_SIZE) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + BLOCK_SIZE) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			if (_keyIs128)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (_keyIs128)
 			{
 				return processBlock128(input, inOff, output, outOff);
 			}
@@ -661,7 +660,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 			// nothing
 		}
diff --git a/crypto/src/crypto/engines/CamelliaLightEngine.cs b/crypto/src/crypto/engines/CamelliaLightEngine.cs
index a301eb55e..a132227c5 100644
--- a/crypto/src/crypto/engines/CamelliaLightEngine.cs
+++ b/crypto/src/crypto/engines/CamelliaLightEngine.cs
@@ -524,22 +524,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 			initialised = false;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Camellia"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BLOCK_SIZE;
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -551,7 +551,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			initialised = true;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
             byte[]	output,
@@ -559,12 +559,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Camellia engine not initialised");
-			if ((inOff + BLOCK_SIZE) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + BLOCK_SIZE) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			if (_keyis128)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (_keyis128)
 			{
 				return processBlock128(input, inOff, output, outOff);
 			}
@@ -574,7 +573,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 	}
diff --git a/crypto/src/crypto/engines/Cast5Engine.cs b/crypto/src/crypto/engines/Cast5Engine.cs
index 4c3f84a55..1af30a335 100644
--- a/crypto/src/crypto/engines/Cast5Engine.cs
+++ b/crypto/src/crypto/engines/Cast5Engine.cs
@@ -329,7 +329,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -360,12 +360,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 			int blockSize = GetBlockSize();
             if (_workingKey == null)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
-            if ((inOff + blockSize) > input.Length)
-                throw new DataLengthException("Input buffer too short");
-            if ((outOff + blockSize) > output.Length)
-                throw new DataLengthException("Output buffer too short");
 
-			if (_encrypting)
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, blockSize, "output buffer too short");
+
+            if (_encrypting)
             {
                 return EncryptBlock(input, inOff, output, outOff);
             }
diff --git a/crypto/src/crypto/engines/ChaChaEngine.cs b/crypto/src/crypto/engines/ChaChaEngine.cs
index f4a7b8fe1..46b59ed2e 100644
--- a/crypto/src/crypto/engines/ChaChaEngine.cs
+++ b/crypto/src/crypto/engines/ChaChaEngine.cs
@@ -162,7 +162,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 				x09 += x14; x04 = R(x04 ^ x09, 12);
 				x03 += x04; x14 = R(x14 ^ x03, 8);
 				x09 += x14; x04 = R(x04 ^ x09, 7);
-
 			}
 
 			x[ 0] = x00 + input[ 0];
@@ -182,8 +181,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 			x[14] = x14 + input[14];
 			x[15] = x15 + input[15];
 		}
-
 	}
-
 }
 
diff --git a/crypto/src/crypto/engines/DesEdeEngine.cs b/crypto/src/crypto/engines/DesEdeEngine.cs
index eec4ec59d..bc40b56a8 100644
--- a/crypto/src/crypto/engines/DesEdeEngine.cs
+++ b/crypto/src/crypto/engines/DesEdeEngine.cs
@@ -70,10 +70,9 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (workingKey1 == null)
                 throw new InvalidOperationException("DESede engine not initialised");
-            if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
+
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
 
             byte[] temp = new byte[BLOCK_SIZE];
 
diff --git a/crypto/src/crypto/engines/DesEdeWrapEngine.cs b/crypto/src/crypto/engines/DesEdeWrapEngine.cs
index fdc71687f..43100a9bd 100644
--- a/crypto/src/crypto/engines/DesEdeWrapEngine.cs
+++ b/crypto/src/crypto/engines/DesEdeWrapEngine.cs
@@ -52,7 +52,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param forWrapping
         * @param param
         */
-        public void Init(
+        public virtual void Init(
 			bool				forWrapping,
 			ICipherParameters	parameters)
         {
@@ -103,7 +103,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         *
         * @return
         */
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "DESede"; }
         }
@@ -116,7 +116,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param inLen
         * @return
         */
-        public byte[] Wrap(
+        public virtual byte[] Wrap(
 			byte[]	input,
 			int		inOff,
 			int		length)
@@ -185,7 +185,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @return
         * @throws InvalidCipherTextException
         */
-        public byte[] Unwrap(
+        public virtual byte[] Unwrap(
 			byte[]	input,
 			int		inOff,
 			int		length)
diff --git a/crypto/src/crypto/engines/DesEngine.cs b/crypto/src/crypto/engines/DesEngine.cs
index 067cf45e3..a6d580bb6 100644
--- a/crypto/src/crypto/engines/DesEngine.cs
+++ b/crypto/src/crypto/engines/DesEngine.cs
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             get { return "DES"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
@@ -59,12 +59,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (workingKey == null)
                 throw new InvalidOperationException("DES engine not initialised");
-			if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
 
-			DesFunc(workingKey, input, inOff, output, outOff);
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            DesFunc(workingKey, input, inOff, output, outOff);
 
 			return BLOCK_SIZE;
         }
diff --git a/crypto/src/crypto/engines/ElGamalEngine.cs b/crypto/src/crypto/engines/ElGamalEngine.cs
index 3d256a087..197d7bc15 100644
--- a/crypto/src/crypto/engines/ElGamalEngine.cs
+++ b/crypto/src/crypto/engines/ElGamalEngine.cs
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		private bool forEncryption;
 		private int bitSize;
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "ElGamal"; }
 		}
@@ -28,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param forEncryption true if we are encrypting, false otherwise.
 		* @param param the necessary ElGamal key parameters.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an input block.
 		*/
-		public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an output block.
 		*/
-		public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -107,7 +107,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return the result of the ElGamal process.
 		* @exception DataLengthException the input block is too large.
 		*/
-		public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			int		length)
diff --git a/crypto/src/crypto/engines/GOST28147Engine.cs b/crypto/src/crypto/engines/GOST28147Engine.cs
index 8eb6f36b5..e37ddaefd 100644
--- a/crypto/src/crypto/engines/GOST28147Engine.cs
+++ b/crypto/src/crypto/engines/GOST28147Engine.cs
@@ -150,7 +150,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param parameters the parameters required to set up the cipher.
 		* @exception ArgumentException if the parameters argument is inappropriate.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -187,48 +187,39 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Gost28147"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BlockSize;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
 			int		outOff)
 		{
 			if (workingKey == null)
-			{
 				throw new InvalidOperationException("Gost28147 engine not initialised");
-			}
 
-			if ((inOff + BlockSize) > input.Length)
-			{
-				throw new DataLengthException("input buffer too short");
-			}
-
-			if ((outOff + BlockSize) > output.Length)
-			{
-				throw new DataLengthException("output buffer too short");
-			}
+            Check.DataLength(input, inOff, BlockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
 
-			Gost28147Func(workingKey, input, inOff, output, outOff);
+            Gost28147Func(workingKey, input, inOff, output, outOff);
 
 			return BlockSize;
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
diff --git a/crypto/src/crypto/engines/HC128Engine.cs b/crypto/src/crypto/engines/HC128Engine.cs
index a2d099f87..40c7a4e17 100644
--- a/crypto/src/crypto/engines/HC128Engine.cs
+++ b/crypto/src/crypto/engines/HC128Engine.cs
@@ -142,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			cnt = 0;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "HC-128"; }
 		}
@@ -156,7 +156,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @throws ArgumentException if the params argument is
 		*                                  inappropriate (ie. the key is not 128 bit long).
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -201,7 +201,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return ret;
 		}
 
-		public void ProcessBytes(
+        public virtual void ProcessBytes(
 			byte[]	input,
 			int		inOff,
 			int		len,
@@ -210,24 +210,23 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
-			if ((inOff + len) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + len) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			for (int i = 0; i < len; i++)
+            Check.DataLength(input, inOff, len, "input buffer too short");
+            Check.OutputLength(output, outOff, len, "output buffer too short");
+
+            for (int i = 0; i < len; i++)
 			{
 				output[outOff + i] = (byte)(input[inOff + i] ^ GetByte());
 			}
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 			idx = 0;
 			Init();
 		}
 
-		public byte ReturnByte(byte input)
+        public virtual byte ReturnByte(byte input)
 		{
 			return (byte)(input ^ GetByte());
 		}
diff --git a/crypto/src/crypto/engines/HC256Engine.cs b/crypto/src/crypto/engines/HC256Engine.cs
index da717dab7..6eb360711 100644
--- a/crypto/src/crypto/engines/HC256Engine.cs
+++ b/crypto/src/crypto/engines/HC256Engine.cs
@@ -126,7 +126,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			cnt = 0;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "HC-256"; }
 		}
@@ -140,7 +140,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @throws ArgumentException if the params argument is
 		*                                  inappropriate (ie. the key is not 256 bit long).
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -185,7 +185,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return ret;
 		}
 
-		public void ProcessBytes(
+        public virtual void ProcessBytes(
 			byte[]	input,
 			int		inOff,
 			int		len,
@@ -194,24 +194,23 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
-			if ((inOff + len) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + len) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			for (int i = 0; i < len; i++)
+            Check.DataLength(input, inOff, len, "input buffer too short");
+            Check.OutputLength(output, outOff, len, "output buffer too short");
+
+            for (int i = 0; i < len; i++)
 			{
 				output[outOff + i] = (byte)(input[inOff + i] ^ GetByte());
 			}
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 			idx = 0;
 			Init();
 		}
 
-		public byte ReturnByte(byte input)
+        public virtual byte ReturnByte(byte input)
 		{
 			return (byte)(input ^ GetByte());
 		}
diff --git a/crypto/src/crypto/engines/ISAACEngine.cs b/crypto/src/crypto/engines/ISAACEngine.cs
index 9c58678a0..f25577130 100644
--- a/crypto/src/crypto/engines/ISAACEngine.cs
+++ b/crypto/src/crypto/engines/ISAACEngine.cs
@@ -35,7 +35,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the params argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption, 
             ICipherParameters	parameters)
         {
@@ -53,7 +53,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             setKey(p.GetKey());
         }
 
-        public byte ReturnByte(
+        public virtual byte ReturnByte(
             byte input)
         {
             if (index == 0) 
@@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return output;
         }
 
-        public void ProcessBytes(
+        public virtual void ProcessBytes(
             byte[]	input, 
             int		inOff, 
             int		len, 
@@ -77,10 +77,9 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (!initialised)
                 throw new InvalidOperationException(AlgorithmName + " not initialised");
-            if ((inOff + len) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + len) > output.Length)
-                throw new DataLengthException("output buffer too short");
+
+            Check.DataLength(input, inOff, len, "input buffer too short");
+            Check.OutputLength(output, outOff, len, "output buffer too short");
 
             for (int i = 0; i < len; i++)
             {
@@ -94,12 +93,12 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "ISAAC"; }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             setKey(workingKey);
         }
diff --git a/crypto/src/crypto/engines/IdeaEngine.cs b/crypto/src/crypto/engines/IdeaEngine.cs
index 46b5a787c..4909510ac 100644
--- a/crypto/src/crypto/engines/IdeaEngine.cs
+++ b/crypto/src/crypto/engines/IdeaEngine.cs
@@ -47,7 +47,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -58,43 +58,37 @@ namespace Org.BouncyCastle.Crypto.Engines
                 ((KeyParameter)parameters).GetKey());
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "IDEA"; }
         }
 
-        public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
         {
             get { return false; }
         }
 
-        public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[] input,
             int inOff,
             byte[] output,
             int outOff)
         {
             if (workingKey == null)
-            {
                 throw new InvalidOperationException("IDEA engine not initialised");
-            }
-            if ((inOff + BLOCK_SIZE) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-            if ((outOff + BLOCK_SIZE) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
             IdeaFunc(workingKey, input, inOff, output, outOff);
             return BLOCK_SIZE;
         }
-        public void Reset()
+        public virtual void Reset()
         {
         }
         private static readonly int    MASK = 0xffff;
diff --git a/crypto/src/crypto/engines/IesEngine.cs b/crypto/src/crypto/engines/IesEngine.cs
index 70df3077c..a2004a9d6 100644
--- a/crypto/src/crypto/engines/IesEngine.cs
+++ b/crypto/src/crypto/engines/IesEngine.cs
@@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param pubParam the recipient's/sender's public key parameters
         * @param param encoding and derivation parameters.
         */
-        public void Init(
+        public virtual void Init(
             bool                     forEncryption,
             ICipherParameters            privParameters,
             ICipherParameters            pubParameters,
@@ -213,7 +213,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return buf;
         }
 
-        public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
             byte[]  input,
             int     inOff,
             int     inLen)
@@ -224,10 +224,16 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             byte[] zBytes = BigIntegers.AsUnsignedByteArray(agree.GetFieldSize(), z);
 
-            return forEncryption
-                ?	EncryptBlock(input, inOff, inLen, zBytes)
-                :	DecryptBlock(input, inOff, inLen, zBytes);
+            try
+            {
+                return forEncryption
+                    ?	EncryptBlock(input, inOff, inLen, zBytes)
+                    :	DecryptBlock(input, inOff, inLen, zBytes);
+            }
+            finally
+            {
+                Array.Clear(zBytes, 0, zBytes.Length);
+            }
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/NaccacheSternEngine.cs b/crypto/src/crypto/engines/NaccacheSternEngine.cs
index 9ca092351..e547e0caf 100644
--- a/crypto/src/crypto/engines/NaccacheSternEngine.cs
+++ b/crypto/src/crypto/engines/NaccacheSternEngine.cs
@@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @see org.bouncycastle.crypto.AsymmetricBlockCipher#init(bool,
 		*      org.bouncycastle.crypto.CipherParameters)
 		*/
-		public void Init(
+		public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public bool Debug
+        public virtual bool Debug
 		{
 			set { this.debug = value; }
 		}
@@ -93,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetInputBlockSize()
 		*/
-		public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @see org.bouncycastle.crypto.AsymmetricBlockCipher#GetOutputBlockSize()
 		*/
-		public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @see org.bouncycastle.crypto.AsymmetricBlockCipher#ProcessBlock(byte[],
 		*      int, int)
 		*/
-		public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
 			byte[]	inBytes,
 			int		inOff,
 			int		length)
@@ -245,7 +245,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return The byte[] representation of the encrypted BigInteger (i.e.
 		*         crypted.toByteArray())
 		*/
-		public byte[] Encrypt(
+        public virtual byte[] Encrypt(
 			BigInteger plain)
 		{
 			// Always return modulus size values 0-padded at the beginning
@@ -273,7 +273,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return encrypt((block1 + block2) mod sigma)
 		* @throws InvalidCipherTextException
 		*/
-		public byte[] AddCryptedBlocks(
+        public virtual byte[] AddCryptedBlocks(
 			byte[] block1,
 			byte[] block2)
 		{
@@ -329,7 +329,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return the data after it went through the NaccacheSternEngine.
 		* @throws InvalidCipherTextException
 		*/
-		public byte[] ProcessData(
+        public virtual byte[] ProcessData(
 			byte[] data)
 		{
 			if (debug)
diff --git a/crypto/src/crypto/engines/NoekeonEngine.cs b/crypto/src/crypto/engines/NoekeonEngine.cs
index b73e696a9..dd78a4ea5 100644
--- a/crypto/src/crypto/engines/NoekeonEngine.cs
+++ b/crypto/src/crypto/engines/NoekeonEngine.cs
@@ -42,17 +42,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 			_initialised = false;
 		}
 
-		public string AlgorithmName
+		public virtual string AlgorithmName
 		{
 			get { return "Noekeon"; }
 		}
 
-		public bool IsPartialBlockOkay
+		public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return GenericSize;
 		}
@@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the params argument is
 		* inappropriate.
 		*/
-		public void Init(
+		public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -80,7 +80,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			setKey(p.GetKey());
 		}
 
-		public int ProcessBlock(
+		public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
@@ -88,17 +88,16 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
-			if ((inOff + GenericSize) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + GenericSize) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			return _forEncryption
+            Check.DataLength(input, inOff, GenericSize, "input buffer too short");
+            Check.OutputLength(output, outOff, GenericSize, "output buffer too short");
+
+            return _forEncryption
 				?	encryptBlock(input, inOff, output, outOff)
 				:	decryptBlock(input, inOff, output, outOff);
 		}
 
-		public void Reset()
+		public virtual void Reset()
 		{
 			// TODO This should do something in case the encryption is aborted
 		}
diff --git a/crypto/src/crypto/engines/NullEngine.cs b/crypto/src/crypto/engines/NullEngine.cs
index 407b8ccc6..f883b7c29 100644
--- a/crypto/src/crypto/engines/NullEngine.cs
+++ b/crypto/src/crypto/engines/NullEngine.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -26,22 +26,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 			initialised = true;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Null"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return true; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BlockSize;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
@@ -49,12 +49,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (!initialised)
 				throw new InvalidOperationException("Null engine not initialised");
-			if ((inOff + BlockSize) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + BlockSize) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			for (int i = 0; i < BlockSize; ++i)
+            Check.DataLength(input, inOff, BlockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, BlockSize, "output buffer too short");
+
+            for (int i = 0; i < BlockSize; ++i)
 			{
 				output[outOff + i] = input[inOff + i];
 			}
@@ -62,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 			// nothing needs to be done
 		}
diff --git a/crypto/src/crypto/engines/RC2Engine.cs b/crypto/src/crypto/engines/RC2Engine.cs
index aaf8c714c..b56953de5 100644
--- a/crypto/src/crypto/engines/RC2Engine.cs
+++ b/crypto/src/crypto/engines/RC2Engine.cs
@@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -139,26 +139,26 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-		public void Reset()
+        public virtual void Reset()
         {
         }
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RC2"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public  int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
@@ -166,12 +166,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (workingKey == null)
                 throw new InvalidOperationException("RC2 engine not initialised");
-            if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
 
-			if (encrypting)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
             }
@@ -308,5 +307,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             outBytes[outOff + 7] = (byte)(x76 >> 8);
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/RC2WrapEngine.cs b/crypto/src/crypto/engines/RC2WrapEngine.cs
index 238c9f76a..5742aa8b7 100644
--- a/crypto/src/crypto/engines/RC2WrapEngine.cs
+++ b/crypto/src/crypto/engines/RC2WrapEngine.cs
@@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			* @param forWrapping
 			* @param param
 			*/
-		public void Init(
+        public virtual void Init(
 			bool				forWrapping,
 			ICipherParameters	parameters)
 		{
@@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return
 		*/
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "RC2"; }
 		}
@@ -114,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param inLen
 		* @return
 		*/
-		public byte[] Wrap(
+        public virtual byte[] Wrap(
 			byte[]	input,
 			int		inOff,
 			int		length)
@@ -215,7 +215,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return
 		* @throws InvalidCipherTextException
 		*/
-		public byte[] Unwrap(
+        public virtual byte[] Unwrap(
 			byte[]	input,
 			int		inOff,
 			int		length)
diff --git a/crypto/src/crypto/engines/RC4Engine.cs b/crypto/src/crypto/engines/RC4Engine.cs
index c65468d93..fd84b7d23 100644
--- a/crypto/src/crypto/engines/RC4Engine.cs
+++ b/crypto/src/crypto/engines/RC4Engine.cs
@@ -27,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -47,12 +47,12 @@ namespace Org.BouncyCastle.Crypto.Engines
             throw new ArgumentException("invalid parameter passed to RC4 init - " + parameters.GetType().ToString());
         }
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RC4"; }
         }
 
-		public byte ReturnByte(
+        public virtual byte ReturnByte(
 			byte input)
         {
             x = (x + 1) & 0xff;
@@ -67,23 +67,15 @@ namespace Org.BouncyCastle.Crypto.Engines
             return (byte)(input ^ engineState[(engineState[x] + engineState[y]) & 0xff]);
         }
 
-        public void ProcessBytes(
+        public virtual void ProcessBytes(
             byte[]	input,
             int		inOff,
             int		length,
             byte[]	output,
-            int		outOff
-        )
+            int		outOff)
         {
-            if ((inOff + length) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + length) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, length, "input buffer too short");
+            Check.OutputLength(output, outOff, length, "output buffer too short");
 
             for (int i = 0; i < length ; i++)
             {
@@ -101,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             SetKey(workingKey);
         }
@@ -143,5 +135,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/RC532Engine.cs b/crypto/src/crypto/engines/RC532Engine.cs
index 1661707ef..169a60b98 100644
--- a/crypto/src/crypto/engines/RC532Engine.cs
+++ b/crypto/src/crypto/engines/RC532Engine.cs
@@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 //            _S            = null;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RC5-32"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return 2 * 4;
         }
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.forEncryption = forEncryption;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
@@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 				:	DecryptBlock(input, inOff, output, outOff);
         }
 
-		public void Reset()
+        public virtual void Reset()
         {
         }
 
@@ -290,5 +290,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             dst[dstOff + 3] = (byte)(word >> 24);
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/RC564Engine.cs b/crypto/src/crypto/engines/RC564Engine.cs
index 5c69d40ff..ddcce0fa8 100644
--- a/crypto/src/crypto/engines/RC564Engine.cs
+++ b/crypto/src/crypto/engines/RC564Engine.cs
@@ -51,17 +51,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 //            _S            = null;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RC5-64"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return 2 * bytesPerWord;
         }
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool             forEncryption,
             ICipherParameters    parameters)
         {
@@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             SetKey(p.GetKey());
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]  input,
             int     inOff,
             byte[]  output,
@@ -102,7 +102,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                                         : DecryptBlock(input, inOff, output, outOff);
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
         }
 
@@ -291,5 +291,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/RC6Engine.cs b/crypto/src/crypto/engines/RC6Engine.cs
index d72cc2f7b..196bd8394 100644
--- a/crypto/src/crypto/engines/RC6Engine.cs
+++ b/crypto/src/crypto/engines/RC6Engine.cs
@@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 //            _S            = null;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RC6"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return 4 * bytesPerWord;
         }
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -84,7 +84,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			SetKey(p.GetKey());
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
@@ -93,17 +93,16 @@ namespace Org.BouncyCastle.Crypto.Engines
 			int blockSize = GetBlockSize();
 			if (_S == null)
 				throw new InvalidOperationException("RC6 engine not initialised");
-			if ((inOff + blockSize) > input.Length)
-				throw new DataLengthException("input buffer too short");
-			if ((outOff + blockSize) > output.Length)
-				throw new DataLengthException("output buffer too short");
 
-			return (forEncryption)
+            Check.DataLength(input, inOff, blockSize, "input buffer too short");
+            Check.OutputLength(output, outOff, blockSize, "output buffer too short");
+
+            return (forEncryption)
 				?	EncryptBlock(input, inOff, output, outOff)
 				:	DecryptBlock(input, inOff, output, outOff);
         }
 
-		public void Reset()
+        public virtual void Reset()
         {
         }
 
@@ -358,5 +357,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/RFC3211WrapEngine.cs b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
index e520075f9..4e3af5227 100644
--- a/crypto/src/crypto/engines/RFC3211WrapEngine.cs
+++ b/crypto/src/crypto/engines/RFC3211WrapEngine.cs
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			this.engine = new CbcBlockCipher(engine);
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forWrapping,
 			ICipherParameters	param)
 		{
@@ -48,12 +48,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return engine.GetUnderlyingCipher().AlgorithmName + "/RFC3211Wrap"; }
 		}
 
-		public byte[] Wrap(
+        public virtual byte[] Wrap(
 			byte[]	inBytes,
 			int		inOff,
 			int		inLen)
@@ -99,7 +99,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return cekBlock;
 		}
 
-		public byte[] Unwrap(
+        public virtual byte[] Unwrap(
 			byte[]	inBytes,
 			int		inOff,
 			int		inLen)
diff --git a/crypto/src/crypto/engines/RFC3394WrapEngine.cs b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
index 5615a63e5..4bb0e2114 100644
--- a/crypto/src/crypto/engines/RFC3394WrapEngine.cs
+++ b/crypto/src/crypto/engines/RFC3394WrapEngine.cs
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			this.engine = engine;
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forWrapping,
 			ICipherParameters	parameters)
 		{
@@ -64,12 +64,12 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return engine.AlgorithmName; }
 		}
 
-		public byte[] Wrap(
+        public virtual byte[] Wrap(
 			byte[]	input,
 			int		inOff,
 			int		inLen)
@@ -119,7 +119,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return block;
 		}
 
-		public byte[] Unwrap(
+        public virtual byte[] Unwrap(
 			byte[]  input,
 			int     inOff,
 			int     inLen)
diff --git a/crypto/src/crypto/engines/RSABlindedEngine.cs b/crypto/src/crypto/engines/RSABlindedEngine.cs
index cdf69ddda..037abf7e9 100644
--- a/crypto/src/crypto/engines/RSABlindedEngine.cs
+++ b/crypto/src/crypto/engines/RSABlindedEngine.cs
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		private RsaKeyParameters key;
 		private SecureRandom random;
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "RSA"; }
 		}
@@ -28,7 +28,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		 * @param forEncryption true if we are encrypting, false otherwise.
 		 * @param param the necessary RSA key parameters.
 		 */
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	param)
 		{
@@ -55,7 +55,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		 *
 		 * @return maximum size for an input block.
 		 */
-		public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
 		{
 			return core.GetInputBlockSize();
 		}
@@ -67,7 +67,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		 *
 		 * @return maximum size for an output block.
 		 */
-		public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
 		{
 			return core.GetOutputBlockSize();
 		}
@@ -81,7 +81,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		 * @return the result of the RSA process.
 		 * @exception DataLengthException the input block is too large.
 		 */
-		public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
 			byte[]	inBuf,
 			int		inOff,
 			int		inLen)
diff --git a/crypto/src/crypto/engines/RSABlindingEngine.cs b/crypto/src/crypto/engines/RSABlindingEngine.cs
index 76b57a3f7..c636627bf 100644
--- a/crypto/src/crypto/engines/RSABlindingEngine.cs
+++ b/crypto/src/crypto/engines/RSABlindingEngine.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
 		private bool forEncryption;
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "RSA"; }
 		}
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param forEncryption true if we are encrypting (blinding), false otherwise.
 		* @param param         the necessary RSA key parameters.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	param)
 		{
@@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an input block.
 		*/
-		public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
 		{
 			return core.GetInputBlockSize();
 		}
@@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an output block.
 		*/
-		public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
 		{
 			return core.GetOutputBlockSize();
 		}
@@ -89,7 +89,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @return the result of the RSA process.
 		* @throws DataLengthException the input block is too large.
 		*/
-		public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
 			byte[]	inBuf,
 			int		inOff,
 			int		inLen)
diff --git a/crypto/src/crypto/engines/RSACoreEngine.cs b/crypto/src/crypto/engines/RSACoreEngine.cs
index 4e64d25d6..38326371f 100644
--- a/crypto/src/crypto/engines/RSACoreEngine.cs
+++ b/crypto/src/crypto/engines/RSACoreEngine.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @param forEncryption true if we are encrypting, false otherwise.
 		* @param param the necessary RSA key parameters.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -45,7 +45,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an input block.
 		*/
-		public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		*
 		* @return maximum size for an output block.
 		*/
-		public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
 		{
 			if (forEncryption)
 			{
@@ -72,7 +72,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return (bitSize - 1) / 8;
 		}
 
-		public BigInteger ConvertInput(
+        public virtual BigInteger ConvertInput(
 			byte[]	inBuf,
 			int		inOff,
 			int		inLen)
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return input;
 		}
 
-		public byte[] ConvertOutput(
+        public virtual byte[] ConvertOutput(
 			BigInteger result)
 		{
 			byte[] output = result.ToByteArrayUnsigned();
@@ -112,7 +112,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return output;
 		}
 
-		public BigInteger ProcessBlock(
+        public virtual BigInteger ProcessBlock(
 			BigInteger input)
 		{
 			if (key is RsaPrivateCrtKeyParameters)
diff --git a/crypto/src/crypto/engines/RijndaelEngine.cs b/crypto/src/crypto/engines/RijndaelEngine.cs
index df2e5baea..80f522353 100644
--- a/crypto/src/crypto/engines/RijndaelEngine.cs
+++ b/crypto/src/crypto/engines/RijndaelEngine.cs
@@ -571,7 +571,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the parameters argument is
 		* inappropriate.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool           forEncryption,
 			ICipherParameters  parameters)
 		{
@@ -585,43 +585,34 @@ namespace Org.BouncyCastle.Crypto.Engines
 			throw new ArgumentException("invalid parameter passed to Rijndael init - " + parameters.GetType().ToString());
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Rijndael"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BC / 2;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	input,
 			int		inOff,
 			byte[]	output,
 			int		outOff)
 		{
 			if (workingKey == null)
-			{
 				throw new InvalidOperationException("Rijndael engine not initialised");
-			}
-
-			if ((inOff + (BC / 2)) > input.Length)
-			{
-				throw new DataLengthException("input buffer too short");
-			}
 
-			if ((outOff + (BC / 2)) > output.Length)
-			{
-				throw new DataLengthException("output buffer too short");
-			}
+            Check.DataLength(input, inOff, (BC / 2), "input buffer too short");
+            Check.OutputLength(output, outOff, (BC / 2), "output buffer too short");
 
-			UnPackBlock(input, inOff);
+            UnPackBlock(input, inOff);
 
 			if (forEncryption)
 			{
@@ -637,11 +628,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BC / 2;
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
-		private  void UnPackBlock(
+		private void UnPackBlock(
 			byte[]      bytes,
 			int         off)
 		{
@@ -743,5 +734,4 @@ namespace Org.BouncyCastle.Crypto.Engines
 			KeyAddition(rk[0]);
 		}
 	}
-
 }
diff --git a/crypto/src/crypto/engines/RsaEngine.cs b/crypto/src/crypto/engines/RsaEngine.cs
index 7e6dfb163..4399b4409 100644
--- a/crypto/src/crypto/engines/RsaEngine.cs
+++ b/crypto/src/crypto/engines/RsaEngine.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Engines
     {
 		private RsaCoreEngine core;
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "RSA"; }
         }
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @param forEncryption true if we are encrypting, false otherwise.
         * @param param the necessary RSA key parameters.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -38,7 +38,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         *
         * @return maximum size for an input block.
         */
-        public int GetInputBlockSize()
+        public virtual int GetInputBlockSize()
         {
 			return core.GetInputBlockSize();
         }
@@ -50,7 +50,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         *
         * @return maximum size for an output block.
         */
-        public int GetOutputBlockSize()
+        public virtual int GetOutputBlockSize()
         {
 			return core.GetOutputBlockSize();
         }
@@ -64,7 +64,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @return the result of the RSA process.
         * @exception DataLengthException the input block is too large.
         */
-        public byte[] ProcessBlock(
+        public virtual byte[] ProcessBlock(
             byte[]	inBuf,
             int		inOff,
             int		inLen)
diff --git a/crypto/src/crypto/engines/SEEDEngine.cs b/crypto/src/crypto/engines/SEEDEngine.cs
index efea0f1fe..f615b8476 100644
--- a/crypto/src/crypto/engines/SEEDEngine.cs
+++ b/crypto/src/crypto/engines/SEEDEngine.cs
@@ -168,7 +168,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		private int[] wKey;
 		private bool forEncryption;
 
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -176,22 +176,22 @@ namespace Org.BouncyCastle.Crypto.Engines
 			wKey = createWorkingKey(((KeyParameter)parameters).GetKey());
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "SEED"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return BlockSize;
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	inBuf,
 			int		inOff,
 			byte[]	outBuf,
@@ -199,12 +199,11 @@ namespace Org.BouncyCastle.Crypto.Engines
 		{
 			if (wKey == null)
 				throw new InvalidOperationException("SEED engine not initialised");
-			if (inOff + BlockSize > inBuf.Length)
-				throw new DataLengthException("input buffer too short");
-			if (outOff + BlockSize > outBuf.Length)
-				throw new DataLengthException("output buffer too short");
 
-			long l = bytesToLong(inBuf, inOff + 0);
+            Check.DataLength(inBuf, inOff, BlockSize, "input buffer too short");
+            Check.OutputLength(outBuf, outOff, BlockSize, "output buffer too short");
+
+            long l = bytesToLong(inBuf, inOff + 0);
 			long r = bytesToLong(inBuf, inOff + 8);
 
 			if (forEncryption)
@@ -234,7 +233,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BlockSize;
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
diff --git a/crypto/src/crypto/engines/Salsa20Engine.cs b/crypto/src/crypto/engines/Salsa20Engine.cs
index 81884d603..9b27dc7b4 100644
--- a/crypto/src/crypto/engines/Salsa20Engine.cs
+++ b/crypto/src/crypto/engines/Salsa20Engine.cs
@@ -61,7 +61,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			this.rounds = rounds;
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption, 
 			ICipherParameters	parameters)
 		{
@@ -108,7 +108,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public byte ReturnByte(
+        public virtual byte ReturnByte(
 			byte input)
 		{
 			if (LimitExceeded())
@@ -136,7 +136,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public void ProcessBytes(
+        public virtual void ProcessBytes(
 			byte[]	inBytes, 
 			int		inOff, 
 			int		len, 
@@ -144,26 +144,15 @@ namespace Org.BouncyCastle.Crypto.Engines
 			int		outOff)
 		{
 			if (!initialised)
-			{
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
-			}
 
-			if ((inOff + len) > inBytes.Length)
-			{
-				throw new DataLengthException("input buffer too short");
-			}
+            Check.DataLength(inBytes, inOff, len, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, len, "output buffer too short");
 
-			if ((outOff + len) > outBytes.Length)
-			{
-				throw new DataLengthException("output buffer too short");
-			}
-
-			if (LimitExceeded((uint)len))
-			{
+            if (LimitExceeded((uint)len))
 				throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV");
-			}
 
-			for (int i = 0; i < len; i++)
+            for (int i = 0; i < len; i++)
 			{
 				if (index == 0)
 				{
@@ -175,7 +164,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			}
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 			index = 0;
 			ResetLimitCounter();
diff --git a/crypto/src/crypto/engines/SerpentEngine.cs b/crypto/src/crypto/engines/SerpentEngine.cs
index 92b25acc6..255c204ab 100644
--- a/crypto/src/crypto/engines/SerpentEngine.cs
+++ b/crypto/src/crypto/engines/SerpentEngine.cs
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -48,17 +48,17 @@ namespace Org.BouncyCastle.Crypto.Engines
             this.wKey = MakeWorkingKey(((KeyParameter)parameters).GetKey());
         }
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Serpent"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
@@ -76,7 +76,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception InvalidOperationException if the cipher isn't initialised.
         * @return the number of bytes processed and produced.
         */
-        public  int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]  input,
             int     inOff,
             byte[]  output,
@@ -84,12 +84,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (wKey == null)
                 throw new InvalidOperationException("Serpent not initialised");
-            if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
 
-			if (encrypting)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
             }
@@ -101,7 +100,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
         }
 
@@ -775,5 +774,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             X0 = RotateRight(x0, 13);
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/SkipjackEngine.cs b/crypto/src/crypto/engines/SkipjackEngine.cs
index 3d2a781e6..a45dc9b24 100644
--- a/crypto/src/crypto/engines/SkipjackEngine.cs
+++ b/crypto/src/crypto/engines/SkipjackEngine.cs
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Engines
         * @exception ArgumentException if the parameters argument is
         * inappropriate.
         */
-        public void Init(
+        public virtual void Init(
             bool				forEncryption,
             ICipherParameters	parameters)
         {
@@ -71,22 +71,22 @@ namespace Org.BouncyCastle.Crypto.Engines
             }
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "SKIPJACK"; }
         }
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
         }
 
-        public int ProcessBlock(
+        public virtual int ProcessBlock(
             byte[]	input,
             int		inOff,
             byte[]	output,
@@ -94,12 +94,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (key1 == null)
                 throw new InvalidOperationException("SKIPJACK engine not initialised");
-            if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
 
-			if (encrypting)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
             }
@@ -111,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			return BLOCK_SIZE;
         }
 
-		public void Reset()
+        public virtual void Reset()
         {
         }
 
@@ -135,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return ((g5 << 8) + g6);
         }
 
-        public int EncryptBlock(
+        public virtual int EncryptBlock(
             byte[]      input,
             int         inOff,
             byte[]      outBytes,
@@ -203,7 +202,7 @@ namespace Org.BouncyCastle.Crypto.Engines
             return ((h6 << 8) + h5);
         }
 
-        public int DecryptBlock(
+        public virtual int DecryptBlock(
             byte[]      input,
             int         inOff,
             byte[]      outBytes,
@@ -251,5 +250,4 @@ namespace Org.BouncyCastle.Crypto.Engines
             return BLOCK_SIZE;
         }
     }
-
 }
diff --git a/crypto/src/crypto/engines/TEAEngine.cs b/crypto/src/crypto/engines/TEAEngine.cs
index 582dd0f73..2e1a7002b 100644
--- a/crypto/src/crypto/engines/TEAEngine.cs
+++ b/crypto/src/crypto/engines/TEAEngine.cs
@@ -36,17 +36,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 			_initialised = false;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "TEA"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return block_size;
 		}
@@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the params argument is
 		* inappropriate.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -77,7 +77,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			setKey(p.GetKey());
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]  inBytes,
 			int     inOff,
 			byte[]  outBytes,
@@ -86,18 +86,15 @@ namespace Org.BouncyCastle.Crypto.Engines
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
 
-			if ((inOff + block_size) > inBytes.Length)
-				throw new DataLengthException("input buffer too short");
+            Check.DataLength(inBytes, inOff, block_size, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, block_size, "output buffer too short");
 
-			if ((outOff + block_size) > outBytes.Length)
-				throw new DataLengthException("output buffer too short");
-
-			return _forEncryption
+            return _forEncryption
 				?	encryptBlock(inBytes, inOff, outBytes, outOff)
 				:	decryptBlock(inBytes, inOff, outBytes, outOff);
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
diff --git a/crypto/src/crypto/engines/ThreefishEngine.cs b/crypto/src/crypto/engines/ThreefishEngine.cs
index 954470345..33ff3a421 100644
--- a/crypto/src/crypto/engines/ThreefishEngine.cs
+++ b/crypto/src/crypto/engines/ThreefishEngine.cs
@@ -155,7 +155,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		/// <param name="forEncryption">Initialise for encryption if true, for decryption if false.</param>
 		/// <param name="parameters">an instance of <see cref="TweakableBlockCipherParameters"/> or <see cref="KeyParameter"/> (to
 		///               use a 0 tweak)</param>
-		public void Init(bool forEncryption, ICipherParameters parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
 		{
 			byte[] keyBytes;
 			byte[] tweakBytes;
@@ -266,26 +266,26 @@ namespace Org.BouncyCastle.Crypto.Engines
 			t[4] = t[1];
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "Threefish-" + (blocksizeBytes * 8); }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return blocksizeBytes;
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
-		public int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
+        public virtual int ProcessBlock(byte[] inBytes, int inOff, byte[] outBytes, int outOff)
 		{
 			if ((outOff + blocksizeBytes) > outBytes.Length)
 			{
diff --git a/crypto/src/crypto/engines/TwofishEngine.cs b/crypto/src/crypto/engines/TwofishEngine.cs
index b983d9d31..04a579ced 100644
--- a/crypto/src/crypto/engines/TwofishEngine.cs
+++ b/crypto/src/crypto/engines/TwofishEngine.cs
@@ -293,12 +293,11 @@ namespace Org.BouncyCastle.Crypto.Engines
         {
             if (workingKey == null)
                 throw new InvalidOperationException("Twofish not initialised");
-            if ((inOff + BLOCK_SIZE) > input.Length)
-                throw new DataLengthException("input buffer too short");
-            if ((outOff + BLOCK_SIZE) > output.Length)
-                throw new DataLengthException("output buffer too short");
 
-			if (encrypting)
+            Check.DataLength(input, inOff, BLOCK_SIZE, "input buffer too short");
+            Check.OutputLength(output, outOff, BLOCK_SIZE, "output buffer too short");
+
+            if (encrypting)
             {
                 EncryptBlock(input, inOff, output, outOff);
             }
diff --git a/crypto/src/crypto/engines/VMPCEngine.cs b/crypto/src/crypto/engines/VMPCEngine.cs
index 1c2802a80..852901e36 100644
--- a/crypto/src/crypto/engines/VMPCEngine.cs
+++ b/crypto/src/crypto/engines/VMPCEngine.cs
@@ -92,15 +92,8 @@ namespace Org.BouncyCastle.Crypto.Engines
             byte[]	output,
             int		outOff)
         {
-            if ((inOff + len) > input.Length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
-
-            if ((outOff + len) > output.Length)
-            {
-                throw new DataLengthException("output buffer too short");
-            }
+            Check.DataLength(input, inOff, len, "input buffer too short");
+            Check.OutputLength(output, outOff, len, "output buffer too short");
 
             for (int i = 0; i < len; i++)
             {
diff --git a/crypto/src/crypto/engines/XSalsa20Engine.cs b/crypto/src/crypto/engines/XSalsa20Engine.cs
index fc6630905..2898b46c8 100644
--- a/crypto/src/crypto/engines/XSalsa20Engine.cs
+++ b/crypto/src/crypto/engines/XSalsa20Engine.cs
@@ -13,7 +13,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 	public class XSalsa20Engine 
 		: Salsa20Engine
 	{
-
 		public override string AlgorithmName
 		{
 			get { return "XSalsa20"; }
@@ -65,7 +64,6 @@ namespace Org.BouncyCastle.Crypto.Engines
 			// Counter reset
 			ResetCounter();
 		}
-
 	}
 }
 
diff --git a/crypto/src/crypto/engines/XTEAEngine.cs b/crypto/src/crypto/engines/XTEAEngine.cs
index eb9291775..40d81fbe6 100644
--- a/crypto/src/crypto/engines/XTEAEngine.cs
+++ b/crypto/src/crypto/engines/XTEAEngine.cs
@@ -34,17 +34,17 @@ namespace Org.BouncyCastle.Crypto.Engines
 			_initialised = false;
 		}
 
-		public string AlgorithmName
+		public virtual string AlgorithmName
 		{
 			get { return "XTEA"; }
 		}
 
-		public bool IsPartialBlockOkay
+        public virtual bool IsPartialBlockOkay
 		{
 			get { return false; }
 		}
 
-		public int GetBlockSize()
+        public virtual int GetBlockSize()
 		{
 			return block_size;
 		}
@@ -57,7 +57,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the params argument is
 		* inappropriate.
 		*/
-		public void Init(
+        public virtual void Init(
 			bool				forEncryption,
 			ICipherParameters	parameters)
 		{
@@ -75,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 			setKey(p.GetKey());
 		}
 
-		public int ProcessBlock(
+        public virtual int ProcessBlock(
 			byte[]	inBytes,
 			int		inOff,
 			byte[]	outBytes,
@@ -84,18 +84,15 @@ namespace Org.BouncyCastle.Crypto.Engines
 			if (!_initialised)
 				throw new InvalidOperationException(AlgorithmName + " not initialised");
 
-			if ((inOff + block_size) > inBytes.Length)
-				throw new DataLengthException("input buffer too short");
+            Check.DataLength(inBytes, inOff, block_size, "input buffer too short");
+            Check.OutputLength(outBytes, outOff, block_size, "output buffer too short");
 
-			if ((outOff + block_size) > outBytes.Length)
-				throw new DataLengthException("output buffer too short");
-
-			return _forEncryption
+            return _forEncryption
 				?	encryptBlock(inBytes, inOff, outBytes, outOff)
 				:	decryptBlock(inBytes, inOff, outBytes, outOff);
 		}
 
-		public void Reset()
+        public virtual void Reset()
 		{
 		}
 
diff --git a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
index e870f1c08..2613b902b 100644
--- a/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
+++ b/crypto/src/crypto/generators/RsaKeyPairGenerator.cs
@@ -4,6 +4,7 @@ using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Math.EC.Multiplier;
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
@@ -11,117 +12,127 @@ namespace Org.BouncyCastle.Crypto.Generators
      * an RSA key pair generator.
      */
     public class RsaKeyPairGenerator
-        : IAsymmetricCipherKeyPairGenerator
+        :   IAsymmetricCipherKeyPairGenerator
     {
-        private static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001);
-        private const int DefaultTests = 12;
+        private static readonly int[] SPECIAL_E_VALUES = new int[]{ 3, 5, 17, 257, 65537 };
+        private static readonly int SPECIAL_E_HIGHEST = SPECIAL_E_VALUES[SPECIAL_E_VALUES.Length - 1];
+        private static readonly int SPECIAL_E_BITS = BigInteger.ValueOf(SPECIAL_E_HIGHEST).BitLength;
 
-        private RsaKeyGenerationParameters param;
+        protected static readonly BigInteger One = BigInteger.One;
+        protected static readonly BigInteger DefaultPublicExponent = BigInteger.ValueOf(0x10001);
+        protected const int DefaultTests = 100;
 
-        public void Init(
+        protected RsaKeyGenerationParameters parameters;
+
+        public virtual void Init(
             KeyGenerationParameters parameters)
         {
             if (parameters is RsaKeyGenerationParameters)
             {
-                this.param = (RsaKeyGenerationParameters)parameters;
+                this.parameters = (RsaKeyGenerationParameters)parameters;
             }
             else
             {
-                this.param = new RsaKeyGenerationParameters(
+                this.parameters = new RsaKeyGenerationParameters(
                     DefaultPublicExponent, parameters.Random, parameters.Strength, DefaultTests);
             }
         }
 
-        public AsymmetricCipherKeyPair GenerateKeyPair()
+        public virtual AsymmetricCipherKeyPair GenerateKeyPair()
         {
-            BigInteger p, q, n, d, e, pSub1, qSub1, phi;
-
-            //
-            // p and q values should have a length of half the strength in bits
-            //
-            int strength = param.Strength;
-            int qBitlength = strength >> 1;
-            int pBitlength = strength - qBitlength;
-            int mindiffbits = strength / 3;
-            int minWeight = strength >> 2;
-
-            e = param.PublicExponent;
-
-            // TODO Consider generating safe primes for p, q (see DHParametersHelper.GenerateSafePrimes)
-            // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
-
-            p = ChooseRandomPrime(pBitlength, e);
-
-            //
-            // Generate a modulus of the required length
-            //
             for (;;)
             {
-                q = ChooseRandomPrime(qBitlength, e);
+                //
+                // p and q values should have a length of half the strength in bits
+                //
+                int strength = parameters.Strength;
+                int pBitlength = (strength + 1) / 2;
+                int qBitlength = strength - pBitlength;
+                int mindiffbits = strength / 3;
+                int minWeight = strength >> 2;
 
-                // p and q should not be too close together (or equal!)
-                BigInteger diff = q.Subtract(p).Abs();
-                if (diff.BitLength < mindiffbits)
-                    continue;
+                BigInteger e = parameters.PublicExponent;
+
+                // TODO Consider generating safe primes for p, q (see DHParametersHelper.generateSafePrimes)
+                // (then p-1 and q-1 will not consist of only small factors - see "Pollard's algorithm")
+
+                BigInteger p = ChooseRandomPrime(pBitlength, e);
+                BigInteger q, n;
 
                 //
-                // calculate the modulus
+                // generate a modulus of the required length
                 //
-                n = p.Multiply(q);
-
-                if (n.BitLength != strength)
+                for (;;)
                 {
+                    q = ChooseRandomPrime(qBitlength, e);
+
+                    // p and q should not be too close together (or equal!)
+                    BigInteger diff = q.Subtract(p).Abs();
+                    if (diff.BitLength < mindiffbits)
+                        continue;
+
                     //
-                    // if we get here our primes aren't big enough, make the largest
-                    // of the two p and try again
+                    // calculate the modulus
                     //
-                    p = p.Max(q);
-                    continue;
+                    n = p.Multiply(q);
+
+                    if (n.BitLength != strength)
+                    {
+                        //
+                        // if we get here our primes aren't big enough, make the largest
+                        // of the two p and try again
+                        //
+                        p = p.Max(q);
+                        continue;
+                    }
+
+                    /*
+	                 * Require a minimum weight of the NAF representation, since low-weight composites may
+	                 * be weak against a version of the number-field-sieve for factoring.
+	                 *
+	                 * See "The number field sieve for integers of low weight", Oliver Schirokauer.
+	                 */
+                    if (WNafUtilities.GetNafWeight(n) < minWeight)
+                    {
+                        p = ChooseRandomPrime(pBitlength, e);
+                        continue;
+                    }
+
+                    break;
                 }
 
-                /*
-                 * Require a minimum weight of the NAF representation, since low-weight composites may
-                 * be weak against a version of the number-field-sieve for factoring.
-                 * 
-                 * See "The number field sieve for integers of low weight", Oliver Schirokauer.
-                 */
-                if (WNafUtilities.GetNafWeight(n) < minWeight)
+                if (p.CompareTo(q) < 0)
                 {
-                    p = ChooseRandomPrime(pBitlength, e);
-                    continue;
+                    BigInteger tmp = p;
+                    p = q;
+                    q = tmp;
                 }
 
-                break;
-            }
-
-            if (p.CompareTo(q) < 0)
-            {
-                phi = p;
-                p = q;
-                q = phi;
-            }
-
-            pSub1 = p.Subtract(BigInteger.One);
-            qSub1 = q.Subtract(BigInteger.One);
-            phi = pSub1.Multiply(qSub1);
+                BigInteger pSub1 = p.Subtract(One);
+                BigInteger qSub1 = q.Subtract(One);
+                //BigInteger phi = pSub1.Multiply(qSub1);
+                BigInteger gcd = pSub1.Gcd(qSub1);
+                BigInteger lcm = pSub1.Divide(gcd).Multiply(qSub1);
 
-            //
-            // calculate the private exponent
-            //
-            d = e.ModInverse(phi);
+                //
+                // calculate the private exponent
+                //
+                BigInteger d = e.ModInverse(lcm);
 
-            //
-            // calculate the CRT factors
-            //
-            BigInteger dP, dQ, qInv;
+                if (d.BitLength <= qBitlength)
+                    continue;
 
-            dP = d.Remainder(pSub1);
-            dQ = d.Remainder(qSub1);
-            qInv = q.ModInverse(p);
+                //
+                // calculate the CRT factors
+                //
+                BigInteger dP = d.Remainder(pSub1);
+                BigInteger dQ = d.Remainder(qSub1);
+                BigInteger qInv = q.ModInverse(p);
 
-            return new AsymmetricCipherKeyPair(
-                new RsaKeyParameters(false, n, e),
-                new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+                return new AsymmetricCipherKeyPair(
+                    new RsaKeyParameters(false, n, e),
+                    new RsaPrivateCrtKeyParameters(n, e, d, p, q, dP, dQ, qInv));
+            }
         }
 
         /// <summary>Choose a random prime value for use with RSA</summary>
@@ -130,17 +141,19 @@ namespace Org.BouncyCastle.Crypto.Generators
         /// <returns>a prime p, with (p-1) relatively prime to e</returns>
         protected virtual BigInteger ChooseRandomPrime(int bitlength, BigInteger e)
         {
+            bool eIsKnownOddPrime = (e.BitLength <= SPECIAL_E_BITS) && Arrays.Contains(SPECIAL_E_VALUES, e.IntValue);
+
             for (;;)
             {
-                BigInteger p = new BigInteger(bitlength, 1, param.Random);
+                BigInteger p = new BigInteger(bitlength, 1, parameters.Random);
 
-                if (p.Mod(e).Equals(BigInteger.One))
+                if (p.Mod(e).Equals(One))
                     continue;
 
-                if (!p.IsProbablePrime(param.Certainty))
+                if (!p.IsProbablePrime(parameters.Certainty))
                     continue;
 
-                if (!e.Gcd(p.Subtract(BigInteger.One)).Equals(BigInteger.One))
+                if (!eIsKnownOddPrime && !e.Gcd(p.Subtract(One)).Equals(One))
                     continue;
 
                 return p;
diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index 653d75cb9..e0b1e6b54 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -61,6 +61,7 @@ namespace Org.BouncyCastle.Crypto.Modes
         {
             this.forEncryption = forEncryption;
 
+            ICipherParameters cipherParameters;
             if (parameters is AeadParameters)
             {
                 AeadParameters param = (AeadParameters) parameters;
@@ -68,7 +69,7 @@ namespace Org.BouncyCastle.Crypto.Modes
                 nonce = param.GetNonce();
                 initialAssociatedText = param.GetAssociatedText();
                 macSize = param.MacSize / 8;
-                keyParam = param.Key;
+                cipherParameters = param.Key;
             }
             else if (parameters is ParametersWithIV)
             {
@@ -77,17 +78,25 @@ namespace Org.BouncyCastle.Crypto.Modes
                 nonce = param.GetIV();
                 initialAssociatedText = null;
                 macSize = macBlock.Length / 2;
-                keyParam = param.Parameters;
+                cipherParameters = param.Parameters;
             }
             else
             {
                 throw new ArgumentException("invalid parameters passed to CCM");
             }
 
+            // NOTE: Very basic support for key re-use, but no performance gain from it
+            if (cipherParameters != null)
+            {
+                keyParam = cipherParameters;
+            }
+
             if (nonce == null || nonce.Length < 7 || nonce.Length > 13)
             {
                 throw new ArgumentException("nonce must have length from 7 to 13 octets");
             }
+
+            Reset();
         }
 
         public virtual string AlgorithmName
@@ -128,6 +137,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             byte[]	outBytes,
             int		outOff)
         {
+            Check.DataLength(inBytes, inOff, inLen, "Input buffer too short");
+
             data.Write(inBytes, inOff, inLen);
 
             return 0;
@@ -137,13 +148,11 @@ namespace Org.BouncyCastle.Crypto.Modes
             byte[]	outBytes,
             int		outOff)
         {
-            byte[] enc = ProcessPacket(data.GetBuffer(), 0, (int)data.Position);
-
-            Array.Copy(enc, 0, outBytes, outOff, enc.Length);
+            int len = ProcessPacket(data.GetBuffer(), 0, (int)data.Position, outBytes, outOff);
 
             Reset();
 
-            return enc.Length;
+            return len;
         }
 
         public virtual void Reset()
@@ -161,11 +170,7 @@ namespace Org.BouncyCastle.Crypto.Modes
         */
         public virtual byte[] GetMac()
         {
-            byte[] mac = new byte[macSize];
-
-            Array.Copy(macBlock, 0, mac, 0, mac.Length);
-
-            return mac;
+            return Arrays.CopyOfRange(macBlock, 0, macSize);
         }
 
         public virtual int GetUpdateOutputSize(
@@ -174,7 +179,7 @@ namespace Org.BouncyCastle.Crypto.Modes
             return 0;
         }
 
-        public int GetOutputSize(
+        public virtual int GetOutputSize(
             int len)
         {
             int totalData = (int)data.Length + len;
@@ -187,10 +192,51 @@ namespace Org.BouncyCastle.Crypto.Modes
             return totalData < macSize ? 0 : totalData - macSize;
         }
 
-        public byte[] ProcessPacket(
-            byte[]	input,
-            int		inOff,
-            int		inLen)
+        /**
+         * Process a packet of data for either CCM decryption or encryption.
+         *
+         * @param in data for processing.
+         * @param inOff offset at which data starts in the input array.
+         * @param inLen length of the data in the input array.
+         * @return a byte array containing the processed input..
+         * @throws IllegalStateException if the cipher is not appropriately set up.
+         * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+         */
+        public virtual byte[] ProcessPacket(byte[] input, int inOff, int inLen)
+        {
+            byte[] output;
+
+            if (forEncryption)
+            {
+                output = new byte[inLen + macSize];
+            }
+            else
+            {
+                if (inLen < macSize)
+                    throw new InvalidCipherTextException("data too short");
+
+                output = new byte[inLen - macSize];
+            }
+
+            ProcessPacket(input, inOff, inLen, output, 0);
+
+            return output;
+        }
+
+        /**
+         * Process a packet of data for either CCM decryption or encryption.
+         *
+         * @param in data for processing.
+         * @param inOff offset at which data starts in the input array.
+         * @param inLen length of the data in the input array.
+         * @param output output array.
+         * @param outOff offset into output array to start putting processed bytes.
+         * @return the number of bytes added to output.
+         * @throws IllegalStateException if the cipher is not appropriately set up.
+         * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+         * @throws DataLengthException if output buffer too short.
+         */
+        public virtual int ProcessPacket(byte[] input, int inOff, int inLen, byte[] output, int outOff)
         {
             // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
             // Need to keep the CTR and CBC Mac parts around and reset
@@ -213,42 +259,45 @@ namespace Org.BouncyCastle.Crypto.Modes
             IBlockCipher ctrCipher = new SicBlockCipher(cipher);
             ctrCipher.Init(forEncryption, new ParametersWithIV(keyParam, iv));
 
-            int index = inOff;
-            int outOff = 0;
-            byte[] output;
+            int outputLen;
+            int inIndex = inOff;
+            int outIndex = outOff;
 
             if (forEncryption)
             {
-                output = new byte[inLen + macSize];
+                outputLen = inLen + macSize;
+                Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
 
                 calculateMac(input, inOff, inLen, macBlock);
 
                 ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);   // S0
 
-                while (index < inLen - BlockSize)                   // S1...
+                while (inIndex < (inOff + inLen - BlockSize))                 // S1...
                 {
-                    ctrCipher.ProcessBlock(input, index, output, outOff);
-                    outOff += BlockSize;
-                    index += BlockSize;
+                    ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
+                    outIndex += BlockSize;
+                    inIndex += BlockSize;
                 }
 
                 byte[] block = new byte[BlockSize];
 
-                Array.Copy(input, index, block, 0, inLen - index);
+                Array.Copy(input, inIndex, block, 0, inLen + inOff - inIndex);
 
                 ctrCipher.ProcessBlock(block, 0, block, 0);
 
-                Array.Copy(block, 0, output, outOff, inLen - index);
+                Array.Copy(block, 0, output, outIndex, inLen + inOff - inIndex);
 
-                outOff += inLen - index;
-
-                Array.Copy(macBlock, 0, output, outOff, output.Length - outOff);
+                Array.Copy(macBlock, 0, output, outOff + inLen, macSize);
             }
             else
             {
-                output = new byte[inLen - macSize];
+                if (inLen < macSize)
+                    throw new InvalidCipherTextException("data too short");
 
-                Array.Copy(input, inOff + inLen - macSize, macBlock, 0, macSize);
+                outputLen = inLen - macSize;
+                Check.OutputLength(output, outOff, outputLen, "Output buffer too short.");
+
+                Array.Copy(input, inOff + outputLen, macBlock, 0, macSize);
 
                 ctrCipher.ProcessBlock(macBlock, 0, macBlock, 0);
 
@@ -257,30 +306,30 @@ namespace Org.BouncyCastle.Crypto.Modes
                     macBlock[i] = 0;
                 }
 
-                while (outOff < output.Length - BlockSize)
+                while (inIndex < (inOff + outputLen - BlockSize))
                 {
-                    ctrCipher.ProcessBlock(input, index, output, outOff);
-                    outOff += BlockSize;
-                    index += BlockSize;
+                    ctrCipher.ProcessBlock(input, inIndex, output, outIndex);
+                    outIndex += BlockSize;
+                    inIndex += BlockSize;
                 }
 
                 byte[] block = new byte[BlockSize];
 
-                Array.Copy(input, index, block, 0, output.Length - outOff);
+                Array.Copy(input, inIndex, block, 0, outputLen - (inIndex - inOff));
 
                 ctrCipher.ProcessBlock(block, 0, block, 0);
 
-                Array.Copy(block, 0, output, outOff, output.Length - outOff);
+                Array.Copy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
 
                 byte[] calculatedMacBlock = new byte[BlockSize];
 
-                calculateMac(output, 0, output.Length, calculatedMacBlock);
+                calculateMac(output, outOff, outputLen, calculatedMacBlock);
 
                 if (!Arrays.ConstantTimeAreEqual(macBlock, calculatedMacBlock))
                     throw new InvalidCipherTextException("mac check in CCM failed");
             }
 
-            return output;
+            return outputLen;
         }
 
         private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
diff --git a/crypto/src/crypto/modes/CtsBlockCipher.cs b/crypto/src/crypto/modes/CtsBlockCipher.cs
index a32b49675..ff37844ab 100644
--- a/crypto/src/crypto/modes/CtsBlockCipher.cs
+++ b/crypto/src/crypto/modes/CtsBlockCipher.cs
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Modes
         }
 
         /**
-        * process a single byte, producing an output block if neccessary.
+        * process a single byte, producing an output block if necessary.
         *
         * @param in the input byte.
         * @param out the space for any output that might be produced.
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index 5ccc69b66..624f385b5 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -54,7 +54,6 @@ namespace Org.BouncyCastle.Crypto.Modes
 			blockSize = cipher.GetBlockSize();
 			mac = new CMac(cipher);
 			macBlock = new byte[blockSize];
-			bufBlock = new byte[blockSize * 2];
 			associatedTextMac = new byte[mac.GetMacSize()];
 			nonceMac = new byte[mac.GetMacSize()];
 			this.cipher = new SicBlockCipher(cipher);
@@ -65,7 +64,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 			get { return cipher.GetUnderlyingCipher().AlgorithmName + "/EAX"; }
 		}
 
-		public IBlockCipher GetUnderlyingCipher()
+		public virtual IBlockCipher GetUnderlyingCipher()
 		{
 			return cipher;
 		}
@@ -107,6 +106,8 @@ namespace Org.BouncyCastle.Crypto.Modes
 				throw new ArgumentException("invalid parameters passed to EAX");
 			}
 
+            bufBlock = new byte[forEncryption ? blockSize : (blockSize + macSize)];
+
             byte[] tag = new byte[blockSize];
 
             // Key reuse implemented in CBC mode of underlying CMac
@@ -117,16 +118,10 @@ namespace Org.BouncyCastle.Crypto.Modes
             mac.BlockUpdate(nonce, 0, nonce.Length);
             mac.DoFinal(nonceMac, 0);
 
-            tag[blockSize - 1] = (byte)Tag.H;
-            mac.BlockUpdate(tag, 0, blockSize);
-
-            if (initialAssociatedText != null)
-            {
-                ProcessAadBytes(initialAssociatedText, 0, initialAssociatedText.Length);
-            }
-
-            // Same BlockCipher underlies this and the mac, so reuse last key on cipher 
+            // Same BlockCipher underlies this and the mac, so reuse last key on cipher
             cipher.Init(true, new ParametersWithIV(null, nonceMac));
+
+            Reset();
 		}
 
         private void InitCipher()
@@ -191,16 +186,16 @@ namespace Org.BouncyCastle.Crypto.Modes
         {
             if (cipherInitialized)
             {
-                throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun.");
+                throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
             }
             mac.Update(input);
         }
 
-        public void ProcessAadBytes(byte[] inBytes, int inOff, int len)
+        public virtual void ProcessAadBytes(byte[] inBytes, int inOff, int len)
         {
             if (cipherInitialized)
             {
-                throw new InvalidOperationException("AAD data cannot be added after encryption/decription processing has begun.");
+                throw new InvalidOperationException("AAD data cannot be added after encryption/decryption processing has begun.");
             }
             mac.BlockUpdate(inBytes, inOff, len);
         }
@@ -247,10 +242,11 @@ namespace Org.BouncyCastle.Crypto.Modes
 
 			if (forEncryption)
 			{
-				cipher.ProcessBlock(bufBlock, 0, tmp, 0);
-				cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
+                Check.OutputLength(outBytes, outOff, extra + macSize, "Output buffer too short");
+
+                cipher.ProcessBlock(bufBlock, 0, tmp, 0);
 
-				Array.Copy(tmp, 0, outBytes, outOff, extra);
+                Array.Copy(tmp, 0, outBytes, outOff, extra);
 
 				mac.BlockUpdate(tmp, 0, extra);
 
@@ -264,14 +260,18 @@ namespace Org.BouncyCastle.Crypto.Modes
 			}
 			else
 			{
-				if (extra > macSize)
+                if (extra < macSize)
+                    throw new InvalidCipherTextException("data too short");
+
+                Check.OutputLength(outBytes, outOff, extra - macSize, "Output buffer too short");
+
+                if (extra > macSize)
 				{
 					mac.BlockUpdate(bufBlock, 0, extra - macSize);
 
 					cipher.ProcessBlock(bufBlock, 0, tmp, 0);
-					cipher.ProcessBlock(bufBlock, blockSize, tmp, blockSize);
 
-					Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
+                    Array.Copy(tmp, 0, outBytes, outOff, extra - macSize);
 				}
 
 				CalculateMac();
@@ -331,6 +331,11 @@ namespace Org.BouncyCastle.Crypto.Modes
 
 			if (bufOff == bufBlock.Length)
 			{
+                Check.OutputLength(outBytes, outOff, blockSize, "Output buffer is too short");
+
+                // TODO Could move the ProcessByte(s) calls to here
+//                InitCipher();
+
 				int size;
 
 				if (forEncryption)
@@ -346,10 +351,14 @@ namespace Org.BouncyCastle.Crypto.Modes
 					size = cipher.ProcessBlock(bufBlock, 0, outBytes, outOff);
 				}
 
-				bufOff = blockSize;
-				Array.Copy(bufBlock, blockSize, bufBlock, 0, blockSize);
+                bufOff = 0;
+                if (!forEncryption)
+                {
+                    Array.Copy(bufBlock, blockSize, bufBlock, 0, macSize);
+                    bufOff = macSize;
+                }
 
-				return size;
+                return size;
 			}
 
 			return 0;
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index 2e2ac2eca..8e6120eef 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -284,6 +284,9 @@ namespace Org.BouncyCastle.Crypto.Modes
             byte[]	output,
             int		outOff)
         {
+            if (input.Length < (inOff + len))
+                throw new DataLengthException("Input buffer too short");
+
             int resultLen = 0;
 
             for (int i = 0; i < len; ++i)
@@ -301,6 +304,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         private void OutputBlock(byte[] output, int offset)
         {
+            Check.OutputLength(output, offset, BlockSize, "Output buffer too short");
             if (totalLength == 0)
             {
                 InitCipher();
@@ -325,12 +329,19 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
 
             int extra = bufOff;
-            if (!forEncryption)
+
+            if (forEncryption)
+            {
+                Check.OutputLength(output, outOff, extra + macSize, "Output buffer too short");
+            }
+            else
             {
                 if (extra < macSize)
                     throw new InvalidCipherTextException("data too short");
 
                 extra -= macSize;
+
+                Check.OutputLength(output, outOff, extra, "Output buffer too short");
             }
 
             if (extra > 0)
diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index 54359dfe8..e7dc466e6 100644
--- a/crypto/src/crypto/modes/OCBBlockCipher.cs
+++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -355,6 +355,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 
                 Xor(mainBlock, Pad);
 
+                Check.OutputLength(output, outOff, mainBlockPos, "Output buffer too short");
                 Array.Copy(mainBlock, 0, output, outOff, mainBlockPos);
 
                 if (!forEncryption)
@@ -382,6 +383,8 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             if (forEncryption)
             {
+                Check.OutputLength(output, outOff, resultLen + macSize, "Output buffer too short");
+
                 // Append tag to the message
                 Array.Copy(macBlock, 0, output, outOff + resultLen, macSize);
                 resultLen += macSize;
@@ -431,6 +434,8 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         protected virtual void ProcessMainBlock(byte[] output, int outOff)
         {
+            Check.DataLength(output, outOff, BLOCK_SIZE, "Output buffer too short");
+
             /*
              * OCB-ENCRYPT/OCB-DECRYPT: Process any whole blocks
              */
diff --git a/crypto/src/crypto/modes/gcm/GcmUtilities.cs b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
index 0f241035f..de41d88f4 100644
--- a/crypto/src/crypto/modes/gcm/GcmUtilities.cs
+++ b/crypto/src/crypto/modes/gcm/GcmUtilities.cs
@@ -106,6 +106,29 @@ namespace Org.BouncyCastle.Crypto.Modes.Gcm
             x[3] = r13;
         }
 
+        internal static void Multiply(ulong[] x, ulong[] y)
+        {
+            ulong r00 = x[0], r01 = x[1], r10 = 0, r11 = 0;
+
+            for (int i = 0; i < 2; ++i)
+            {
+                long bits = (long)y[i];
+                for (int j = 0; j < 64; ++j)
+                {
+                    ulong m1 = (ulong)(bits >> 63); bits <<= 1;
+                    r10 ^= (r00 & m1);
+                    r11 ^= (r01 & m1);
+
+                    ulong m2 = (r01 << 63) >> 8;
+                    r01 = (r01 >> 1) | (r00 << 63);
+                    r00 = (r00 >> 1) ^ (m2 & E1L);
+                }
+            }
+
+            x[0] = r10;
+            x[1] = r11;
+        }
+
         // P is the value with only bit i=1 set
         internal static void MultiplyP(uint[] x)
         {
diff --git a/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs b/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
index fb8a92ba3..5d2f8cf15 100644
--- a/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
+++ b/crypto/src/crypto/paddings/PaddedBufferedBlockCipher.cs
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Paddings
 		}
 
 		/**
-		* process a single byte, producing an output block if neccessary.
+		* process a single byte, producing an output block if necessary.
 		*
 		* @param in the input byte.
 		* @param out the space for any output that might be produced.
@@ -178,10 +178,7 @@ namespace Org.BouncyCastle.Crypto.Paddings
 
 			if (outLength > 0)
 			{
-				if ((outOff + outLength) > output.Length)
-				{
-					throw new DataLengthException("output buffer too short");
-				}
+                Check.OutputLength(output, outOff, outLength, "output buffer too short");
 			}
 
 			int resultLen = 0;
@@ -242,7 +239,7 @@ namespace Org.BouncyCastle.Crypto.Paddings
 					{
 						Reset();
 
-						throw new DataLengthException("output buffer too short");
+						throw new OutputLengthException("output buffer too short");
 					}
 
 					resultLen = cipher.ProcessBlock(buf, 0, output, outOff);
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/signers/DsaDigestSigner.cs b/crypto/src/crypto/signers/DsaDigestSigner.cs
index aee713450..086601481 100644
--- a/crypto/src/crypto/signers/DsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/DsaDigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.dsaSigner = signer;
 		}
 
-		public string AlgorithmName
+		public virtual string AlgorithmName
 		{
 			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool							forSigning,
 			ICipherParameters	parameters)
 		{
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte b
 		 */
-		public void Update(
+        public virtual void Update(
 			byte input)
 		{
 			digest.Update(input);
@@ -71,7 +71,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte array in
 		 */
-		public void BlockUpdate(
+        public virtual void BlockUpdate(
 			byte[]	input,
 			int			inOff,
 			int			length)
@@ -83,7 +83,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * Generate a signature for the message we've been loaded with using
 		 * the key we were initialised with.
      */
-		public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
 		{
 			if (!forSigning)
 				throw new InvalidOperationException("DSADigestSigner not initialised for signature generation.");
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[] signature)
 		{
 			if (forSigning)
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <summary>Reset the internal state</summary>
-		public void Reset()
+        public virtual void Reset()
 		{
 			digest.Reset();
 		}
diff --git a/crypto/src/crypto/signers/ECGOST3410Signer.cs b/crypto/src/crypto/signers/ECGOST3410Signer.cs
index 6027aa9b9..28ab79c1c 100644
--- a/crypto/src/crypto/signers/ECGOST3410Signer.cs
+++ b/crypto/src/crypto/signers/ECGOST3410Signer.cs
@@ -18,12 +18,12 @@ namespace Org.BouncyCastle.Crypto.Signers
         private ECKeyParameters key;
         private SecureRandom random;
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "ECGOST3410"; }
         }
 
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          *
          * @param message the message that will be verified later.
          */
-        public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
             byte[] message)
         {
             byte[] mRev = new byte[message.Length]; // conversion is little-endian
@@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * the passed in message (for standard GOST3410 the message should be
          * a GOST3411 hash of the real message to be verified).
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[]		message,
             BigInteger	r,
             BigInteger	s)
diff --git a/crypto/src/crypto/signers/ECNRSigner.cs b/crypto/src/crypto/signers/ECNRSigner.cs
index cae15bdbf..bb21a4994 100644
--- a/crypto/src/crypto/signers/ECNRSigner.cs
+++ b/crypto/src/crypto/signers/ECNRSigner.cs
@@ -19,12 +19,12 @@ namespace Org.BouncyCastle.Crypto.Signers
         private ECKeyParameters	key;
         private SecureRandom	random;
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "ECNR"; }
         }
 
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param digest  the digest to be signed.
          * @exception DataLengthException if the digest is longer than the key allows
          */
-        public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
             byte[] message)
         {
             if (!this.forSigning)
@@ -134,7 +134,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param s       the s value of the signature.
          * @exception DataLengthException if the digest is longer than the key allows
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[]		message,
             BigInteger	r,
             BigInteger	s)
diff --git a/crypto/src/crypto/signers/GOST3410DigestSigner.cs b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
index 58aefa368..bc32808df 100644
--- a/crypto/src/crypto/signers/GOST3410DigestSigner.cs
+++ b/crypto/src/crypto/signers/GOST3410DigestSigner.cs
@@ -26,12 +26,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.digest = digest;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return digest.AlgorithmName + "with" + dsaSigner.AlgorithmName; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forSigning,
 			ICipherParameters	parameters)
 		{
@@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte b
 		 */
-		public void Update(
+        public virtual void Update(
 			byte input)
 		{
 			digest.Update(input);
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		/**
 		 * update the internal digest with the byte array in
 		 */
-		public void BlockUpdate(
+        public virtual void BlockUpdate(
 			byte[]	input,
 			int		inOff,
 			int		length)
@@ -86,7 +86,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * Generate a signature for the message we've been loaded with using
 		 * the key we were initialised with.
 		 */
-		public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
 		{
 			if (!forSigning)
 				throw new InvalidOperationException("GOST3410DigestSigner not initialised for signature generation.");
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <returns>true if the internal state represents the signature described in the passed in array.</returns>
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[] signature)
 		{
 			if (forSigning)
@@ -137,7 +137,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		}
 
 		/// <summary>Reset the internal state</summary>
-		public void Reset()
+        public virtual void Reset()
 		{
 			digest.Reset();
 		}
diff --git a/crypto/src/crypto/signers/GOST3410Signer.cs b/crypto/src/crypto/signers/GOST3410Signer.cs
index 375eeb5cc..f1832ae37 100644
--- a/crypto/src/crypto/signers/GOST3410Signer.cs
+++ b/crypto/src/crypto/signers/GOST3410Signer.cs
@@ -15,12 +15,12 @@ namespace Org.BouncyCastle.Crypto.Signers
 		private Gost3410KeyParameters key;
 		private SecureRandom random;
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return "GOST3410"; }
 		}
 
-		public void Init(
+        public virtual void Init(
 			bool				forSigning,
 			ICipherParameters	parameters)
 		{
@@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 *
 		 * @param message the message that will be verified later.
 		 */
-		public BigInteger[] GenerateSignature(
+        public virtual BigInteger[] GenerateSignature(
 			byte[] message)
 		{
 			byte[] mRev = new byte[message.Length]; // conversion is little-endian
@@ -92,7 +92,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 		 * the passed in message for standard Gost3410 the message should be a
 		 * Gost3411 hash of the real message to be verified.
 		 */
-		public bool VerifySignature(
+        public virtual bool VerifySignature(
 			byte[]		message,
 			BigInteger	r,
 			BigInteger	s)
diff --git a/crypto/src/crypto/signers/GenericSigner.cs b/crypto/src/crypto/signers/GenericSigner.cs
index 5035b454d..a5512176f 100644
--- a/crypto/src/crypto/signers/GenericSigner.cs
+++ b/crypto/src/crypto/signers/GenericSigner.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             this.digest = digest;
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return "Generic(" + engine.AlgorithmName + "/" + digest.AlgorithmName + ")"; }
         }
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * @param parameters
         *            necessary parameters.
         */
-        public void Init(bool forSigning, ICipherParameters parameters)
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
         {
             this.forSigning = forSigning;
 
@@ -62,7 +62,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
         * update the internal digest with the byte b
         */
-        public void Update(byte input)
+        public virtual void Update(byte input)
         {
             digest.Update(input);
         }
@@ -70,7 +70,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
         * update the internal digest with the byte array in
         */
-        public void BlockUpdate(byte[] input, int inOff, int length)
+        public virtual void BlockUpdate(byte[] input, int inOff, int length)
         {
             digest.BlockUpdate(input, inOff, length);
         }
@@ -79,7 +79,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * Generate a signature for the message we've been loaded with using the key
         * we were initialised with.
         */
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
                 throw new InvalidOperationException("GenericSigner not initialised for signature generation.");
@@ -94,7 +94,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         * return true if the internal state represents the signature described in
         * the passed in array.
         */
-        public bool VerifySignature(byte[] signature)
+        public virtual bool VerifySignature(byte[] signature)
         {
             if (forSigning)
                 throw new InvalidOperationException("GenericSigner not initialised for verification");
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             digest.Reset();
         }
diff --git a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
index d4f6c5522..1486656bd 100644
--- a/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
+++ b/crypto/src/crypto/signers/Iso9796d2PssSigner.cs
@@ -120,7 +120,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         {
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
         }
@@ -365,7 +365,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /// <summary> Generate a signature for the loaded message using the key we were
         /// initialised with.
         /// </summary>
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             int digSize = digest.GetDigestSize();
             byte[] m2Hash = new byte[digSize];
diff --git a/crypto/src/crypto/signers/Iso9796d2Signer.cs b/crypto/src/crypto/signers/Iso9796d2Signer.cs
index cfb8942e6..4bb4d17a6 100644
--- a/crypto/src/crypto/signers/Iso9796d2Signer.cs
+++ b/crypto/src/crypto/signers/Iso9796d2Signer.cs
@@ -105,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         {
         }
 
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
         }
@@ -252,7 +252,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         }
 
         /// <summary> update the internal digest with the byte b</summary>
-        public void Update(
+        public virtual void Update(
             byte input)
         {
             digest.Update(input);
@@ -266,7 +266,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         }
 
         /// <summary> update the internal digest with the byte array in</summary>
-        public void BlockUpdate(
+        public virtual void BlockUpdate(
             byte[]	input,
             int		inOff,
             int		length)
diff --git a/crypto/src/crypto/signers/PssSigner.cs b/crypto/src/crypto/signers/PssSigner.cs
index 6900224f3..03890902b 100644
--- a/crypto/src/crypto/signers/PssSigner.cs
+++ b/crypto/src/crypto/signers/PssSigner.cs
@@ -115,7 +115,7 @@ namespace Org.BouncyCastle.Crypto.Signers
 			this.trailer = trailer;
 		}
 
-		public string AlgorithmName
+        public virtual string AlgorithmName
 		{
 			get { return mgfDigest.AlgorithmName + "withRSAandMGF1"; }
 		}
diff --git a/crypto/src/crypto/signers/RsaDigestSigner.cs b/crypto/src/crypto/signers/RsaDigestSigner.cs
index 9af4e7145..d9b19cf6b 100644
--- a/crypto/src/crypto/signers/RsaDigestSigner.cs
+++ b/crypto/src/crypto/signers/RsaDigestSigner.cs
@@ -64,8 +64,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             this.algId = algId;
         }
 
-        [Obsolete]
-        public string AlgorithmName
+        public virtual string AlgorithmName
         {
             get { return digest.AlgorithmName + "withRSA"; }
         }
@@ -76,7 +75,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * @param forSigning true if for signing, false otherwise
          * @param param necessary parameters.
          */
-        public void Init(
+        public virtual void Init(
             bool				forSigning,
             ICipherParameters	parameters)
         {
@@ -106,7 +105,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
          * update the internal digest with the byte b
          */
-        public void Update(
+        public virtual void Update(
             byte input)
         {
             digest.Update(input);
@@ -115,7 +114,7 @@ namespace Org.BouncyCastle.Crypto.Signers
         /**
          * update the internal digest with the byte array in
          */
-        public void BlockUpdate(
+        public virtual void BlockUpdate(
             byte[]	input,
             int		inOff,
             int		length)
@@ -127,7 +126,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * Generate a signature for the message we've been loaded with using
          * the key we were initialised with.
          */
-        public byte[] GenerateSignature()
+        public virtual byte[] GenerateSignature()
         {
             if (!forSigning)
                 throw new InvalidOperationException("RsaDigestSigner not initialised for signature generation.");
@@ -143,7 +142,7 @@ namespace Org.BouncyCastle.Crypto.Signers
          * return true if the internal state represents the signature described
          * in the passed in array.
          */
-        public bool VerifySignature(
+        public virtual bool VerifySignature(
             byte[] signature)
         {
             if (forSigning)
@@ -197,7 +196,7 @@ namespace Org.BouncyCastle.Crypto.Signers
             }
         }
 
-        public void Reset()
+        public virtual void Reset()
         {
             digest.Reset();
         }
diff --git a/crypto/src/crypto/signers/X931Signer.cs b/crypto/src/crypto/signers/X931Signer.cs
new file mode 100644
index 000000000..89f512b78
--- /dev/null
+++ b/crypto/src/crypto/signers/X931Signer.cs
@@ -0,0 +1,234 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Signers
+{
+    /**
+     * X9.31-1998 - signing using a hash.
+     * <p>
+     * The message digest hash, H, is encapsulated to form a byte string as follows
+     * </p>
+     * <pre>
+     * EB = 06 || PS || 0xBA || H || TRAILER
+     * </pre>
+     * where PS is a string of bytes all of value 0xBB of length such that |EB|=|n|, and TRAILER is the ISO/IEC 10118 part number† for the digest. The byte string, EB, is converted to an integer value, the message representative, f.
+     */
+    public class X931Signer
+        :   ISigner
+    {
+        public const int TRAILER_IMPLICIT    = 0xBC;
+        public const int TRAILER_RIPEMD160   = 0x31CC;
+        public const int TRAILER_RIPEMD128   = 0x32CC;
+        public const int TRAILER_SHA1        = 0x33CC;
+        public const int TRAILER_SHA256      = 0x34CC;
+        public const int TRAILER_SHA512      = 0x35CC;
+        public const int TRAILER_SHA384      = 0x36CC;
+        public const int TRAILER_WHIRLPOOL   = 0x37CC;
+        public const int TRAILER_SHA224      = 0x38CC;
+
+        private static readonly IDictionary trailerMap = Platform.CreateHashtable();
+
+        static X931Signer()
+        {
+            trailerMap.Add("RIPEMD128", TRAILER_RIPEMD128);
+            trailerMap.Add("RIPEMD160", TRAILER_RIPEMD160);
+
+            trailerMap.Add("SHA-1", TRAILER_SHA1);
+            trailerMap.Add("SHA-224", TRAILER_SHA224);
+            trailerMap.Add("SHA-256", TRAILER_SHA256);
+            trailerMap.Add("SHA-384", TRAILER_SHA384);
+            trailerMap.Add("SHA-512", TRAILER_SHA512);
+
+            trailerMap.Add("Whirlpool", TRAILER_WHIRLPOOL);
+        }
+
+        private IDigest                     digest;
+        private IAsymmetricBlockCipher      cipher;
+        private RsaKeyParameters            kParam;
+
+        private int         trailer;
+        private int         keyBits;
+        private byte[]      block;
+
+        /**
+         * Generate a signer for the with either implicit or explicit trailers
+         * for ISO9796-2.
+         *
+         * @param cipher base cipher to use for signature creation/verification
+         * @param digest digest to use.
+         * @param implicit whether or not the trailer is implicit or gives the hash.
+         */
+        public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest, bool isImplicit)
+        {
+            this.cipher = cipher;
+            this.digest = digest;
+
+            if (isImplicit)
+            {
+                trailer = TRAILER_IMPLICIT;
+            }
+            else
+            {
+                string name = digest.AlgorithmName;
+                if (!trailerMap.Contains(name))
+                    throw new ArgumentException("no valid trailer", "digest");
+
+                trailer = (int)trailerMap[name];
+            }
+        }
+
+        public virtual string AlgorithmName
+        {
+            get { return digest.AlgorithmName + "with" + cipher.AlgorithmName + "/X9.31"; }
+        }
+
+        /**
+         * Constructor for a signer with an explicit digest trailer.
+         *
+         * @param cipher cipher to use.
+         * @param digest digest to sign with.
+         */
+        public X931Signer(IAsymmetricBlockCipher cipher, IDigest digest)
+            :   this(cipher, digest, false)
+        {
+        }
+
+        public virtual void Init(bool forSigning, ICipherParameters parameters)
+        {
+            kParam = (RsaKeyParameters)parameters;
+
+            cipher.Init(forSigning, kParam);
+
+            keyBits = kParam.Modulus.BitLength;
+
+            block = new byte[(keyBits + 7) / 8];
+
+            Reset();
+        }
+
+        /// <summary> clear possible sensitive data</summary>
+        private void ClearBlock(byte[] block)
+        {
+            Array.Clear(block, 0, block.Length);
+        }
+
+        /**
+         * update the internal digest with the byte b
+         */
+        public virtual void Update(byte b)
+        {
+            digest.Update(b);
+        }
+
+        /**
+         * update the internal digest with the byte array in
+         */
+        public virtual void BlockUpdate(byte[] input, int off, int len)
+        {
+            digest.BlockUpdate(input, off, len);
+        }
+
+        /**
+         * reset the internal state
+         */
+        public virtual void Reset()
+        {
+            digest.Reset();
+        }
+
+        /**
+         * generate a signature for the loaded message using the key we were
+         * initialised with.
+         */
+        public virtual byte[] GenerateSignature()
+        {
+            CreateSignatureBlock();
+
+            BigInteger t = new BigInteger(1, cipher.ProcessBlock(block, 0, block.Length));
+            ClearBlock(block);
+
+            t = t.Min(kParam.Modulus.Subtract(t));
+
+            return BigIntegers.AsUnsignedByteArray((kParam.Modulus.BitLength + 7) / 8, t);
+        }
+
+        private void CreateSignatureBlock()
+        {
+            int digSize = digest.GetDigestSize();
+
+            int delta;
+
+            if (trailer == TRAILER_IMPLICIT)
+            {
+                delta = block.Length - digSize - 1;
+                digest.DoFinal(block, delta);
+                block[block.Length - 1] = (byte)TRAILER_IMPLICIT;
+            }
+            else
+            {
+                delta = block.Length - digSize - 2;
+                digest.DoFinal(block, delta);
+                block[block.Length - 2] = (byte)(trailer >> 8);
+                block[block.Length - 1] = (byte)trailer;
+            }
+
+            block[0] = 0x6b;
+            for (int i = delta - 2; i != 0; i--)
+            {
+                block[i] = (byte)0xbb;
+            }
+            block[delta - 1] = (byte)0xba;
+        }
+
+        /**
+         * return true if the signature represents a ISO9796-2 signature
+         * for the passed in message.
+         */
+        public virtual bool VerifySignature(byte[] signature)
+        {
+            try
+            {
+                block = cipher.ProcessBlock(signature, 0, signature.Length);
+            }
+            catch (Exception)
+            {
+                return false;
+            }
+
+            BigInteger t = new BigInteger(block);
+            BigInteger f;
+
+            if ((t.IntValue & 15) == 12)
+            {
+                 f = t;
+            }
+            else
+            {
+                t = kParam.Modulus.Subtract(t);
+                if ((t.IntValue & 15) == 12)
+                {
+                     f = t;
+                }
+                else
+                {
+                    return false;
+                }
+            }
+
+            CreateSignatureBlock();
+
+            byte[] fBlock = BigIntegers.AsUnsignedByteArray(block.Length, f);
+
+            bool rv = Arrays.ConstantTimeAreEqual(block, fBlock);
+
+            ClearBlock(block);
+            ClearBlock(fBlock);
+
+            return rv;
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsClient.cs b/crypto/src/crypto/tls/AbstractTlsClient.cs
index 771bc004b..046feb78c 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);
 
@@ -263,6 +243,14 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public override TlsCipher GetCipher()
+        {
+            int encryptionAlgorithm = TlsUtilities.GetEncryptionAlgorithm(mSelectedCipherSuite);
+            int macAlgorithm = TlsUtilities.GetMacAlgorithm(mSelectedCipherSuite);
+
+            return mCipherFactory.CreateCipher(mContext, encryptionAlgorithm, macAlgorithm);
+        }
+
         public virtual void NotifyNewSessionTicket(NewSessionTicket newSessionTicket)
         {
         }
diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs
index 83150d37e..e283ee58c 100644
--- a/crypto/src/crypto/tls/AbstractTlsContext.cs
+++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -96,6 +96,15 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length)
         {
+            /*
+             * TODO[session-hash]
+             * 
+             * draft-ietf-tls-session-hash-04 5.4. If a client or server chooses to continue with a full
+             * handshake without the extended master secret extension, [..] the client or server MUST
+             * NOT export any key material based on the new master secret for any subsequent
+             * application-level authentication. In particular, it MUST disable [RFC5705] [..].
+             */
+
             if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length))
                 throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value");
 
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/AbstractTlsServer.cs b/crypto/src/crypto/tls/AbstractTlsServer.cs
index b0a5f0d52..7fe3fcbe5 100644
--- a/crypto/src/crypto/tls/AbstractTlsServer.cs
+++ b/crypto/src/crypto/tls/AbstractTlsServer.cs
@@ -320,6 +320,14 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public override TlsCipher GetCipher()
+        {
+            int encryptionAlgorithm = TlsUtilities.GetEncryptionAlgorithm(mSelectedCipherSuite);
+            int macAlgorithm = TlsUtilities.GetMacAlgorithm(mSelectedCipherSuite);
+
+            return mCipherFactory.CreateCipher(mContext, encryptionAlgorithm, macAlgorithm);
+        }
+
         public virtual NewSessionTicket GetNewSessionTicket()
         {
             /*
diff --git a/crypto/src/crypto/tls/DefaultTlsClient.cs b/crypto/src/crypto/tls/DefaultTlsClient.cs
index ec98413b7..ff7e6da85 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,
@@ -39,395 +48,28 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override TlsKeyExchange GetKeyExchange()
         {
-            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_SEED_CBC_SHA:
-                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
-
-            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
-                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
-
-            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
-                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
-                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
-
-            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-            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 CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
-
-            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
-                return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
-
-            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
-                return CreateRsaKeyExchange();
-
-            default:
-                /*
-                    * Note: internal error here; the TlsProtocol implementation verifies that the
-                    * server-selected cipher suite was in the list of client-offered cipher suites, so if
-                    * we now can't produce an implementation, we shouldn't have offered it!
-                    */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-
-        public override TlsCipher GetCipher()
-        {
-            switch (mSelectedCipherSuite)
+            switch (keyExchangeAlgorithm)
             {
-            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
+            case KeyExchangeAlgorithm.DH_DSS:
+            case KeyExchangeAlgorithm.DH_RSA:
+                return CreateDHKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
+            case KeyExchangeAlgorithm.DHE_DSS:
+            case KeyExchangeAlgorithm.DHE_RSA:
+                return CreateDheKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
+            case KeyExchangeAlgorithm.ECDH_ECDSA:
+            case KeyExchangeAlgorithm.ECDH_RSA:
+                return CreateECDHKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
+            case KeyExchangeAlgorithm.ECDHE_ECDSA:
+            case KeyExchangeAlgorithm.ECDHE_RSA:
+                return CreateECDheKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1);
+            case KeyExchangeAlgorithm.RSA:
+                return CreateRsaKeyExchange();
 
             default:
                 /*
diff --git a/crypto/src/crypto/tls/DefaultTlsServer.cs b/crypto/src/crypto/tls/DefaultTlsServer.cs
index 75f6d8d88..b12c43e1c 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,
@@ -56,472 +72,62 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override TlsCredentials GetCredentials()
         {
-            switch (mSelectedCipherSuite)
-            {
-            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
-                return GetRsaEncryptionCredentials();
-
-            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
-                return GetRsaSignerCredentials();
-
-            default:
-                /*
-                 * Note: internal error here; selected a key exchange we don't implement!
-                 */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-        public override TlsKeyExchange GetKeyExchange()
-        {
-            switch (mSelectedCipherSuite)
+            switch (keyExchangeAlgorithm)
             {
-            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
-                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
-
-            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
-                return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
-
-            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
-                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+                case KeyExchangeAlgorithm.DH_DSS:
+                case KeyExchangeAlgorithm.DHE_DSS:
+                    return GetDsaSignerCredentials();
 
-            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
-                return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+                case KeyExchangeAlgorithm.ECDH_ECDSA:
+                case KeyExchangeAlgorithm.ECDHE_ECDSA:
+                    return GetECDsaSignerCredentials();
 
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+                case KeyExchangeAlgorithm.DHE_RSA:
+                case KeyExchangeAlgorithm.ECDHE_RSA:
+                    return GetRsaSignerCredentials();
 
-            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-                return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+                case KeyExchangeAlgorithm.RSA:
+                    return GetRsaEncryptionCredentials();
 
-            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 CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
-
-            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
-                return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
-
-            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
-                return createRSAKeyExchange();
-
-            default:
-                /*
-                 * Note: internal error here; selected a key exchange we don't implement!
-                 */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                default:
+                    /* Note: internal error here; selected a key exchange we don't implement! */
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
 
-        public override TlsCipher GetCipher()
+        public override TlsKeyExchange GetKeyExchange()
         {
-            switch (mSelectedCipherSuite)
-            {
-            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AEAD_CHACHA20_POLY1305, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_md5);
-
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
+            switch (keyExchangeAlgorithm)
+            {
+            case KeyExchangeAlgorithm.DH_DSS:
+            case KeyExchangeAlgorithm.DH_RSA:
+                return CreateDHKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_md5);
+            case KeyExchangeAlgorithm.DHE_DSS:
+            case KeyExchangeAlgorithm.DHE_RSA:
+                return CreateDheKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
+            case KeyExchangeAlgorithm.ECDH_ECDSA:
+            case KeyExchangeAlgorithm.ECDH_RSA:
+                return CreateECDHKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
+            case KeyExchangeAlgorithm.ECDHE_ECDSA:
+            case KeyExchangeAlgorithm.ECDHE_RSA:
+                return CreateECDheKeyExchange(keyExchangeAlgorithm);
 
-            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
-            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SEED_CBC, MacAlgorithm.hmac_sha1);
+            case KeyExchangeAlgorithm.RSA:
+                return CreateRsaKeyExchange();
 
             default:
                 /*
-                 * Note: internal error here; selected a cipher suite we don't implement!
-                 */
+                    * 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);
             }
         }
@@ -548,7 +154,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                 mServerECPointFormats);
         }
 
-        protected virtual TlsKeyExchange createRSAKeyExchange()
+        protected virtual TlsKeyExchange CreateRsaKeyExchange()
         {
             return new TlsRsaKeyExchange(mSupportedSignatureAlgorithms);
         }
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..76635065c 100644
--- a/crypto/src/crypto/tls/DtlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsClientProtocol.cs
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             DtlsRecordLayer recordLayer = new DtlsRecordLayer(transport, state.clientContext, client, ContentType.handshake);
 
             TlsSession sessionToResume = state.client.GetSessionToResume();
-            if (sessionToResume != null)
+            if (sessionToResume != null && sessionToResume.IsResumable)
             {
                 SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
                 if (sessionParameters != null)
@@ -143,6 +143,8 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 IDictionary sessionServerExtensions = state.sessionParameters.ReadServerExtensions();
 
+                // TODO Check encrypt-then-MAC extension and maybe others
+
                 securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
 
                 securityParameters.masterSecret = Arrays.Clone(state.sessionParameters.MasterSecret);
@@ -317,23 +319,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 +371,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);
@@ -435,8 +432,6 @@ namespace Org.BouncyCastle.Crypto.Tls
             // Integer -> byte[]
             state.clientExtensions = client.GetClientExtensions();
 
-            securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions);
-
             // Cipher Suites (and SCSV)
             {
                 /*
@@ -651,15 +646,6 @@ namespace Org.BouncyCastle.Crypto.Tls
             IDictionary serverExtensions = TlsProtocol.ReadExtensions(buf);
 
             /*
-             * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret"
-             * extension, it MUST include the "extended_master_secret" extension in its ServerHello
-             * message.
-             */
-            bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverExtensions);
-            if (serverSentExtendedMasterSecret != securityParameters.extendedMasterSecret)
-                throw new TlsFatalAlert(AlertDescription.handshake_failure);
-
-            /*
              * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
              * extended client hello message. However, see RFC 5746 exception below. We always include
              * the SCSV, so an Extended Server Hello is always allowed.
@@ -689,15 +675,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                         throw new TlsFatalAlert(AlertDescription.unsupported_extension);
 
                     /*
-                     * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to
-                     * proceed with resumption, the extension does not have any effect. Requiring the
-                     * extension to be included anyway makes the extension negotiation logic easier,
-                     * because it does not depend on whether resumption is accepted or not.
-                     */
-                    if (extType == ExtensionType.extended_master_secret)
-                        continue;
-
-                    /*
                      * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
                      * extensions appearing in the client hello, and send a server hello containing no
                      * extensions[.]
@@ -748,6 +725,8 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 securityParameters.encryptThenMac = serverSentEncryptThenMAC;
 
+                securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(serverExtensions);
+
                 state.maxFragmentLength = EvaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions,
                     AlertDescription.illegal_parameter);
 
@@ -760,6 +739,13 @@ namespace Org.BouncyCastle.Crypto.Tls
                     ExtensionType.session_ticket, AlertDescription.illegal_parameter);
             }
 
+            /*
+             * TODO[session-hash]
+             * 
+             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
+             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             */
+
             state.client.NotifySecureRenegotiation(state.secure_renegotiation);
 
             if (state.clientExtensions != null)
diff --git a/crypto/src/crypto/tls/DtlsReliableHandshake.cs b/crypto/src/crypto/tls/DtlsReliableHandshake.cs
index bf9e61d03..8e4439e67 100644
--- a/crypto/src/crypto/tls/DtlsReliableHandshake.cs
+++ b/crypto/src/crypto/tls/DtlsReliableHandshake.cs
@@ -114,17 +114,17 @@ namespace Org.BouncyCastle.Crypto.Tls
                 {
                     for (; ; )
                     {
-                        int Received = mRecordLayer.Receive(buf, 0, receiveLimit, readTimeoutMillis);
-                        if (Received < 0)
+                        int received = mRecordLayer.Receive(buf, 0, receiveLimit, readTimeoutMillis);
+                        if (received < 0)
                         {
                             break;
                         }
-                        if (Received < 12)
+                        if (received < 12)
                         {
                             continue;
                         }
                         int fragment_length = TlsUtilities.ReadUint24(buf, 9);
-                        if (Received != (fragment_length + 12))
+                        if (received != (fragment_length + 12))
                         {
                             continue;
                         }
diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs
index 3335a9f36..f148eb7d7 100644
--- a/crypto/src/crypto/tls/DtlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs
@@ -538,6 +538,12 @@ namespace Org.BouncyCastle.Crypto.Tls
             TlsServerContextImpl context = state.serverContext;
             SecurityParameters securityParameters = context.SecurityParameters;
 
+            /*
+             * TODO[session-hash]
+             * 
+             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
+             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             */
             securityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(state.clientExtensions);
 
             context.SetClientVersion(client_version);
diff --git a/crypto/src/crypto/tls/ExporterLabel.cs b/crypto/src/crypto/tls/ExporterLabel.cs
index 280321e2a..5970769d7 100644
--- a/crypto/src/crypto/tls/ExporterLabel.cs
+++ b/crypto/src/crypto/tls/ExporterLabel.cs
@@ -30,7 +30,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public const string dtls_srtp = "EXTRACTOR-dtls_srtp";
 
         /*
-         * draft-ietf-tls-session-hash-01
+         * draft-ietf-tls-session-hash-04
          */
         public static readonly string extended_master_secret = "extended master secret";
     }
diff --git a/crypto/src/crypto/tls/ExtensionType.cs b/crypto/src/crypto/tls/ExtensionType.cs
index acee380b6..b4b24f7c3 100644
--- a/crypto/src/crypto/tls/ExtensionType.cs
+++ b/crypto/src/crypto/tls/ExtensionType.cs
@@ -49,7 +49,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public const int encrypt_then_mac = 22;
 
         /*
-         * draft-ietf-tls-session-hash-01
+         * draft-ietf-tls-session-hash-04
          * 
          * NOTE: Early code-point assignment
          */
diff --git a/crypto/src/crypto/tls/PskTlsClient.cs b/crypto/src/crypto/tls/PskTlsClient.cs
index 1f4b0865c..2ef80dcfd 100644
--- a/crypto/src/crypto/tls/PskTlsClient.cs
+++ b/crypto/src/crypto/tls/PskTlsClient.cs
@@ -32,87 +32,15 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override TlsKeyExchange GetKeyExchange()
         {
-            switch (mSelectedCipherSuite)
-            {
-            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
-            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
-
-            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+            switch (keyExchangeAlgorithm)
+            {
+            case KeyExchangeAlgorithm.DHE_PSK:
+            case KeyExchangeAlgorithm.ECDHE_PSK:
+            case KeyExchangeAlgorithm.PSK:
+            case KeyExchangeAlgorithm.RSA_PSK:
+                return CreatePskKeyExchange(keyExchangeAlgorithm);
 
             default:
                 /*
@@ -133,134 +61,6 @@ namespace Org.BouncyCastle.Crypto.Tls
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        public override TlsCipher GetCipher()
-        {
-            switch (mSelectedCipherSuite)
-            {
-            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
-
-            default:
-                /*
-                    * Note: internal error here; the TlsProtocol implementation verifies that the
-                    * server-selected cipher suite was in the list of client-offered cipher suites, so if
-                    * we now can't produce an implementation, we shouldn't have offered it!
-                    */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-            }
-        }
-
         protected virtual TlsKeyExchange CreatePskKeyExchange(int keyExchange)
         {
             return new TlsPskKeyExchange(keyExchange, mSupportedSignatureAlgorithms, mPskIdentity, null, null, mNamedCurves,
diff --git a/crypto/src/crypto/tls/PskTlsServer.cs b/crypto/src/crypto/tls/PskTlsServer.cs
index bdb8b74a5..27d2b8119 100644
--- a/crypto/src/crypto/tls/PskTlsServer.cs
+++ b/crypto/src/crypto/tls/PskTlsServer.cs
@@ -44,301 +44,47 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public override TlsCredentials GetCredentials()
         {
-            switch (mSelectedCipherSuite)
-            {
-            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
-
-            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
-                return null;
+            switch (keyExchangeAlgorithm)
+            {
+                case KeyExchangeAlgorithm.DHE_PSK:
+                case KeyExchangeAlgorithm.ECDHE_PSK:
+                case KeyExchangeAlgorithm.PSK:
+                    return null;
 
-            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
-                return GetRsaEncryptionCredentials();
+                case KeyExchangeAlgorithm.RSA_PSK:
+                    return GetRsaEncryptionCredentials();
 
-            default:
-                /* Note: internal error here; selected a key exchange we don't implement! */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                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_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
-            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
-
-            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
-
-            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
-                return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
-
-            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);
-            }
-        }
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-        public override TlsCipher GetCipher()
-        {
-            switch (mSelectedCipherSuite)
+            switch (keyExchangeAlgorithm)
             {
-            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.cls_3DES_EDE_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
-            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_CCM_8, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.AES_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_CBC, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_128_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_CBC, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.CAMELLIA_256_GCM, MacAlgorithm.cls_null);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.ESTREAM_SALSA20, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha1);
-                    
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha256);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
-            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.NULL, MacAlgorithm.hmac_sha384);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
-            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.RC4_128, MacAlgorithm.hmac_sha1);
-
-            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
-            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
-                return mCipherFactory.CreateCipher(mContext, EncryptionAlgorithm.SALSA20, MacAlgorithm.hmac_sha1);
-
-            default:
-                /*
-                 * Note: internal error here; the TlsProtocol implementation verifies that the
-                 * server-selected cipher suite was in the list of client-offered cipher suites, so if
-                 * we now can't produce an implementation, we shouldn't have offered it!
-                 */
-                throw new TlsFatalAlert(AlertDescription.internal_error);
+                case KeyExchangeAlgorithm.DHE_PSK:
+                case KeyExchangeAlgorithm.ECDHE_PSK:
+                case KeyExchangeAlgorithm.PSK:
+                case KeyExchangeAlgorithm.RSA_PSK:
+                    return CreatePskKeyExchange(keyExchangeAlgorithm);
+
+                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 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..df1607751 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);
         }
@@ -59,22 +68,14 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         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);
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
 
-            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);
+            switch (keyExchangeAlgorithm)
+            {
+            case KeyExchangeAlgorithm.SRP:
+            case KeyExchangeAlgorithm.SRP_DSS:
+            case KeyExchangeAlgorithm.SRP_RSA:
+                return CreateSrpKeyExchange(keyExchangeAlgorithm);
 
             default:
                 /*
@@ -86,38 +87,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        public override TlsCipher GetCipher()
+        public override TlsAuthentication GetAuthentication()
         {
-            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);
-            }
+            /*
+             * 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);
         }
 
         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..f97878380
--- /dev/null
+++ b/crypto/src/crypto/tls/SrpTlsServer.cs
@@ -0,0 +1,121 @@
+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()
+        {
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
+
+            switch (keyExchangeAlgorithm)
+            {
+                case KeyExchangeAlgorithm.SRP:
+                    return null;
+
+                case KeyExchangeAlgorithm.SRP_DSS:
+                    return GetDsaSignerCredentials();
+
+                case KeyExchangeAlgorithm.SRP_RSA:
+                    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()
+        {
+            int keyExchangeAlgorithm = TlsUtilities.GetKeyExchangeAlgorithm(mSelectedCipherSuite);
+
+            switch (keyExchangeAlgorithm)
+            {
+            case KeyExchangeAlgorithm.SRP:
+            case KeyExchangeAlgorithm.SRP_DSS:
+            case KeyExchangeAlgorithm.SRP_RSA:
+                return CreateSrpKeyExchange(keyExchangeAlgorithm);
+
+            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 19e7d71aa..5b9e81b3f 100644
--- a/crypto/src/crypto/tls/TlsClientProtocol.cs
+++ b/crypto/src/crypto/tls/TlsClientProtocol.cs
@@ -58,7 +58,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mRecordStream.Init(mTlsClientContext);
 
             TlsSession sessionToResume = tlsClient.GetSessionToResume();
-            if (sessionToResume != null)
+            if (sessionToResume != null && sessionToResume.IsResumable)
             {
                 SessionParameters sessionParameters = sessionToResume.ExportSessionParameters();
                 if (sessionParameters != null)
@@ -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);
@@ -529,15 +525,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                  */
                 if (this.mConnectionState == CS_END)
                 {
-                    /*
-                     * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
-                     * handshake_failure alert.
-                     */
-                    if (TlsUtilities.IsSsl(Context))
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-
-                    string message = "Renegotiation not supported";
-                    RaiseWarning(AlertDescription.no_renegotiation, message);
+                    RefuseRenegotiation();
                 }
                 break;
             }
@@ -636,15 +624,6 @@ namespace Org.BouncyCastle.Crypto.Tls
             this.mServerExtensions = ReadExtensions(buf);
 
             /*
-             * draft-ietf-tls-session-hash-01 5.2. If a server receives the "extended_master_secret"
-             * extension, it MUST include the "extended_master_secret" extension in its ServerHello
-             * message.
-             */
-            bool serverSentExtendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mServerExtensions);
-            if (serverSentExtendedMasterSecret != mSecurityParameters.extendedMasterSecret)
-                throw new TlsFatalAlert(AlertDescription.handshake_failure);
-
-            /*
              * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
              * extended client hello message.
              * 
@@ -676,15 +655,6 @@ namespace Org.BouncyCastle.Crypto.Tls
                         throw new TlsFatalAlert(AlertDescription.unsupported_extension);
 
                     /*
-                     * draft-ietf-tls-session-hash-01 5.2. Implementation note: if the server decides to
-                     * proceed with resumption, the extension does not have any effect. Requiring the
-                     * extension to be included anyway makes the extension negotiation logic easier,
-                     * because it does not depend on whether resumption is accepted or not.
-                     */
-                    if (extType == ExtensionType.extended_master_secret)
-                        continue;
-
-                    /*
                      * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
                      * extensions appearing in the client hello, and Send a server hello containing no
                      * extensions[.]
@@ -737,8 +707,6 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 sessionClientExtensions = null;
                 sessionServerExtensions = this.mSessionParameters.ReadServerExtensions();
-
-                this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
             }
 
             this.mSecurityParameters.cipherSuite = selectedCipherSuite;
@@ -758,6 +726,8 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 this.mSecurityParameters.encryptThenMac = serverSentEncryptThenMAC;
 
+                this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(sessionServerExtensions);
+
                 this.mSecurityParameters.maxFragmentLength = ProcessMaxFragmentLengthExtension(sessionClientExtensions,
                     sessionServerExtensions, AlertDescription.illegal_parameter);
 
@@ -776,6 +746,13 @@ namespace Org.BouncyCastle.Crypto.Tls
                         AlertDescription.illegal_parameter);
             }
 
+            /*
+             * TODO[session-hash]
+             * 
+             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
+             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             */
+
             if (sessionClientExtensions != null)
             {
                 this.mTlsClient.ProcessServerExtensions(sessionServerExtensions);
@@ -832,8 +809,6 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             this.mClientExtensions = this.mTlsClient.GetClientExtensions();
 
-            this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions);
-
             HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
 
             TlsUtilities.WriteVersion(client_version, message);
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/TlsEccUtilities.cs b/crypto/src/crypto/tls/TlsEccUtilities.cs
index e938b1685..d0d794d0e 100644
--- a/crypto/src/crypto/tls/TlsEccUtilities.cs
+++ b/crypto/src/crypto/tls/TlsEccUtilities.cs
@@ -427,7 +427,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         public static ECPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random, byte[] ecPointFormats,
             ECDomainParameters ecParams, Stream output)
         {
-            AsymmetricCipherKeyPair kp = TlsEccUtilities.GenerateECKeyPair(random, ecParams);
+            AsymmetricCipherKeyPair kp = GenerateECKeyPair(random, ecParams);
 
             ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)kp.Public;
             WriteECPoint(ecPointFormats, ecPublicKey.Q, output);
diff --git a/crypto/src/crypto/tls/TlsProtocol.cs b/crypto/src/crypto/tls/TlsProtocol.cs
index 09838a717..4ea72cd57 100644
--- a/crypto/src/crypto/tls/TlsProtocol.cs
+++ b/crypto/src/crypto/tls/TlsProtocol.cs
@@ -99,6 +99,12 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
         }
 
+        protected virtual void CheckReceivedChangeCipherSpec(bool expected)
+        {
+            if (expected != mReceivedChangeCipherSpec)
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
         protected virtual void CleanupHandshake()
         {
             if (this.mExpectedVerifyData != null)
@@ -162,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();
@@ -259,6 +267,8 @@ namespace Org.BouncyCastle.Crypto.Tls
                          */
                         byte[] buf = mHandshakeQueue.RemoveData(len, 4);
 
+                        CheckReceivedChangeCipherSpec(mConnectionState == CS_END || type == HandshakeType.finished);
+
                         /*
                          * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
                          * starting at client hello up to, but not including, this finished message.
@@ -270,15 +280,20 @@ namespace Org.BouncyCastle.Crypto.Tls
                             break;
                         case HandshakeType.finished:
                         default:
-                            if (type == HandshakeType.finished && this.mExpectedVerifyData == null)
+                        {
+                            TlsContext ctx = Context;
+                            if (type == HandshakeType.finished
+                                && this.mExpectedVerifyData == null
+                                && ctx.SecurityParameters.MasterSecret != null)
                             {
-                                this.mExpectedVerifyData = CreateVerifyData(!Context.IsServer);
+                                this.mExpectedVerifyData = CreateVerifyData(!ctx.IsServer);
                             }
 
                             mRecordStream.UpdateHandshakeData(beginning, 0, 4);
                             mRecordStream.UpdateHandshakeData(buf, 0, len);
                             break;
                         }
+                        }
 
                         /*
                          * Now, parse the message.
@@ -612,6 +627,9 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual void ProcessFinishedMessage(MemoryStream buf)
         {
+            if (mExpectedVerifyData == null)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
             byte[] verify_data = TlsUtilities.ReadFully(mExpectedVerifyData.Length, buf);
 
             AssertEmpty(buf);
@@ -751,6 +769,18 @@ namespace Org.BouncyCastle.Crypto.Tls
             return maxFragmentLength;
         }
 
+        protected virtual void RefuseRenegotiation()
+        {
+            /*
+             * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
+             * handshake_failure alert.
+             */
+            if (TlsUtilities.IsSsl(Context))
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+
+            RaiseWarning(AlertDescription.no_renegotiation, "Renegotiation not supported");
+        }
+
         /**
          * Make sure the InputStream 'buf' now empty. Fail otherwise.
          *
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/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
index b1fb830b6..fd6808382 100644
--- a/crypto/src/crypto/tls/TlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -179,6 +179,11 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                     break;
                 }
+                case CS_END:
+                {
+                    RefuseRenegotiation();
+                    break;
+                }
                 default:
                     throw new TlsFatalAlert(AlertDescription.unexpected_message);
                 }
@@ -496,6 +501,12 @@ namespace Org.BouncyCastle.Crypto.Tls
              */
             this.mClientExtensions = ReadExtensions(buf);
 
+            /*
+             * TODO[session-hash]
+             * 
+             * draft-ietf-tls-session-hash-04 4. Clients and servers SHOULD NOT accept handshakes
+             * that do not use the extended master secret [..]. (and see 5.2, 5.3)
+             */
             this.mSecurityParameters.extendedMasterSecret = TlsExtensionsUtilities.HasExtendedMasterSecretExtension(mClientExtensions);
 
             ContextAdmin.SetClientVersion(client_version);
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..a8c8a2b28 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)
@@ -1490,6 +1531,528 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public static int GetKeyExchangeAlgorithm(int ciphersuite)
+        {
+            switch (ciphersuite)
+            {
+            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+                return KeyExchangeAlgorithm.DH_DSS;
+
+            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
+                return KeyExchangeAlgorithm.DH_RSA;
+
+            case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+                return KeyExchangeAlgorithm.DHE_DSS;
+
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+                return KeyExchangeAlgorithm.DHE_PSK;
+
+            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+                return KeyExchangeAlgorithm.DHE_RSA;
+
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+                return KeyExchangeAlgorithm.ECDH_ECDSA;
+
+            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA:
+                return KeyExchangeAlgorithm.ECDH_RSA;
+
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_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 KeyExchangeAlgorithm.ECDHE_ECDSA;
+
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+                return KeyExchangeAlgorithm.ECDHE_PSK;
+
+            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+                return KeyExchangeAlgorithm.ECDHE_RSA;
+
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+                return KeyExchangeAlgorithm.PSK;
+
+            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
+                return KeyExchangeAlgorithm.RSA;
+
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+                return KeyExchangeAlgorithm.RSA_PSK;
+
+            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 KeyExchangeAlgorithm.SRP;
+
+            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 KeyExchangeAlgorithm.SRP_DSS;
+
+            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 KeyExchangeAlgorithm.SRP_RSA;
+
+            default:
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
+        public static int GetMacAlgorithm(int ciphersuite)
+        {
+            switch (ciphersuite)
+            {
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            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_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_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+            case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384:
+                return MacAlgorithm.cls_null;
+
+            case CipherSuite.TLS_RSA_WITH_NULL_MD5:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
+                return MacAlgorithm.hmac_md5;
+
+            case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DH_RSA_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_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDH_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDH_RSA_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_256_CBC_SHA:
+            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:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+            case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA:
+            case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+            case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+            case CipherSuite.TLS_RSA_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:
+            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 MacAlgorithm.hmac_sha1;
+
+            case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256:
+            case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
+                return MacAlgorithm.hmac_sha256;
+
+            case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384:
+            case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+                return MacAlgorithm.hmac_sha384;
+
+            default:
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+        }
+
         public static ProtocolVersion GetMinimumVersion(int ciphersuite)
         {
             switch (ciphersuite)
diff --git a/crypto/src/pkix/PkixParameters.cs b/crypto/src/pkix/PkixParameters.cs
index 6df1b646f..47d3b5e37 100644
--- a/crypto/src/pkix/PkixParameters.cs
+++ b/crypto/src/pkix/PkixParameters.cs
@@ -745,7 +745,7 @@ namespace Org.BouncyCastle.Pkix
 		}
 
 		/**
-		* Returns the neccessary attributes which must be contained in an attribute
+		* Returns the necessary attributes which must be contained in an attribute
 		* certificate.
 		* <p>
 		* The returned <code>ISet</code> is immutable and contains
@@ -760,7 +760,7 @@ namespace Org.BouncyCastle.Pkix
 		}
 
 		/**
-		* Sets the neccessary which must be contained in an attribute certificate.
+		* Sets the necessary which must be contained in an attribute certificate.
 		* <p>
 		* The <code>ISet</code> must contain <code>String</code>s with the
 		* OIDs.
diff --git a/crypto/src/security/SignerUtilities.cs b/crypto/src/security/SignerUtilities.cs
index c1aea50d6..bd1515147 100644
--- a/crypto/src/security/SignerUtilities.cs
+++ b/crypto/src/security/SignerUtilities.cs
@@ -534,6 +534,26 @@ namespace Org.BouncyCastle.Security
                 return new Iso9796d2Signer(new RsaBlindedEngine(), new RipeMD160Digest(), true);
             }
 
+            if (mechanism.EndsWith("/X9.31"))
+            {
+                string x931 = mechanism.Substring(0, mechanism.Length - "/X9.31".Length);
+                int withPos = x931.IndexOf("WITH");
+                if (withPos > 0)
+                {
+                    int endPos = withPos + "WITH".Length;
+
+                    string digestName = x931.Substring(0, withPos);
+                    IDigest digest = DigestUtilities.GetDigest(digestName);
+
+                    string cipherName = x931.Substring(endPos, x931.Length - endPos);
+                    if (cipherName.Equals("RSA"))
+                    {
+                        IAsymmetricBlockCipher cipher = new RsaBlindedEngine();
+                        return new X931Signer(cipher, digest);
+                    }
+                }
+            }
+
             throw new SecurityUtilityException("Signer " + algorithm + " not recognised.");
         }
 
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 59cff41e1..3fd7154d9 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -256,13 +256,37 @@
     <Compile Include="src\crypto\test\VMPCMacTest.cs" />
     <Compile Include="src\crypto\test\VMPCTest.cs" />
     <Compile Include="src\crypto\test\WhirlpoolDigestTest.cs" />
+    <Compile Include="src\crypto\test\X931SignerTest.cs" />
     <Compile Include="src\crypto\test\XSalsa20Test.cs" />
     <Compile Include="src\crypto\test\XTEATest.cs" />
+    <Compile Include="src\crypto\tls\test\DtlsProtocolTest.cs" />
+    <Compile Include="src\crypto\tls\test\DtlsTestCase.cs" />
+    <Compile Include="src\crypto\tls\test\DtlsTestSuite.cs" />
+    <Compile Include="src\crypto\tls\test\LoggingDatagramTransport.cs" />
+    <Compile Include="src\crypto\tls\test\MockDatagramAssociation.cs" />
+    <Compile Include="src\crypto\tls\test\MockDtlsClient.cs" />
+    <Compile Include="src\crypto\tls\test\MockDtlsServer.cs" />
+    <Compile Include="src\crypto\tls\test\MockPskTlsClient.cs" />
+    <Compile Include="src\crypto\tls\test\MockPskTlsServer.cs" />
+    <Compile Include="src\crypto\tls\test\MockSrpTlsClient.cs" />
+    <Compile Include="src\crypto\tls\test\MockSrpTlsServer.cs" />
     <Compile Include="src\crypto\tls\test\MockTlsClient.cs" />
     <Compile Include="src\crypto\tls\test\MockTlsServer.cs" />
+    <Compile Include="src\crypto\tls\test\NetworkStream.cs" />
+    <Compile Include="src\crypto\tls\test\PipedStream.cs" />
+    <Compile Include="src\crypto\tls\test\PskTlsClientTest.cs" />
     <Compile Include="src\crypto\tls\test\TlsClientTest.cs" />
+    <Compile Include="src\crypto\tls\test\TlsProtocolTest.cs" />
+    <Compile Include="src\crypto\tls\test\TlsPskProtocolTest.cs" />
     <Compile Include="src\crypto\tls\test\TlsServerTest.cs" />
+    <Compile Include="src\crypto\tls\test\TlsSrpProtocolTest.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestCase.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestClientImpl.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestConfig.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestServerImpl.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestSuite.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestUtilities.cs" />
+    <Compile Include="src\crypto\tls\test\UnreliableDatagramTransport.cs" />
     <Compile Include="src\math\ec\test\AllTests.cs" />
     <Compile Include="src\math\ec\test\ECAlgorithmsTest.cs" />
     <Compile Include="src\math\ec\test\ECPointPerformanceTest.cs" />
@@ -1199,9 +1223,17 @@
     <EmbeddedResource Include="data\tls\server.tmpl" />
     <EmbeddedResource Include="data\tls\x509-ca-key.pem" />
     <EmbeddedResource Include="data\tls\x509-ca.pem" />
+    <EmbeddedResource Include="data\tls\x509-client-dsa.pem" />
+    <EmbeddedResource Include="data\tls\x509-client-ecdsa.pem" />
     <EmbeddedResource Include="data\tls\x509-client-key.pem" />
+    <EmbeddedResource Include="data\tls\x509-client-key-dsa.pem" />
+    <EmbeddedResource Include="data\tls\x509-client-key-ecdsa.pem" />
     <EmbeddedResource Include="data\tls\x509-client.pem" />
+    <EmbeddedResource Include="data\tls\x509-server-dsa.pem" />
+    <EmbeddedResource Include="data\tls\x509-server-ecdsa.pem" />
     <EmbeddedResource Include="data\tls\x509-server-key.pem" />
+    <EmbeddedResource Include="data\tls\x509-server-key-dsa.pem" />
+    <EmbeddedResource Include="data\tls\x509-server-key-ecdsa.pem" />
     <EmbeddedResource Include="data\tls\x509-server.pem" />
     <EmbeddedResource Include="data\tls\keystores\client_store.dsa" />
     <EmbeddedResource Include="data\tls\keystores\client_store.rsa" />
diff --git a/crypto/test/data/tls/x509-client-dsa.pem b/crypto/test/data/tls/x509-client-dsa.pem
new file mode 100644
index 000000000..91d9e4415
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-dsa.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmzCCBFOgAwIBAgIMVOBtHi6d3lDIPyoMMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5NTU0
+MloYDzIwMzUwMjEwMDk1NTQyWjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBDbGllbnQwggNHMIICOgYHKoZIzjgEATCCAi0CggEBAPvZZVsYbqUzF5Aq5JCU
+GJtg9+Hgrab/2F8vnEKsYKWtKFY4BCalIbhbZ/e4VPAEV1SlV/GgmtkPVrOutThq
+03j/eo98t82vismks6SMupNoyOiPqDZ+HyNEZFIQV9Ur/1D/tPBQGrD7Fbh5JOmK
+QPCoo46t9Xclfm+8u8bUIhFQV4LIkug5YBm2J8wy5FeaRYno1+4Hp543oD6SkOtF
+aJCp05nfiNRtaZy/LXr22UnSggG9M/zug+u3Bi2R70TI5FJq7TwYHfAlbesbLBjH
+QHvE95WlFxK1YovmziBLfew5RClZ3kgj7f3SL/3WeRy2ittQaObwV27ZNj4enYAD
+qkkCIQDsN/jM9mS5UDmPNRR6Wby2jvJQbHdm6yMqlFiIXVAECQKCAQEAzVrnDdM6
+kPTVALWJNYC279xIEMETimJGP39o3YXMJFlyoEzFYamJPneYlYpY4jGfuetC+jFn
+HouDivtjdf/vAc+og4jaro4NcaDCjzPZBTQuvb0cFhlSbVZsit1IMv/sztJUdPcd
+QKnGP+NfiChBDh8LLskvuVefEia0Ix/xwdK764zVn9oxiP5E2sKMMYnnFoVykbKU
+W43sOONe+P2zOF3VTIOUngszcNDKJsXrFKZ+MtE9FvKduaXBmd0y9Pat7jFCodry
+127mBeIZOKb9h3YMZCf7tQBFpX6o8JX4Ltw9hVRIsspgarli3K18WmOR+6pvAzJx
+IxxiYDbo1PHMAgOCAQUAAoIBAGPk3p3hPR51sTwf2LwDrZ5Fh9tLzFoSh6ojjiR/
+FJsqwbrEEPYi4vozJOBao17qfZayMArcq7QB5eUzBaeeggvyYgYPUIUh8kBZqeLp
+WtWYBXwDqNQiRSAqX0nl35SZEnyyou4kidHFW4SWSbTQw9pkM1gtP2Q7pkxabdjt
+fQTJsilslKFhoV/zWYsa72YJ1GilnDLwPanaBDdpnX84W+TDS2Gy2wb0o15etOTU
+Ly9EoHisZZfdwdIoAd0pJbjSHAJnudB+skL2VfPwfwxQDOTV7gh6XIe5h5bLKR3x
+ruLjNx1ppcTkjKmUruTp7V+/sH9EAS8B9epiNDGwL0s8gX6jdjB0MAwGA1UdEwEB
+/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAweAADAdBgNV
+HQ4EFgQUGvXFed+UN0C5+YrVvcR+7cn9U6swHwYDVR0jBBgwFoAU9mOvr6Pi7+4O
+5bKxRihqeCkyHukwDQYJKoZIhvcNAQELBQADggExAChl15lXRB43Oh+qIeibolYt
+OBCHlUQcQcFzqK556TKNQfhWoRv1ovxbxgPokogfnLfxKdmHrb4E+aF0syxNpXGn
+gBY2G7uyuKjoJnMNsxdTiS6jzps+LlgwHMYsFac0vcJATVz1U8hoR90B4pwkNkXv
+ErVXj4WvHqB5a4gsqPpAslZvhxOgWLvjA+1V4HohspnMVTup3zG6frdmwLWZ+FPI
+LyrEaUaWiilFl83RWuVlExhxGyfLF8u/06OJYHCUNtHT4UQavFzQMz5UwruGor7x
+n8QEE84SXaQxIyWO5T2vnlxZ0+fw/0GXyJS0F0WvJ5WkK+s+y6nXZ8Hq2fDecDWn
+1nw44LpnwGb//010RdtOeAHAQmGuuEf8rf1Xiw990hAYvTzM4cT9/l22vQPR3rg=
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-client-ecdsa.pem b/crypto/test/data/tls/x509-client-ecdsa.pem
new file mode 100644
index 000000000..fd8bc845b
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-ecdsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqzCCAWOgAwIBAgIMVOBhzQE9XhiRAUXpMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5MDcy
+NVoYDzIwMzUwMjEwMDkwNzI1WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBDbGllbnQwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASWdahP6F9xwnD6eABg
+2AwifyDDCw5jy5hGe+Lnvyjok/zdkw2n8lWCFdhInFC7mRcGS7aduF7dykv9C9oU
+mY17o3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1Ud
+DwEB/wQFAwMHgAAwHQYDVR0OBBYEFAg1z6i+u89U0RQrH9ApFWIklZrpMB8GA1Ud
+IwQYMBaAFPZjr6+j4u/uDuWysUYoangpMh7pMA0GCSqGSIb3DQEBCwUAA4IBMQCL
+RJIeoOlVOcseShihLd3OU7NU3tGeLDnEcwPOAPYsDN1c7V/zROTD/0W0nhSVdRfB
+AyxjjDjq3J/MTf1DrouEynoAU8SZL5OnxbceX74U2WsSaJU0PAaw9zTOaKoOuTET
+EVs/OMx00XXhZcu+lPFAKT3YNJKO1Rm7Sy4vnY4jnsuRsk9+D6E3CmDSnKQC/g90
+/0djrDTYpL9Lyd5zzekZPpzX7YgNfmq2X04521+3ahfq4bBMC8GRdAuon2h2FwFX
++aH9cjHHDgxPt5pHkg3Uk+Cl6KhVb2ilS0uYd4XaMPXOUOUOxRpmQ3/7YhNgyyaL
+IOP+fWkbOiFbUTDaOcbBo9y8gvy7+sXuGF5k4nHXNs46Ew1gThQPFtZjqgdiEi+A
+5hYd5P2j0V9L9LprzM79
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-client-key-dsa.pem b/crypto/test/data/tls/x509-client-key-dsa.pem
new file mode 100644
index 000000000..e4e9632c7
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-key-dsa.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICZQIBADCCAjoGByqGSM44BAEwggItAoIBAQD72WVbGG6lMxeQKuSQlBibYPfh
+4K2m/9hfL5xCrGClrShWOAQmpSG4W2f3uFTwBFdUpVfxoJrZD1azrrU4atN4/3qP
+fLfNr4rJpLOkjLqTaMjoj6g2fh8jRGRSEFfVK/9Q/7TwUBqw+xW4eSTpikDwqKOO
+rfV3JX5vvLvG1CIRUFeCyJLoOWAZtifMMuRXmkWJ6NfuB6eeN6A+kpDrRWiQqdOZ
+34jUbWmcvy169tlJ0oIBvTP87oPrtwYtke9EyORSau08GB3wJW3rGywYx0B7xPeV
+pRcStWKL5s4gS33sOUQpWd5II+390i/91nkctorbUGjm8Fdu2TY+Hp2AA6pJAiEA
+7Df4zPZkuVA5jzUUelm8to7yUGx3ZusjKpRYiF1QBAkCggEBAM1a5w3TOpD01QC1
+iTWAtu/cSBDBE4piRj9/aN2FzCRZcqBMxWGpiT53mJWKWOIxn7nrQvoxZx6Lg4r7
+Y3X/7wHPqIOI2q6ODXGgwo8z2QU0Lr29HBYZUm1WbIrdSDL/7M7SVHT3HUCpxj/j
+X4goQQ4fCy7JL7lXnxImtCMf8cHSu+uM1Z/aMYj+RNrCjDGJ5xaFcpGylFuN7Djj
+Xvj9szhd1UyDlJ4LM3DQyibF6xSmfjLRPRbynbmlwZndMvT2re4xQqHa8tdu5gXi
+GTim/Yd2DGQn+7UARaV+qPCV+C7cPYVUSLLKYGq5YtytfFpjkfuqbwMycSMcYmA2
+6NTxzAIEIgIgC6eNrpVR96umhuvIbLz6sy2YwlGFpkK+LLJOL2GmQ4g=
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-client-key-ecdsa.pem b/crypto/test/data/tls/x509-client-key-ecdsa.pem
new file mode 100644
index 000000000..89232e303
--- /dev/null
+++ b/crypto/test/data/tls/x509-client-key-ecdsa.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhANLHM3ugk0ZTEYNx
+uKn3ytAv71M61ChZAjWdC9nW1U7zoAoGCCqGSM49AwEHoUQDQgAElnWoT+hfccJw
++ngAYNgMIn8gwwsOY8uYRnvi578o6JP83ZMNp/JVghXYSJxQu5kXBku2nbhe3cpL
+/QvaFJmNew==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-server-dsa.pem b/crypto/test/data/tls/x509-server-dsa.pem
new file mode 100644
index 000000000..1078dbcbc
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-dsa.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFmjCCBFKgAwIBAgIMVOBtTzLTG8B9QCZvMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5NTYz
+MVoYDzIwMzUwMjEwMDk1NjMxWjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBTZXJ2ZXIwggNGMIICOQYHKoZIzjgEATCCAiwCggEBANblGd/puZYWDvmSMCx0
+AnQQcbmjUhB3MN68/fhkKFwBPU1QhJRiSEVDG1bnsUP55EQKDs1vpiSXgy89P3aT
+cagq0M+urrN97MPeKoBF8sk4o6axruspxD41OyKhW68Nnk54E+3mEPABS4yYpsuE
+hFfaZhOUXdA9aUFWnPN7oo7QlI+F7la8bluz8G8j81aMsEwjBdEKg+2Mp1+33NVa
+IWEegnunVEkb0vJd/WwLZYj+OgmK4fn/qqIdRXIbjeNUvpAtklE5NrARswI0RXuX
+yWxPfBrQSbWk3DQvbuWbEeSUrhYY67YbpPGA5Rng8awpxdXhvzsSmT9c6BlrLfF6
+IN0CIQDWtWHaMmlAVZW9p0Dmsj70sNHJxOYWlqjCEzF7axV3fwKCAQAgFZojuX/n
+leri2E9C6b8cQTof46JZeAeG2Svk8I91cnIv0iXTHfyzWLWDAYnfeuMmt+nMA6/A
+1K94YI1duc3iahLa5U3fOACWJdFtL/CAFVyNth7lDeHbWTFjM5HgA76pzmAfvFtZ
+pcJp1AUZZB+3coKSr6ZrMagt1HhoefGyo7JkPpsxGiFEKB79rXkFi0AEsSnbduFE
+czrg8DBdXm9//yUQnTw2pphcwLZt6t6Eq3Wrpu364Dn+hJegpLfA6uOhtRJw+7xv
+5jkBbvIxp1PLZEn16qmPKMs3Im4QUa2u5F0nLYUGHi01i5OSn0UWnom8EcDis/0Q
+CrkyBaiMaNkkA4IBBQACggEAQob2cpVeV4uwnX8XutQLro6BkEFMhpaQLUzbI97J
+Nh1OUMnW3K2KCCdhygTYfH3j2S8ys4W30zutHyhiApUdYUZKxNXXu4+NPoLwCyAE
+rrN5db5QgVx8aOG/Gfmp4POMpeGKioXgOsSWpUB0uyedgT4sszq/vuZc455HiS07
+LTlaJjeIQFtKdVYXGKNWqP/WLRnjSY9IXA5hxBXvlNAVGS/M4TvBWURUJF33THTq
+ioS0DYJ9Ikyn2fsgLgSRRyNU7Y5945yUhY5p6E6Njk0ZBB+2YUtB6TQhPvEY/G48
+xwH4hTZppmmfu2UIE+YlsWv10buTOT+mxU/5LjDI6RQM2KN2MHQwDAYDVR0TAQH/
+BAIwADATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1Ud
+DgQWBBS8bllTQWk2fm2R6XSZEIoRTd6/+zAfBgNVHSMEGDAWgBT2Y6+vo+Lv7g7l
+srFGKGp4KTIe6TANBgkqhkiG9w0BAQsFAAOCATEAptRUkWUT+MdvD7v733B6Gad+
+wO0G9n7Au7ZsS6NVPxc4DrozFyyhPU1uOl1koUyYQP52V1lkm1+rS7tQXm4+32iz
+bLwDCYn4LD1IZQ2hTNY/g/U0oA1dp3ZOJeydZDZ9Bm1NWYeWqfHk/ZiHMdrEdIUo
+7Zkg1T0ZBvmBXTUJzeDqIV0fMotFOQBJg5avyCcX56jHXbu2AmGQsQf6NoniXG37
+wY/xiPSUUmOKjAU/B4tc24ADa5r37JzlHbpOGe1nTb8AYxjyPDdjO4Um2BugKE+K
+rq+NJMg8Ej2sXLOnS2FweCoIR+RXGAUUBizlHMKJ2UGQ5sCX7U86qvniMcO8CsXR
+7eSkBTYpxVLENxb2zf3g22972QWuL9eHyQfuO5dca2JnenZv1aIrhTH59fX8Sw==
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-server-ecdsa.pem b/crypto/test/data/tls/x509-server-ecdsa.pem
new file mode 100644
index 000000000..221fce99a
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-ecdsa.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICqzCCAWOgAwIBAgIMVOBhgRR/e3iAhKcbMA0GCSqGSIb3DQEBCwUAMCMxITAf
+BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAiGA8yMDE1MDIxNTA5MDYw
+OVoYDzIwMzUwMjEwMDkwNjA5WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVz
+dCBTZXJ2ZXIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQzME1njDHqizStYiqf
+xC45McfdqtcDFCsyik7g7JwwGBzDI3WTP5stb8gLHwGARqkaTIDr/wau+N12bIxp
+Tj6Co3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+DwEB/wQFAwMHgAAwHQYDVR0OBBYEFOaifCD845nra+asxN59LzV1LlkxMB8GA1Ud
+IwQYMBaAFPZjr6+j4u/uDuWysUYoangpMh7pMA0GCSqGSIb3DQEBCwUAA4IBMQCc
+wMTPTd/82sDeJkN6B8M2AFfLf/0uw01/2I/UQp2rJCJu1Yps9S1foHz939o4qJA3
++kieb3eVE8rcMQHMv42hagj09LmYY1Unzdu8xJDMq7gDIph72Fjy5YFqb9je79C4
+JPOadGsAsG9OY9DxTKsHyiX3Mqztx1hUQ0mNiAyaFEDGus+ZhSGMdHj0uR1LqZL9
+9OgszZY0UomZfhwnPg2sHumdh+13RZ38w33b43LpCcyu7v05szquqJ8lnol8j10r
+6sH1xXZmb6OJIXZeHTnX5Dv1z0/R8qyG3gyVU7sP+8KVk529Q/YjIJ8YEV1LSERW
+9cKbI+8DMxVE/z01gd0YLqpPcDSfzGUTww+lMWyGQ+KDUD2bC2O4LbZhzT+ZxCPN
++mw+T5KV78wcG02PThMf
+-----END CERTIFICATE-----
diff --git a/crypto/test/data/tls/x509-server-key-dsa.pem b/crypto/test/data/tls/x509-server-key-dsa.pem
new file mode 100644
index 000000000..d6073b31f
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-key-dsa.pem
@@ -0,0 +1,15 @@
+-----BEGIN PRIVATE KEY-----
+MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQDW5Rnf6bmWFg75kjAsdAJ0EHG5
+o1IQdzDevP34ZChcAT1NUISUYkhFQxtW57FD+eRECg7Nb6Ykl4MvPT92k3GoKtDP
+rq6zfezD3iqARfLJOKOmsa7rKcQ+NTsioVuvDZ5OeBPt5hDwAUuMmKbLhIRX2mYT
+lF3QPWlBVpzze6KO0JSPhe5WvG5bs/BvI/NWjLBMIwXRCoPtjKdft9zVWiFhHoJ7
+p1RJG9LyXf1sC2WI/joJiuH5/6qiHUVyG43jVL6QLZJROTawEbMCNEV7l8lsT3wa
+0Em1pNw0L27lmxHklK4WGOu2G6TxgOUZ4PGsKcXV4b87Epk/XOgZay3xeiDdAiEA
+1rVh2jJpQFWVvadA5rI+9LDRycTmFpaowhMxe2sVd38CggEAIBWaI7l/55Xq4thP
+Qum/HEE6H+OiWXgHhtkr5PCPdXJyL9Il0x38s1i1gwGJ33rjJrfpzAOvwNSveGCN
+XbnN4moS2uVN3zgAliXRbS/wgBVcjbYe5Q3h21kxYzOR4AO+qc5gH7xbWaXCadQF
+GWQft3KCkq+mazGoLdR4aHnxsqOyZD6bMRohRCge/a15BYtABLEp23bhRHM64PAw
+XV5vf/8lEJ08NqaYXMC2berehKt1q6bt+uA5/oSXoKS3wOrjobUScPu8b+Y5AW7y
+MadTy2RJ9eqpjyjLNyJuEFGtruRdJy2FBh4tNYuTkp9FFp6JvBHA4rP9EAq5MgWo
+jGjZJAQiAiBhYRIk49XUmvOqUx953A+n8/KWUfyZyTHPkcIXBvW+Ig==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/data/tls/x509-server-key-ecdsa.pem b/crypto/test/data/tls/x509-server-key-ecdsa.pem
new file mode 100644
index 000000000..01fe38567
--- /dev/null
+++ b/crypto/test/data/tls/x509-server-key-ecdsa.pem
@@ -0,0 +1,6 @@
+-----BEGIN PRIVATE KEY-----
+MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhALoiXMjOXuPkNVT6
+g/8PnBmXj8wEvWXCyrpO+fh9EMTdoAoGCCqGSM49AwEHoUQDQgAEMzBNZ4wx6os0
+rWIqn8QuOTHH3arXAxQrMopO4OycMBgcwyN1kz+bLW/ICx8BgEapGkyA6/8Grvjd
+dmyMaU4+gg==
+-----END PRIVATE KEY-----
diff --git a/crypto/test/lib/nunit.core.dll b/crypto/test/lib/nunit.core.dll
index f58c07d42..8d99c637d 100644
--- a/crypto/test/lib/nunit.core.dll
+++ b/crypto/test/lib/nunit.core.dll
Binary files differdiff --git a/crypto/test/lib/nunit.core.interfaces.dll b/crypto/test/lib/nunit.core.interfaces.dll
index cdf50b687..70a76b21c 100644
--- a/crypto/test/lib/nunit.core.interfaces.dll
+++ b/crypto/test/lib/nunit.core.interfaces.dll
Binary files differdiff --git a/crypto/test/lib/nunit.framework.dll b/crypto/test/lib/nunit.framework.dll
index c7b1c65d9..ba484ba8c 100644
--- a/crypto/test/lib/nunit.framework.dll
+++ b/crypto/test/lib/nunit.framework.dll
Binary files differdiff --git a/crypto/test/src/asn1/test/AllTests.cs b/crypto/test/src/asn1/test/AllTests.cs
index a1ae8fd48..ad2f90362 100644
--- a/crypto/test/src/asn1/test/AllTests.cs
+++ b/crypto/test/src/asn1/test/AllTests.cs
@@ -9,24 +9,22 @@ namespace Org.BouncyCastle.Asn1.Tests
     {
         public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+		public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("ASN.1 tests");
-
-			suite.Add(new AllTests());
-
-			// TODO Add these tests to RegressionTest list
-			suite.Add(new Asn1SequenceParserTest());
-			suite.Add(new OctetStringTest());
-			suite.Add(new ParseTest());
-			suite.Add(new TimeTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("ASN.1 tests");
+			    // TODO Add these tests to RegressionTest list
+			    suite.Add(new Asn1SequenceParserTest());
+			    suite.Add(new OctetStringTest());
+			    suite.Add(new ParseTest());
+			    suite.Add(new TimeTest());
+			    return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/cms/test/AllTests.cs b/crypto/test/src/cms/test/AllTests.cs
index 1ce3b7c8d..b7ac7644b 100644
--- a/crypto/test/src/cms/test/AllTests.cs
+++ b/crypto/test/src/cms/test/AllTests.cs
@@ -9,27 +9,26 @@ namespace Org.BouncyCastle.Cms.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("CMS Tests");
-
-			suite.Add(new CompressedDataTest());
-            suite.Add(new CompressedDataStreamTest());
-			suite.Add(new EnvelopedDataTest());
-			suite.Add(new EnvelopedDataStreamTest());
-			suite.Add(new Rfc4134Test());
-			suite.Add(new SignedDataTest());
-			suite.Add(new SignedDataStreamTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("CMS Tests");
+                suite.Add(new CompressedDataTest());
+                suite.Add(new CompressedDataStreamTest());
+                suite.Add(new EnvelopedDataTest());
+                suite.Add(new EnvelopedDataStreamTest());
+                suite.Add(new Rfc4134Test());
+                suite.Add(new SignedDataTest());
+                suite.Add(new SignedDataStreamTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/crypto/io/test/AllTests.cs b/crypto/test/src/crypto/io/test/AllTests.cs
index 0296a2dc0..5c8c759f9 100644
--- a/crypto/test/src/crypto/io/test/AllTests.cs
+++ b/crypto/test/src/crypto/io/test/AllTests.cs
@@ -7,21 +7,20 @@ namespace Org.BouncyCastle.Crypto.IO.Tests
 {
 	public class AllTests
 	{
-		public static void Main(
-			string[] args)
-		{
-//            junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("IO tests");
-
-			suite.Add(new CipherStreamTest());
-
-			return suite;
-		}
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("IO tests");
+                suite.Add(new CipherStreamTest());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/crypto/test/AllTests.cs b/crypto/test/src/crypto/test/AllTests.cs
index 1cb1b965d..3d8ef5602 100644
--- a/crypto/test/src/crypto/test/AllTests.cs
+++ b/crypto/test/src/crypto/test/AllTests.cs
@@ -10,19 +10,24 @@ namespace Org.BouncyCastle.Crypto.Tests
 	[TestFixture]
 	public class AllTests
 	{
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("Lightweight Crypto Tests");
-				suite.Add(new AllTests());
-		        suite.Add(new GcmReorderTest());
-				return suite;
-			}
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		[Test]
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("Lightweight Crypto Tests");
+                suite.Add(new AllTests());
+                suite.Add(new GcmReorderTest());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestCrypto()
 		{
 			foreach (Org.BouncyCastle.Utilities.Test.ITest test in RegressionTest.tests)
@@ -35,13 +40,5 @@ namespace Org.BouncyCastle.Crypto.Tests
 				}
 			}
 		}
-
-        public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }
diff --git a/crypto/test/src/crypto/test/CCMTest.cs b/crypto/test/src/crypto/test/CCMTest.cs
index 4a54fb4f9..8c46e11e7 100644
--- a/crypto/test/src/crypto/test/CCMTest.cs
+++ b/crypto/test/src/crypto/test/CCMTest.cs
@@ -81,7 +81,38 @@ namespace Org.BouncyCastle.Crypto.Tests
 			//
 			checkVectors(4, ccm, K4, 112, N4, A4, A4, T5, C5);
 
-			//
+            // decryption with output specified, non-zero offset.
+            ccm.Init(false, new AeadParameters(new KeyParameter(K2), 48, N2, A2));
+
+            byte[] inBuf = new byte[C2.Length + 10];
+            byte[] outBuf = new byte[ccm.GetOutputSize(C2.Length) + 10];
+
+            Array.Copy(C2, 0, inBuf, 10, C2.Length);
+
+            int len = ccm.ProcessPacket(inBuf, 10, C2.Length, outBuf, 10);
+            byte[] output = ccm.ProcessPacket(C2, 0, C2.Length);
+
+            if (len != output.Length || !isEqual(output, outBuf, 10))
+            {
+                Fail("decryption output incorrect");
+            }
+
+            // encryption with output specified, non-zero offset.
+            ccm.Init(true, new AeadParameters(new KeyParameter(K2), 48, N2, A2));
+
+            int inLen = len;
+            inBuf = outBuf;
+            outBuf = new byte[ccm.GetOutputSize(inLen) + 10];
+
+            len = ccm.ProcessPacket(inBuf, 10, inLen, outBuf, 10);
+            output = ccm.ProcessPacket(inBuf, 10, inLen);
+
+            if (len != output.Length || !isEqual(output, outBuf, 10))
+            {
+                Fail("encryption output incorrect");
+            }
+
+            //
 			// exception tests
 			//
 
@@ -121,6 +152,17 @@ namespace Org.BouncyCastle.Crypto.Tests
 			}
 		}
 
+        private bool isEqual(byte[] exp, byte[] other, int off)
+        {
+            for (int i = 0; i != exp.Length; i++)
+            {
+                if (exp[i] != other[off + i])
+                    return false;
+            }
+
+            return true;
+        }
+
 		private void checkVectors(
 			int count,
 			CcmBlockCipher ccm,
@@ -203,7 +245,8 @@ namespace Org.BouncyCastle.Crypto.Tests
 
 			if (!AreEqual(p, dec))
 			{
-                Fail("decrypted stream fails to match in test " + count + " with " + additionalDataType);
+                Fail("decrypted stream fails to match in test " + count + " with " + additionalDataType,
+                    Hex.ToHexString(p), Hex.ToHexString(dec));
             }
 
 			if (!AreEqual(t, ccm.GetMac()))
diff --git a/crypto/test/src/crypto/test/RegressionTest.cs b/crypto/test/src/crypto/test/RegressionTest.cs
index 27d6bb0e9..0b7a0f72d 100644
--- a/crypto/test/src/crypto/test/RegressionTest.cs
+++ b/crypto/test/src/crypto/test/RegressionTest.cs
@@ -118,7 +118,8 @@ namespace Org.BouncyCastle.Crypto.Tests
             new SipHashTest(),
             new Poly1305Test(),
             new OcbTest(),
-            new SM3DigestTest()
+            new SM3DigestTest(),
+            new X931SignerTest()
         };
 
         public static void Main(
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/test/X931SignerTest.cs b/crypto/test/src/crypto/test/X931SignerTest.cs
new file mode 100644
index 000000000..d03cbc8e4
--- /dev/null
+++ b/crypto/test/src/crypto/test/X931SignerTest.cs
@@ -0,0 +1,145 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.Test;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tests
+{
+	[TestFixture]
+	public class X931SignerTest
+		:   SimpleTest
+    {
+        public override string Name
+        {
+            get { return "X931Signer"; }
+        }
+
+        public override void PerformTest()
+        {
+            BigInteger rsaPubMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPubExp = new BigInteger(Base64.Decode("EQ=="));
+            BigInteger rsaPrivMod = new BigInteger(Base64.Decode("AIASoe2PQb1IP7bTyC9usjHP7FvnUMVpKW49iuFtrw/dMpYlsMMoIU2jupfifDpdFxIktSB4P+6Ymg5WjvHKTIrvQ7SR4zV4jaPTu56Ys0pZ9EDA6gb3HLjtU+8Bb1mfWM+yjKxcPDuFjwEtjGlPHg1Vq+CA9HNcMSKNn2+tW6qt"));
+            BigInteger rsaPrivDP = new BigInteger(Base64.Decode("JXzfzG5v+HtLJIZqYMUefJfFLu8DPuJGaLD6lI3cZ0babWZ/oPGoJa5iHpX4Ul/7l3s1PFsuy1GhzCdOdlfRcQ=="));
+            BigInteger rsaPrivDQ = new BigInteger(Base64.Decode("YNdJhw3cn0gBoVmMIFRZzflPDNthBiWy/dUMSRfJCxoZjSnr1gysZHK01HteV1YYNGcwPdr3j4FbOfri5c6DUQ=="));
+            BigInteger rsaPrivExp = new BigInteger(Base64.Decode("DxFAOhDajr00rBjqX+7nyZ/9sHWRCCp9WEN5wCsFiWVRPtdB+NeLcou7mWXwf1Y+8xNgmmh//fPV45G2dsyBeZbXeJwB7bzx9NMEAfedchyOwjR8PYdjK3NpTLKtZlEJ6Jkh4QihrXpZMO4fKZWUm9bid3+lmiq43FwW+Hof8/E="));
+            BigInteger rsaPrivP = new BigInteger(Base64.Decode("AJ9StyTVW+AL/1s7RBtFwZGFBgd3zctBqzzwKPda6LbtIFDznmwDCqAlIQH9X14X7UPLokCDhuAa76OnDXb1OiE="));
+            BigInteger rsaPrivQ = new BigInteger(Base64.Decode("AM3JfD79dNJ5A3beScSzPtWxx/tSLi0QHFtkuhtSizeXdkv5FSba7lVzwEOGKHmW829bRoNxThDy4ds1IihW1w0="));
+            BigInteger rsaPrivQinv = new BigInteger(Base64.Decode("Lt0g7wrsNsQxuDdB8q/rH8fSFeBXMGLtCIqfOec1j7FEIuYA/ACiRDgXkHa0WgN7nLXSjHoy630wC5Toq8vvUg=="));
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, rsaPubMod, rsaPubExp);
+            RsaPrivateCrtKeyParameters rsaPrivate = new RsaPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv);
+
+            byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 };
+
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(true, rsaPrivate);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            byte[] sig = signer.GenerateSignature();
+
+            signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+            signer.Init(false, rsaPublic);
+            signer.BlockUpdate(msg, 0, msg.Length);
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("X9.31 Signer failed.");
+            }
+
+            ShouldPassSignatureTest1();
+            ShouldPassSignatureTest2();
+            ShouldPassSignatureTest3();
+        }
+
+        private void ShouldPassSignatureTest1()
+        {
+            BigInteger n = new BigInteger("c9be1b28f8caccca65d86cc3c9bbcc13eccc059df3b80bd2292b811eff3aa0dd75e1e85c333b8e3fa9bed53bb20f5359ff4e6900c5e9a388e3a4772a583a79e2299c76582c2b27694b65e9ba22e66bfb817f8b70b22206d7d8ae488c86dbb7137c26d5eff9b33c90e6cee640630313b7a715802e15142fef498c404a8de19674974785f0f852e2d470fe85a2e54ffca9f5851f672b71df691785a5cdabe8f14aa628942147de7593b2cf962414a5b59c632c4e14f1768c0ab2e9250824beea60a3529f11bf5e070ce90a47686eb0be1086fb21f0827f55295b4a48307db0b048c05a4aec3f488c576ca6f1879d354224c7e84cbcd8e76dd217a3de54dba73c35", 16);
+            BigInteger e = new BigInteger("e75b1b", 16);
+            byte[] msg = Hex.Decode("5bb0d1c0ef9b5c7af2477fe08d45523d3842a4b2db943f7033126c2a7829bacb3d2cfc6497ec91688189e81b7f8742488224ba320ce983ce9480722f2cc5bc42611f00bb6311884f660ccc244788378673532edb05284fd92e83f6f6dab406209032e6af9a33c998677933e32d6fb95fd27408940d7728f9c9c40267ca1d20ce");
+            byte[] sig = Hex.Decode("0fe8bb8e3109a1eb7489ef35bf4c1a0780071da789c8bd226a4170538eafefdd30b732d628f0e87a0b9450051feae9754d4fb61f57862d10f0bacc4f660d13281d0cd1141c006ade5186ff7d961a4c6cd0a4b352fc1295c5afd088f80ac1f8e192ef116a010a442655fe8ff5eeacea15807906fb0f0dfa86e680d4c005872357f7ece9aa4e20b15d5f709b30f08648ecaa34f2fbf54eb6b414fa2ff6f87561f70163235e69ccb4ac82a2e46d3be214cc2ef5263b569b2d8fd839b21a9e102665105ea762bda25bb446cfd831487a6b846100dee113ae95ae64f4af22c428c87bab809541c962bb3a56d4c86588e0af4ebc7fcc66dadced311051356d3ea745f7");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 1 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest2()
+        {
+            BigInteger n = new BigInteger("b746ba6c3c0be64bbe33aa55b2929b0af4e86d773d44bfe5914db9287788c4663984b61a418d2eecca30d752ff6b620a07ec72eeb2b422d2429da352407b99982800b9dd7697be6a7b1baa98ca5f4fc2fe33400f20b9dba337ac25c987804165d4a6e0ee4d18eabd6de5abdfe578cae6713ff91d16c80a5bb20217fe614d9509e75a43e1825327b9da8f0a9f6eeaa1c04b69fb4bacc073569fff4ab491becbe6d0441d437fc3fa823239c4a0f75321666b68dd3f66e2dd394089a15bcc288a68a4eb0a48e17d639743b9dea0a91cc35820544732aff253f8ca9967c609dc01c2f8cd0313a7a91cfa94ff74289a1d2b6f19d1811f4b9a65f4cce9e5759b4cc64f", 16);
+            BigInteger e = new BigInteger("dcbbdb", 16);
+            byte[] msg = Hex.Decode("a5d3c8a060f897bbbc20ae0955052f37fbc70986b6e11c65075c9f457142bfa93856897c69020aa81a91b5e4f39e05cdeecc63395ab849c8262ca8bc5c96870aecb8edb0aba0024a9bdb71e06de6100344e5c318bc979ef32b8a49a8278ba99d4861bce42ebbc5c8c666aaa6cac39aff8779f2cae367620f9edd4cb1d80b6c8c");
+            byte[] sig = Hex.Decode("39fbbd1804c689a533b0043f84da0f06081038c0fbf31e443e46a05e58f50de5198bbca40522afefaba3aed7082a6cb93b1da39f1f5a42246bf64930781948d300549bef0f8d554ecfca60a1b1ecba95a7014ee4545ad4f0c4e3a31942c6738b4ccd6244b6a21267dadf0826a5f713f13b1f5a9ab8501d957a26d4948278ac67851071a315674bdab173bfef2c2690c8373da6bf3d69f30c0e5da8883de872f59521b40793854085641adf98d13db991c5d0a8aaa0222934fa33332e90ef0b954e195cb267d6ffb36c96e14d1ec7b915a87598b4461a3146566354dc2ae748c84ee0cd46543b53ebff8cdf47725b280a1f799fb6ebb4a31ad2bdd5178250f83a");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha224Digest());
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 2 failed.");
+            }
+        }
+
+        private void ShouldPassSignatureTest3()
+        {
+            BigInteger n = new BigInteger("dcb5686a3d2063a3f9cf7b9b32d2d3765b4c449b09b4960245a9111cd3b0cbd3260496885b8e1fa5db33b03efcc759d9c1afe29d93c6faebc7e0efada334b5b9a29655e2da2c8f11103d8203be311feab7ae88e9f1b2ec7d8fc655d77202b1681dd9717ec0f525b35584987e19539635a1ed23ca482a00149c609a23dc1645fd", 16);
+            BigInteger e = new BigInteger("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dc9f7", 16);
+            BigInteger d = new BigInteger("189d6345099098992e0c9ca5f281e1338092342fa0acc85cc2a111f30f9bd2fb4753cd1a48ef0ddca9bf1af33ec76fb2e23a9fb4896c26f2235b516f7c05ef7ae81e70f4b491a5fedba9b935e9c76d761a813ce7776ff8a1e5efe1166ff2eca26aa900da88c908d51af9de26977fe39719cc781df32216fa41b838f0c63803c3", 16);
+
+            byte[] msg = Hex.Decode("911475c6e210ef4ac65b6fe8d2bfe5e01b959771b137c4ef69b88716e0d2ff9ebc1fad0f358c1dd7d50cc99a7b893ac9a6207076f08d8467d9e48c69c683bfe64a44dabaa3f7c243880f6ab7229bf7bb587822314fc5de5131983bfb2eef8b4bc1eac36f353724b567cd1ae8cddd64ddb7057549d5c81ad5fa3b5e751f00abf5");
+            byte[] sig = Hex.Decode("02c50ec0ac8a7f38ef5630c396964d6a6daaa7e3083ab5b57fa2a2632f3b70e2e85c8456cd774d45d7e44fcb063f0f04fff9f1e3adfda11272535a92cb59320b190b5ee4261f23d6ceaa925df3a7bfa42e26bf61ea9645d9d64b3c90a820802768a6e209c9f83705375a3867afccc037e8242a98fa4c3db6b2d9877754d47289");
+
+            RsaKeyParameters rsaPublic = new RsaKeyParameters(false, n, e);
+            X931Signer signer = new X931Signer(new RsaEngine(), new Sha1Digest());
+
+            signer.Init(true, new RsaKeyParameters(true, n, d));
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            byte[] s = signer.GenerateSignature();
+
+            if (!Arrays.AreEqual(sig, s))
+            {
+                Fail("RSA X931 sig test 3 failed.");
+            }
+
+            signer.Init(false, rsaPublic);
+
+            signer.BlockUpdate(msg, 0, msg.Length);
+
+            if (!signer.VerifySignature(sig))
+            {
+                Fail("RSA X931 verify test 3 failed.");
+            }
+        }
+
+        public static void Main(string[] args)
+        {
+            RunTest(new X931SignerTest());
+        }
+
+        [Test]
+        public void TestFunction()
+        {
+            string resultText = Perform().ToString();
+
+            Assert.AreEqual(Name + ": Okay", resultText);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
new file mode 100644
index 000000000..bc99ccc63
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsProtocolTest.cs
@@ -0,0 +1,102 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsProtocolTest
+    {
+        [Test]
+        public void TestClientServer()
+        {
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            Server server = new Server(serverProtocol, network.Server);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            DatagramTransport clientTransport = network.Client;
+
+            clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0);
+
+            clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+
+            MockDtlsClient client = new MockDtlsClient(null);
+
+            DtlsTransport dtlsClient = clientProtocol.Connect(client, clientTransport);
+
+            for (int i = 1; i <= 10; ++i)
+            {
+                byte[] data = new byte[i];
+                Arrays.Fill(data, (byte)i);
+                dtlsClient.Send(data, 0, data.Length);
+            }
+
+            byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+            while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+            {
+            }
+
+            dtlsClient.Close();
+
+            server.Shutdown(serverThread);
+        }
+
+        internal class Server
+        {
+            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private volatile bool isShutdown = false;
+
+            internal Server(DtlsServerProtocol serverProtocol, DatagramTransport serverTransport)
+            {
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockDtlsServer server = new MockDtlsServer();
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(server, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!isShutdown)
+                    {
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 1000);
+                        if (length >= 0)
+                        {
+                            dtlsServer.Send(buf, 0, length);
+                        }
+                    }
+                    dtlsServer.Close();
+                }
+                catch (Exception e)
+                {
+                    Console.Error.WriteLine(e.StackTrace);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
new file mode 100644
index 000000000..d4af04fac
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
@@ -0,0 +1,153 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    [TestFixture]
+    public class DtlsTestCase
+    {
+        private static void CheckDtlsVersion(ProtocolVersion version)
+        {
+            if (version != null && !version.IsDtls)
+                throw new InvalidOperationException("Non-DTLS version");
+        }
+
+        [Test, TestCaseSource(typeof(DtlsTestSuite), "Suite")]
+        public void RunTest(TlsTestConfig config)
+        {
+            CheckDtlsVersion(config.clientMinimumVersion);
+            CheckDtlsVersion(config.clientOfferVersion);
+            CheckDtlsVersion(config.serverMaximumVersion);
+            CheckDtlsVersion(config.serverMinimumVersion);
+
+            SecureRandom secureRandom = new SecureRandom();
+
+            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
+            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+
+            MockDatagramAssociation network = new MockDatagramAssociation(1500);
+
+            TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+            TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+            Server server = new Server(this, serverProtocol, network.Server, serverImpl);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            Exception caught = null;
+            try
+            {
+                DatagramTransport clientTransport = network.Client;
+
+                if (TlsTestConfig.DEBUG)
+                {
+                    clientTransport = new LoggingDatagramTransport(clientTransport, Console.Out);
+                }
+
+                DtlsTransport dtlsClient = clientProtocol.Connect(clientImpl, clientTransport);
+
+                for (int i = 1; i <= 10; ++i)
+                {
+                    byte[] data = new byte[i];
+                    Arrays.Fill(data, (byte)i);
+                    dtlsClient.Send(data, 0, data.Length);
+                }
+    
+                byte[] buf = new byte[dtlsClient.GetReceiveLimit()];
+                while (dtlsClient.Receive(buf, 0, buf.Length, 100) >= 0)
+                {
+                }
+    
+                dtlsClient.Close();
+            }
+            catch (Exception e)
+            {
+                caught = e;
+                LogException(caught);
+            }
+
+            server.Shutdown(serverThread);
+
+            // TODO Add checks that the various streams were closed
+
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, "Client fatal alert connection end");
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, "Server fatal alert connection end");
+
+            Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, "Client fatal alert description");
+            Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, "Server fatal alert description");
+
+            if (config.expectFatalAlertConnectionEnd == -1)
+            {
+                Assert.IsNull(caught, "Unexpected client exception");
+                Assert.IsNull(server.mCaught, "Unexpected server exception");
+            }
+        }
+
+        protected void LogException(Exception e)
+        {
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.Error.WriteLine(e.StackTrace);
+            }
+        }
+
+        internal class Server
+        {
+            private readonly DtlsTestCase mOuter;
+            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DatagramTransport mServerTransport;
+            private readonly TlsTestServerImpl mServerImpl;
+
+            private volatile bool isShutdown = false;
+            internal Exception mCaught = null;
+
+            internal Server(DtlsTestCase outer, DtlsServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+            {
+                this.mOuter = outer;
+                this.mServerProtocol = serverProtocol;
+                this.mServerTransport = serverTransport;
+                this.mServerImpl = serverImpl;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    DtlsTransport dtlsServer = mServerProtocol.Accept(mServerImpl, mServerTransport);
+                    byte[] buf = new byte[dtlsServer.GetReceiveLimit()];
+                    while (!isShutdown)
+                    {
+                        int length = dtlsServer.Receive(buf, 0, buf.Length, 100);
+                        if (length >= 0)
+                        {
+                            dtlsServer.Send(buf, 0, length);
+                        }
+                    }
+                    dtlsServer.Close();
+                }
+                catch (Exception e)
+                {
+                    mCaught = e;
+                    mOuter.LogException(mCaught);
+                }
+            }
+
+            internal void Shutdown(Thread serverThread)
+            {
+                if (!isShutdown)
+                {
+                    isShutdown = true;
+                    serverThread.Interrupt();
+                    serverThread.Join();
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
new file mode 100644
index 000000000..eb9d42e5f
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class DtlsTestSuite
+    {
+        // Make the access to constants less verbose 
+        internal class C : TlsTestConfig {}
+
+        public DtlsTestSuite()
+        {
+        }
+
+        public static IEnumerable Suite()
+        {
+            IList testSuite = new ArrayList();
+
+            AddFallbackTests(testSuite);
+            AddVersionTests(testSuite, ProtocolVersion.DTLSv10);
+            AddVersionTests(testSuite, ProtocolVersion.DTLSv12);
+
+            return testSuite;
+        }
+
+        private static void AddFallbackTests(IList testSuite)
+        {
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientFallback = true;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+            }
+
+            /*
+             * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+             * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+             * alerts being raised
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+            //    c.clientOfferVersion = ProtocolVersion.DTLSv10;
+            //    c.clientFallback = true;
+            //    c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+            //    testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientOfferVersion = ProtocolVersion.DTLSv10;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+            }
+        }
+
+        private static void AddVersionTests(IList testSuite, ProtocolVersion version)
+        {
+            string prefix = version.ToString()
+                .Replace(" ", "")
+                .Replace("\\", "")
+                .Replace(".", "")
+                + "_";
+
+            /*
+             * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit
+             * of the DTLS server after a fatal alert. As of writing, manual runs show the correct
+             * alerts being raised
+             */
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+            //    c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+            //    c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+            //}
+
+            //{
+            //    TlsTestConfig c = CreateDtlsTestConfig(version);
+            //    c.clientAuth = C.CLIENT_AUTH_NONE;
+            //    c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+            //    c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+            //}
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+            }
+        }
+
+        private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion version)
+        {
+            TlsTestConfig c = new TlsTestConfig();
+            c.clientMinimumVersion = ProtocolVersion.DTLSv10;
+            /*
+             * TODO We'd like to just set the offer version to DTLSv12, but there is a known issue with
+             * overly-restrictive version checks b/w BC DTLS 1.2 client, BC DTLS 1.0 server
+             */
+            c.clientOfferVersion = version;
+            c.serverMaximumVersion = version;
+            c.serverMinimumVersion = ProtocolVersion.DTLSv10;
+            return c;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs b/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs
new file mode 100644
index 000000000..a26c5bdbf
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/LoggingDatagramTransport.cs
@@ -0,0 +1,86 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class LoggingDatagramTransport
+        :   DatagramTransport
+    {
+        private static readonly string HEX_CHARS = "0123456789ABCDEF";
+
+        private readonly DatagramTransport transport;
+        private readonly TextWriter output;
+        private readonly long launchTimestamp;
+
+        public LoggingDatagramTransport(DatagramTransport transport, TextWriter output)
+        {
+            this.transport = transport;
+            this.output = output;
+            this.launchTimestamp = DateTimeUtilities.CurrentUnixMs();
+        }
+
+        public virtual int GetReceiveLimit()
+        {
+            return transport.GetReceiveLimit();
+        }
+
+        public virtual int GetSendLimit()
+        {
+            return transport.GetSendLimit();
+        }
+
+        public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+        {
+            int length = transport.Receive(buf, off, len, waitMillis);
+            if (length >= 0)
+            {
+                DumpDatagram("Received", buf, off, length);
+            }
+            return length;
+        }
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            DumpDatagram("Sending", buf, off, len);
+            transport.Send(buf, off, len);
+        }
+
+        public virtual void Close()
+        {
+        }
+
+        private void DumpDatagram(string verb, byte[] buf, int off, int len)
+        {
+            long timestamp = DateTimeUtilities.CurrentUnixMs() - launchTimestamp;
+            StringBuilder sb = new StringBuilder("(+" + timestamp + "ms) " + verb + " " + len + " byte datagram:");
+            for (int pos = 0; pos < len; ++pos)
+            {
+                if (pos % 16 == 0)
+                {
+                    sb.Append(Environment.NewLine);
+                    sb.Append("    ");
+                }
+                else if (pos % 16 == 8)
+                {
+                    sb.Append('-');
+                }
+                else
+                {
+                    sb.Append(' ');
+                }
+                int val = buf[off + pos] & 0xFF;
+                sb.Append(HEX_CHARS[val >> 4]);
+                sb.Append(HEX_CHARS[val & 0xF]);
+            }
+            Dump(sb.ToString());
+        }
+
+        private void Dump(string s)
+        {
+            lock (this) output.WriteLine(s);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
new file mode 100644
index 000000000..48df36ca9
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDatagramAssociation.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Net;
+using System.Threading;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDatagramAssociation
+    {
+        private int mtu;
+        private MockDatagramTransport client, server;
+
+        public MockDatagramAssociation(int mtu)
+        {
+            this.mtu = mtu;
+
+            IList clientQueue = new ArrayList();
+            IList serverQueue = new ArrayList();
+
+            this.client = new MockDatagramTransport(this, clientQueue, serverQueue);
+            this.server = new MockDatagramTransport(this, serverQueue, clientQueue);
+        }
+
+        public virtual DatagramTransport Client
+        {
+            get { return client; }
+        }
+
+        public virtual DatagramTransport Server
+        {
+            get { return server; }
+        }
+
+        private class MockDatagramTransport
+            :   DatagramTransport
+        {
+            private readonly MockDatagramAssociation mOuter;
+
+            private IList receiveQueue, sendQueue;
+
+            internal MockDatagramTransport(MockDatagramAssociation outer, IList receiveQueue, IList sendQueue)
+            {
+                this.mOuter = outer;
+                this.receiveQueue = receiveQueue;
+                this.sendQueue = sendQueue;
+            }
+
+            public virtual int GetReceiveLimit()
+            {
+                return mOuter.mtu;
+            }
+
+            public virtual int GetSendLimit()
+            {
+                return mOuter.mtu;
+            }
+
+            public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+            {
+                lock (receiveQueue)
+                {
+                    if (receiveQueue.Count < 1)
+                    {
+                        try
+                        {
+                            Monitor.Wait(receiveQueue, waitMillis);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                            // TODO Keep waiting until full wait expired?
+                        }
+                        if (receiveQueue.Count < 1)
+                        {
+                            return -1;
+                        }
+                    }
+                    byte[] packet = (byte[])receiveQueue[0];
+                    receiveQueue.RemoveAt(0);
+                    int copyLength = System.Math.Min(len, packet.Length);
+                    Array.Copy(packet, 0, buf, off, copyLength);
+                    return copyLength;
+                }
+            }
+
+            public virtual void Send(byte[] buf, int off, int len)
+            {
+                if (len > mOuter.mtu)
+                {
+                    // TODO Simulate rejection?
+                }
+
+                byte[] packet = Arrays.CopyOfRange(buf, off, off + len);
+
+                lock (sendQueue)
+                {
+                    sendQueue.Add(packet);
+                    Monitor.PulseAll(sendQueue);
+                }
+            }
+
+            public virtual void Close()
+            {
+                // TODO?
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsClient.cs b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
new file mode 100644
index 000000000..e3c604db7
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsClient.cs
@@ -0,0 +1,150 @@
+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
+{
+    public class MockDtlsClient
+        :   DefaultTlsClient
+    {
+        protected TlsSession mSession;
+
+        public MockDtlsClient(TlsSession session)
+        {
+            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("DTLS 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("DTLS client received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override ProtocolVersion ClientVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        //public override int[] GetCipherSuites()
+        //{
+        //    return Arrays.Concatenate(base.GetCipherSuites(),
+        //        new int[]
+        //        {
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+        //        });
+        //}
+
+        public override IDictionary GetClientExtensions()
+        {
+            IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+            TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
+            TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+            TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+            return clientExtensions;
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            Console.WriteLine("Negotiated " + serverVersion);
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(mContext);
+        }
+
+        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;
+            }
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsContext context)
+            {
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+                Console.WriteLine("DTLS 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 + ")");
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                    return null;
+
+                return TlsTestUtilities.LoadSignerCredentials(mContext, certificateRequest.SupportedSignatureAlgorithms,
+                    SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockDtlsServer.cs b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
new file mode 100644
index 000000000..19062181b
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockDtlsServer.cs
@@ -0,0 +1,100 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class MockDtlsServer
+        :   DefaultTlsServer
+    {
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+            output.WriteLine("DTLS 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("DTLS server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return Arrays.Concatenate(base.GetCipherSuites(),
+                new int[]
+                {
+                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+                    CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+                });
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            X509CertificateStructure[] chain = clientCertificate.GetCertificateList();
+            Console.WriteLine("DTLS server received client 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 + ")");
+            }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+            get { return ProtocolVersion.DTLSv12; }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+            get { return ProtocolVersion.DTLSv10; }
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[] { "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
new file mode 100644
index 000000000..dfc0e93a0
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockPskTlsClient.cs
@@ -0,0 +1,132 @@
+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 MockPskTlsClient
+        :   PskTlsClient
+    {
+        internal TlsSession mSession;
+
+        internal MockPskTlsClient(TlsSession session)
+            :   this(session, new BasicTlsPskIdentity("client", new byte[16]))
+        {
+        }
+
+        internal MockPskTlsClient(TlsSession session, TlsPskIdentity pskIdentity)
+            :   base(pskIdentity)
+        {
+            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-PSK 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-PSK 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 int[] GetCipherSuites()
+        {
+            return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+        }
+
+        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-PSK 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-PSK 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/MockPskTlsServer.cs b/crypto/test/src/crypto/tls/test/MockPskTlsServer.cs
new file mode 100644
index 000000000..7394a2077
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockPskTlsServer.cs
@@ -0,0 +1,105 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class MockPskTlsServer
+        :   PskTlsServer
+    {
+        internal MockPskTlsServer()
+            :   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-PSK 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-PSK server received alert: " + AlertLevel.GetText(alertLevel)
+                + ", " + AlertDescription.GetText(alertDescription));
+        }
+
+        public override void NotifyHandshakeComplete()
+        {
+            base.NotifyHandshakeComplete();
+
+            byte[] pskIdentity = mContext.SecurityParameters.PskIdentity;
+            if (pskIdentity != null)
+            {
+                string name = Strings.FromUtf8ByteArray(pskIdentity);
+                Console.WriteLine("TLS-PSK server completed handshake for PSK identity: " + name);
+            }
+        }
+
+        protected override int[] GetCipherSuites()
+        {
+            return new int[]{ CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384, CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,
+                CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA };
+        }
+
+        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-PSK server negotiated " + serverVersion);
+
+            return serverVersion;
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{"x509-server.pem", "x509-ca.pem"},
+                "x509-server-key.pem");
+        }
+
+        internal class MyIdentityManager
+            :   TlsPskIdentityManager
+        {
+            public virtual byte[] GetHint()
+            {
+                return Strings.ToUtf8ByteArray("hint");
+            }
+
+            public virtual byte[] GetPsk(byte[] identity)
+            {
+                if (identity != null)
+                {
+                    string name = Strings.FromUtf8ByteArray(identity);
+                    if (name.Equals("client"))
+                    {
+                        return new byte[16];
+                    }
+                }
+                return null;
+            }
+        }
+    }
+}
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..8a6b9f496
--- /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 a458e32e6..7c1198632 100644
--- a/crypto/test/src/crypto/tls/test/MockTlsClient.cs
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -45,25 +45,24 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 + ", " + AlertDescription.GetText(alertDescription));
         }
 
-        public override int[] GetCipherSuites()
-        {
-            return Arrays.Concatenate(base.GetCipherSuites(),
-                new int[]
-                {
-                    CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
-                    CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
-                    CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
-                    CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
-                    CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
-                });
-        }
+        //public override int[] GetCipherSuites()
+        //{
+        //    return Arrays.Concatenate(base.GetCipherSuites(),
+        //        new int[]
+        //        {
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+        //            CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+        //        });
+        //}
 
         public override IDictionary GetClientExtensions()
         {
             IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
             TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
-            // TODO[draft-ietf-tls-session-hash-01] Enable once code-point assigned (only for compatible server though)
-//            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
+            TlsExtensionsUtilities.AddExtendedMasterSecretExtension(clientExtensions);
             TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
             TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
             return clientExtensions;
@@ -121,8 +120,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 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) + " ("
+                    // TODO Create fingerprint based on certificate signature algorithm digest
+                    Console.WriteLine("    fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
                         + entry.Subject + ")");
                 }
             }
@@ -133,27 +132,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/NetworkStream.cs b/crypto/test/src/crypto/tls/test/NetworkStream.cs
new file mode 100644
index 000000000..04de81e13
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/NetworkStream.cs
@@ -0,0 +1,101 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class NetworkStream
+        :   Stream
+    {
+        private readonly Stream mInner;
+        private bool mClosed = false;
+
+        internal NetworkStream(Stream inner)
+        {
+            this.mInner = inner;
+        }
+
+        internal virtual bool IsClosed
+        {
+            get { lock (this) return mClosed; }
+        }
+
+        public override bool CanRead
+        {
+            get { return mInner.CanRead; }
+        }
+
+        public override bool CanSeek
+        {
+            get { return mInner.CanSeek; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return mInner.CanWrite; }
+        }
+
+        public override void Close()
+        {
+            lock (this) mClosed = true;
+        }
+
+        public override void Flush()
+        {
+            mInner.Flush();
+        }
+
+        public override long Length
+        {
+            get { return mInner.Length; }
+        }
+
+        public override long Position
+        {
+            get { return mInner.Position; }
+            set { mInner.Position = value; }
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            return mInner.Seek(offset, origin);
+        }
+
+        public override void SetLength(long value)
+        {
+            mInner.SetLength(value);
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            CheckNotClosed();
+            return mInner.Read(buffer, offset, count);
+        }
+
+        public override int ReadByte()
+        {
+            CheckNotClosed();
+            return mInner.ReadByte();
+        }
+
+        public override void Write(byte[] buf, int off, int len)
+        {
+            CheckNotClosed();
+            mInner.Write(buf, off, len);
+        }
+
+        public override void WriteByte(byte value)
+        {
+            CheckNotClosed();
+            mInner.WriteByte(value);
+        }
+
+        private void CheckNotClosed()
+        {
+            lock (this)
+            {
+                if (mClosed)
+                    throw new ObjectDisposedException(this.GetType().Name);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/PipedStream.cs b/crypto/test/src/crypto/tls/test/PipedStream.cs
new file mode 100644
index 000000000..6b2c15059
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/PipedStream.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class PipedStream
+        :   Stream
+    {
+        private readonly MemoryStream mBuf = new MemoryStream();
+        private bool mClosed = false;
+
+        private PipedStream mOther = null;
+        private long mReadPos = 0;
+
+        internal PipedStream()
+        {
+        }
+
+        internal PipedStream(PipedStream other)
+        {
+            lock (other)
+            {
+                this.mOther = other;
+                other.mOther = this;
+            }
+        }
+
+        public override bool CanRead
+        {
+            get { return true; }
+        }
+
+        public override bool CanSeek
+        {
+            get { return false; }
+        }
+
+        public override bool CanWrite
+        {
+            get { return true; }
+        }
+
+        public override void Close()
+        {
+            lock (this)
+            {
+                mClosed = true;
+                Monitor.PulseAll(this);
+            }
+        }
+
+        public override void Flush()
+        {
+        }
+
+        public override long Length
+        {
+            get { throw new NotImplementedException(); }
+        }
+
+        public override long Position
+        {
+            get { throw new NotImplementedException(); }
+            set { throw new NotImplementedException(); }
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void SetLength(long value)
+        {
+            throw new NotImplementedException();
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            lock (mOther)
+            {
+                WaitForData();
+                int len = (int)System.Math.Min(count, mOther.mBuf.Position - mReadPos);
+                Array.Copy(mOther.mBuf.GetBuffer(), mReadPos, buffer, offset, len);
+                mReadPos += len;
+                return len;
+            }
+        }
+
+        public override int ReadByte()
+        {
+            lock (mOther)
+            {
+                WaitForData();
+                bool eof = (mReadPos >= mOther.mBuf.Position);
+                return eof ? -1 : mOther.mBuf.GetBuffer()[mReadPos++];
+            }
+        }
+
+        public override void Write(byte[] buf, int off, int len)
+        {
+            lock (this)
+            {
+                CheckOpen();
+                mBuf.Write(buf, off, len);
+                Monitor.PulseAll(this);
+            }
+        }
+
+        public override void WriteByte(byte value)
+        {
+            lock (this)
+            {
+                CheckOpen();
+                mBuf.WriteByte(value);
+                Monitor.PulseAll(mBuf);
+            }
+        }
+
+        private void CheckOpen()
+        {
+            if (mClosed)
+                throw new ObjectDisposedException(this.GetType().Name);
+        }
+
+        private void WaitForData()
+        {
+            while (mReadPos >= mOther.mBuf.Position && !mOther.mClosed)
+            {
+                Monitor.Wait(mOther);
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
new file mode 100644
index 000000000..7072c7105
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/PskTlsClientTest.cs
@@ -0,0 +1,79 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    /**
+     * A simple test designed to conduct a TLS handshake with an external TLS server.
+     * <p>
+     * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+     * this package (under 'src/test/resources') for help configuring an external TLS server.
+     * </p><p>
+     * In both cases, extra options are required to enable PSK ciphersuites and configure identities/keys.
+     * </p>
+     */
+    public class PskTlsClientTest
+    {
+        private static readonly SecureRandom secureRandom = new SecureRandom();
+
+        public static void Main(string[] args)
+        {
+            string hostname = "localhost";
+            int port = 5556;
+
+            long time1 = DateTime.UtcNow.Ticks;
+
+            /*
+             * Note: This is the default PSK identity for 'openssl s_server' testing, the server must be
+             * started with "-psk 6161616161" to make the keys match, and possibly the "-psk_hint"
+             * option should be present.
+             */
+            string psk_identity = "Client_identity";
+            byte[] psk = new byte[]{ 0x61, 0x61, 0x61, 0x61, 0x61 };
+
+            BasicTlsPskIdentity pskIdentity = new BasicTlsPskIdentity(psk_identity, psk);
+
+            MockPskTlsClient client = new MockPskTlsClient(null, pskIdentity);
+            TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+            protocol.Close();
+
+            long time2 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+            client = new MockPskTlsClient(client.GetSessionToResume(), pskIdentity);
+            protocol = OpenTlsConnection(hostname, port, client);
+
+            long time3 = DateTime.UtcNow.Ticks;
+            Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+            byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+            Stream tlsStream = protocol.Stream;
+            tlsStream.Write(req, 0, req.Length);
+            tlsStream.Flush();
+
+            StreamReader reader = new StreamReader(tlsStream);
+
+            String line;
+            while ((line = reader.ReadLine()) != null)
+            {
+                Console.WriteLine(">>> " + line);
+            }
+
+            protocol.Close();
+        }
+
+        internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+        {
+            TcpClient tcp = new TcpClient(hostname, port);
+
+            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+            protocol.Connect(client);
+            return protocol;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsProtocolTest.cs
new file mode 100644
index 000000000..ba5b90c75
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsProtocolTest.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 TlsProtocolTest
+    {
+        [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();
+
+            MockTlsClient client = new MockTlsClient(null);
+            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
+                {
+                    MockTlsServer server = new MockTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs b/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.cs
new file mode 100644
index 000000000..b059bb2cb
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsPskProtocolTest.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 TlsPskProtocolTest
+    {
+        [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();
+
+            MockPskTlsClient client = new MockPskTlsClient(null);
+            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
+                {
+                    MockPskTlsServer server = new MockPskTlsServer();
+                    mServerProtocol.Accept(server);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception)
+                {
+                    //throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+}
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);
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestCase.cs b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
new file mode 100644
index 000000000..4b0c12710
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
@@ -0,0 +1,164 @@
+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 TlsTestCase
+    {
+        private static void CheckTlsVersion(ProtocolVersion version)
+        {
+            if (version != null && !version.IsTls)
+                throw new InvalidOperationException("Non-TLS version");
+        }
+
+        [Test, TestCaseSource(typeof(TlsTestSuite), "Suite")]
+        public void RunTest(TlsTestConfig config)
+        {
+            CheckTlsVersion(config.clientMinimumVersion);
+            CheckTlsVersion(config.clientOfferVersion);
+            CheckTlsVersion(config.serverMaximumVersion);
+            CheckTlsVersion(config.serverMinimumVersion);
+
+            SecureRandom secureRandom = new SecureRandom();
+
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            NetworkStream clientNet = new NetworkStream(clientPipe);
+            NetworkStream serverNet = new NetworkStream(serverPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNet, secureRandom);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNet, secureRandom);
+
+            TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+            TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+            Server server = new Server(this, serverProtocol, serverImpl);
+
+            Thread serverThread = new Thread(new ThreadStart(server.Run));
+            serverThread.Start();
+
+            Exception caught = null;
+            try
+            {
+                clientProtocol.Connect(clientImpl);
+
+                // 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();
+            }
+            catch (Exception e)
+            {
+                caught = e;
+                LogException(caught);
+            }
+
+            server.AllowExit();
+            serverThread.Join();
+
+            Assert.IsTrue(clientNet.IsClosed, "Client Stream not closed");
+            Assert.IsTrue(serverNet.IsClosed, "Server Stream not closed");
+
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, clientImpl.FirstFatalAlertConnectionEnd, "Client fatal alert connection end");
+            Assert.AreEqual(config.expectFatalAlertConnectionEnd, serverImpl.FirstFatalAlertConnectionEnd, "Server fatal alert connection end");
+
+            Assert.AreEqual(config.expectFatalAlertDescription, clientImpl.FirstFatalAlertDescription, "Client fatal alert description");
+            Assert.AreEqual(config.expectFatalAlertDescription, serverImpl.FirstFatalAlertDescription, "Server fatal alert description");
+
+            if (config.expectFatalAlertConnectionEnd == -1)
+            {
+                Assert.IsNull(caught, "Unexpected client exception");
+                Assert.IsNull(server.mCaught, "Unexpected server exception");
+            }
+        }
+
+        protected virtual void LogException(Exception e)
+        {
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.Error.WriteLine(e);
+            }
+        }
+
+        internal class Server
+        {
+            protected readonly TlsTestCase mOuter;
+            protected readonly TlsServerProtocol mServerProtocol;
+            protected readonly TlsTestServerImpl mServerImpl;
+
+            internal bool mCanExit = false;
+            internal Exception mCaught = null;
+
+            internal Server(TlsTestCase outer, TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
+            {
+                this.mOuter = outer;
+                this.mServerProtocol = serverProtocol;
+                this.mServerImpl = serverImpl;
+            }
+
+            internal void AllowExit()
+            {
+                lock (this)
+                {
+                    mCanExit = true;
+                    Monitor.PulseAll(this);
+                }
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    mServerProtocol.Accept(mServerImpl);
+                    Streams.PipeAll(mServerProtocol.Stream, mServerProtocol.Stream);
+                    mServerProtocol.Close();
+                }
+                catch (Exception e)
+                {
+                    mCaught = e;
+                    mOuter.LogException(mCaught);
+                }
+
+                WaitExit();
+            }
+
+            protected void WaitExit()
+            {
+                lock (this)
+                {
+                    while (!mCanExit)
+                    {
+                        try
+                        {
+                            Monitor.Wait(this);
+                        }
+                        catch (ThreadInterruptedException)
+                        {
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
new file mode 100644
index 000000000..48af9e0f8
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
@@ -0,0 +1,262 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestClientImpl
+        :   DefaultTlsClient
+    {
+        protected readonly TlsTestConfig mConfig;
+
+        protected int firstFatalAlertConnectionEnd = -1;
+        protected int firstFatalAlertDescription = -1;
+
+        internal TlsTestClientImpl(TlsTestConfig config)
+        {
+            this.mConfig = config;
+        }
+
+        internal int FirstFatalAlertConnectionEnd
+        {
+            get { return firstFatalAlertConnectionEnd; }
+        }
+
+        internal int FirstFatalAlertDescription
+        {
+            get { return firstFatalAlertDescription; }
+        }
+
+        public override ProtocolVersion ClientVersion
+        {
+	        get 
+	        { 
+                if (mConfig.clientOfferVersion != null)
+                {
+                    return mConfig.clientOfferVersion;
+                }
+
+                return base.ClientVersion;
+            }
+        }
+
+        public override ProtocolVersion MinimumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.clientMinimumVersion != null)
+                {
+                    return mConfig.clientMinimumVersion;
+                }
+
+                return base.MinimumVersion;
+	        }
+        }
+
+        public override bool IsFallback
+        {
+            get { return mConfig.clientFallback; }
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.client;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS 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)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.server;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS client received alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+            }
+        }
+
+        public override void NotifyServerVersion(ProtocolVersion serverVersion)
+        {
+            base.NotifyServerVersion(serverVersion);
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS client negotiated " + serverVersion);
+            }
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(this, mContext);
+        }
+
+        protected virtual Certificate CorruptCertificate(Certificate cert)
+        {
+            X509CertificateStructure[] certList = cert.GetCertificateList();
+            certList[0] = CorruptCertificateSignature(certList[0]);
+            return new Certificate(certList);
+        }
+
+        protected virtual X509CertificateStructure CorruptCertificateSignature(X509CertificateStructure cert)
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+            v.Add(cert.TbsCertificate);
+            v.Add(cert.SignatureAlgorithm);
+            v.Add(CorruptBitString(cert.Signature));
+
+            return X509CertificateStructure.GetInstance(new DerSequence(v));
+        }
+
+        protected virtual DerBitString CorruptBitString(DerBitString bs)
+        {
+            return new DerBitString(CorruptBit(bs.GetBytes()));
+        }
+
+        protected virtual byte[] CorruptBit(byte[] bs)
+        {
+            bs = Arrays.Clone(bs);
+
+            // Flip a random bit
+            int bit = mContext.SecureRandom.Next(bs.Length << 3); 
+            bs[bit >> 3] ^= (byte)(1 << (bit & 7));
+
+            return bs;
+        }
+
+        internal class MyTlsAuthentication
+            :   TlsAuthentication
+        {
+            private readonly TlsTestClientImpl mOuter;
+            private readonly TlsContext mContext;
+
+            internal MyTlsAuthentication(TlsTestClientImpl outer, TlsContext context)
+            {
+                this.mOuter = outer;
+                this.mContext = context;
+            }
+
+            public virtual void NotifyServerCertificate(Certificate serverCertificate)
+            {
+                bool isEmpty = serverCertificate == null || serverCertificate.IsEmpty;
+
+                X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+
+                // TODO Cache test resources?
+                if (isEmpty || !(chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server.pem"))
+                    || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server-dsa.pem"))
+                    || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-server-ecdsa.pem"))))
+                {
+                    throw new TlsFatalAlert(AlertDescription.bad_certificate);
+                }
+
+                if (TlsTestConfig.DEBUG)
+                {
+                    Console.WriteLine("TLS 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 + ")");
+                    }
+                }
+            }
+
+            public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                if (mOuter.mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+                    throw new InvalidOperationException();
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+                    return null;
+
+                byte[] certificateTypes = certificateRequest.CertificateTypes;
+                if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+                {
+                    return null;
+                }
+
+                TlsSignerCredentials signerCredentials = TlsTestUtilities.LoadSignerCredentials(mContext,
+                    certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                    "x509-client.pem", "x509-client-key.pem");
+
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+                {
+                    return signerCredentials;
+                }
+
+                return new MyTlsSignerCredentials(mOuter, signerCredentials);
+            }
+        };
+
+        internal class MyTlsSignerCredentials
+            :   TlsSignerCredentials
+        {
+            private readonly TlsTestClientImpl mOuter;
+            private readonly TlsSignerCredentials mInner;
+
+            internal MyTlsSignerCredentials(TlsTestClientImpl outer, TlsSignerCredentials inner)
+            {
+                this.mOuter = outer;
+                this.mInner = inner;
+            }
+
+            public virtual byte[] GenerateCertificateSignature(byte[] hash)
+            {
+                byte[] sig = mInner.GenerateCertificateSignature(hash);
+
+                if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+                {
+                    sig = mOuter.CorruptBit(sig);
+                }
+
+                return sig;
+            }
+
+            public virtual Certificate Certificate
+            {
+                get
+                {
+                    Certificate cert = mInner.Certificate;
+
+                    if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+                    {
+                        cert = mOuter.CorruptCertificate(cert);
+                    }
+
+                    return cert;
+                }
+            }
+
+            public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+            {
+                get { return mInner.SignatureAndHashAlgorithm; }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
new file mode 100644
index 000000000..0d1e7badb
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class TlsTestConfig
+    {
+        public static readonly bool DEBUG = false;
+
+        /**
+         * Client does not authenticate, ignores any certificate request
+         */
+        public const int CLIENT_AUTH_NONE = 0;
+
+        /**
+         * Client will authenticate if it receives a certificate request
+         */
+        public const int CLIENT_AUTH_VALID = 1;
+
+        /**
+         * Client will authenticate if it receives a certificate request, with an invalid certificate
+         */
+        public const int CLIENT_AUTH_INVALID_CERT = 2;
+
+        /**
+         * Client will authenticate if it receives a certificate request, with an invalid CertificateVerify signature
+         */
+        public const int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+        /**
+         * Server will not request a client certificate
+         */
+        public const int SERVER_CERT_REQ_NONE = 0;
+
+        /**
+         * Server will request a client certificate but receiving one is optional
+         */
+        public const int SERVER_CERT_REQ_OPTIONAL = 1;
+
+        /**
+         * Server will request a client certificate and receiving one is mandatory
+         */
+        public const int SERVER_CERT_REQ_MANDATORY = 2;
+
+        /**
+         * Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+         */
+        public int clientAuth = CLIENT_AUTH_VALID;
+
+        /**
+         * Configures the minimum protocol version the client will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion clientMinimumVersion = null;
+
+        /**
+         * Configures the protocol version the client will offer. If null, uses the library's default.
+         */
+        public ProtocolVersion clientOfferVersion = null;
+
+        /**
+         * Configures whether the client will indicate version fallback via TLS_FALLBACK_SCSV.
+         */
+        public bool clientFallback = false;
+
+        /**
+         * Configures whether the test server will send a certificate request.
+         */
+        public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+        /**
+         * Configures the maximum protocol version the server will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion serverMaximumVersion = null;
+
+        /**
+         * Configures the minimum protocol version the server will accept. If null, uses the library's default.
+         */
+        public ProtocolVersion serverMinimumVersion = null;
+
+        /**
+         * Configures the connection end that a fatal alert is expected to be raised. Use ConnectionEnd.* constants.
+         */
+        public int expectFatalAlertConnectionEnd = -1;
+
+        /**
+         * Configures the type of fatal alert expected to be raised. Use AlertDescription.* constants.
+         */
+        public int expectFatalAlertDescription = -1;
+
+        public void ExpectClientFatalAlert(byte alertDescription)
+        {
+            this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+            this.expectFatalAlertDescription = alertDescription;
+        }
+
+        public void ExpectServerFatalAlert(byte alertDescription)
+        {
+            this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+            this.expectFatalAlertDescription = alertDescription;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
new file mode 100644
index 000000000..152d5dbdc
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestServerImpl
+        :   DefaultTlsServer
+    {
+        protected readonly TlsTestConfig mConfig;
+
+        protected int firstFatalAlertConnectionEnd = -1;
+        protected int firstFatalAlertDescription = -1;
+
+        internal TlsTestServerImpl(TlsTestConfig config)
+        {
+            this.mConfig = config;
+        }
+
+        internal int FirstFatalAlertConnectionEnd
+        {
+            get { return firstFatalAlertConnectionEnd; }
+        }
+
+        internal int FirstFatalAlertDescription
+        {
+            get { return firstFatalAlertDescription; }
+        }
+
+        protected override ProtocolVersion MaximumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.serverMaximumVersion != null)
+                {
+                    return mConfig.serverMaximumVersion;
+                }
+
+                return base.MaximumVersion;
+	        }
+        }
+
+        protected override ProtocolVersion MinimumVersion
+        {
+	        get 
+	        { 
+                if (mConfig.serverMinimumVersion != null)
+                {
+                    return mConfig.serverMinimumVersion;
+                }
+
+                return base.MinimumVersion;
+	        }
+        }
+
+        public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.server;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS 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)
+        {
+            if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+            {
+                firstFatalAlertConnectionEnd = ConnectionEnd.client;
+                firstFatalAlertDescription = alertDescription;
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+                output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+                    + ", " + AlertDescription.GetText(alertDescription));
+            }
+        }
+
+        public override ProtocolVersion GetServerVersion()
+        {
+            ProtocolVersion serverVersion = base.GetServerVersion();
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS server negotiated " + serverVersion);
+            }
+
+            return serverVersion;
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            if (mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+            {
+                return null;
+            }
+
+            byte[] certificateTypes = new byte[]{ ClientCertificateType.rsa_sign,
+                ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign };
+
+            IList serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+            }
+
+            IList certificateAuthorities = new ArrayList();
+            certificateAuthorities.Add(TlsTestUtilities.LoadCertificateResource("x509-ca.pem").Subject);
+
+            return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            bool isEmpty = (clientCertificate == null || clientCertificate.IsEmpty);
+
+            if (isEmpty != (mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+            {
+                throw new InvalidOperationException();
+            }
+            if (isEmpty && (mConfig.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+            {
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+
+            X509CertificateStructure[] chain = clientCertificate.GetCertificateList();
+
+            // TODO Cache test resources?
+            if (!isEmpty && !(chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client.pem"))
+                || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client-dsa.pem"))
+                || chain[0].Equals(TlsTestUtilities.LoadCertificateResource("x509-client-ecdsa.pem"))))
+            {
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+            }
+
+            if (TlsTestConfig.DEBUG)
+            {
+                Console.WriteLine("TLS server received client 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 + ")");
+                }
+            }
+        }
+
+        protected override TlsSignerCredentials GetDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.dsa,
+                "x509-server-dsa.pem", "x509-server-key-dsa.pem");
+        }
+
+        protected override TlsSignerCredentials GetECDsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.ecdsa,
+                "x509-server-ecdsa.pem", "x509-server-key-ecdsa.pem");
+        }
+
+        protected override TlsEncryptionCredentials GetRsaEncryptionCredentials()
+        {
+            return TlsTestUtilities.LoadEncryptionCredentials(mContext, new string[]{ "x509-server.pem", "x509-ca.pem" },
+                "x509-server-key.pem");
+        }
+
+        protected override TlsSignerCredentials GetRsaSignerCredentials()
+        {
+            return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
+                "x509-server.pem", "x509-server-key.pem");
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
new file mode 100644
index 000000000..dfd09d06e
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections;
+
+using NUnit.Framework;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class TlsTestSuite
+    {
+        // Make the access to constants less verbose 
+        internal class C : TlsTestConfig {}
+
+        public TlsTestSuite()
+        {
+        }
+
+        public static IEnumerable Suite()
+        {
+            IList testSuite = new ArrayList();
+
+            AddFallbackTests(testSuite);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv10);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv11);
+            AddVersionTests(testSuite, ProtocolVersion.TLSv12);
+
+            return testSuite;
+        }
+
+        private static void AddFallbackTests(IList testSuite)
+        {
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientFallback = true;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientOfferVersion = ProtocolVersion.TLSv11;
+                c.clientFallback = true;
+                c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
+                c.clientOfferVersion = ProtocolVersion.TLSv11;
+
+                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+            }
+        }
+
+        private static void AddVersionTests(IList testSuite, ProtocolVersion version)
+        {
+            string prefix = version.ToString()
+                .Replace(" ", "")
+                .Replace("\\", "")
+                .Replace(".", "")
+                + "_";
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+                c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+                c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+                c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+                c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+            }
+
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+
+                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+            }
+        }
+
+        private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion version)
+        {
+            TlsTestConfig c = new TlsTestConfig();
+            c.clientMinimumVersion = ProtocolVersion.TLSv10;
+            c.clientOfferVersion = ProtocolVersion.TLSv12;
+            c.serverMaximumVersion = version;
+            c.serverMinimumVersion = ProtocolVersion.TLSv10;
+            return c;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
index 272dfd4fd..a76858ce6 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.Globalization;
 using System.IO;
 using System.Text;
@@ -89,6 +90,34 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             return new DefaultTlsSignerCredentials(context, certificate, privateKey, signatureAndHashAlgorithm);
         }
 
+        internal static TlsSignerCredentials LoadSignerCredentials(TlsContext context, IList supportedSignatureAlgorithms,
+            byte signatureAlgorithm, string certResource, string keyResource)
+        {
+            /*
+             * TODO Note that this code fails to provide default value for the client supported
+             * algorithms if it wasn't sent.
+             */
+
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+            if (supportedSignatureAlgorithms != null)
+            {
+                foreach (SignatureAndHashAlgorithm alg in supportedSignatureAlgorithms)
+                {
+                    if (alg.Signature == signatureAlgorithm)
+                    {
+                        signatureAndHashAlgorithm = alg;
+                        break;
+                    }
+                }
+
+                if (signatureAndHashAlgorithm == null)
+                    return null;
+            }
+
+            return LoadSignerCredentials(context, new String[]{ certResource, "x509-ca.pem" },
+                keyResource, signatureAndHashAlgorithm);
+        }
+
         internal static Certificate LoadCertificateChain(string[] resources)
         {
             X509CertificateStructure[] chain = new X509CertificateStructure[resources.Length];
diff --git a/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs b/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs
new file mode 100644
index 000000000..b771ab7cf
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/UnreliableDatagramTransport.cs
@@ -0,0 +1,84 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    public class UnreliableDatagramTransport
+        :   DatagramTransport
+    {
+        private readonly DatagramTransport transport;
+        private readonly Random random;
+        private readonly int percentPacketLossReceiving, percentPacketLossSending;
+
+        public UnreliableDatagramTransport(DatagramTransport transport, Random random,
+            int percentPacketLossReceiving, int percentPacketLossSending)
+        {
+            if (percentPacketLossReceiving < 0 || percentPacketLossReceiving > 100)
+                throw new ArgumentException("out of range", "percentPacketLossReceiving");
+            if (percentPacketLossSending < 0 || percentPacketLossSending > 100)
+                throw new ArgumentException("out of range", "percentPacketLossSending");
+
+            this.transport = transport;
+            this.random = random;
+            this.percentPacketLossReceiving = percentPacketLossReceiving;
+            this.percentPacketLossSending = percentPacketLossSending;
+        }
+
+        public virtual int GetReceiveLimit()
+        {
+            return transport.GetReceiveLimit();
+        }
+
+        public virtual int GetSendLimit()
+        {
+            return transport.GetSendLimit();
+        }
+
+        public virtual int Receive(byte[] buf, int off, int len, int waitMillis)
+        {
+            long endMillis = DateTimeUtilities.CurrentUnixMs() + waitMillis;
+            for (;;)
+            {
+                int length = transport.Receive(buf, off, len, waitMillis);
+                if (length < 0 || !LostPacket(percentPacketLossReceiving))
+                {
+                    return length;
+                }
+
+                Console.WriteLine("PACKET LOSS (" + length + " byte packet not received)");
+
+                long now = DateTimeUtilities.CurrentUnixMs();
+                if (now >= endMillis)
+                {
+                    return -1;
+                }
+
+                waitMillis = (int)(endMillis - now);
+            }
+        }
+
+        public virtual void Send(byte[] buf, int off, int len)
+        {
+            if (LostPacket(percentPacketLossSending))
+            {
+                Console.WriteLine("PACKET LOSS (" + len + " byte packet not sent)");
+            }
+            else
+            {
+                transport.Send(buf, off, len);
+            }
+        }
+
+        public virtual void Close()
+        {
+            transport.Close();
+        }
+
+        private bool LostPacket(int percentPacketLoss)
+        {
+            return percentPacketLoss > 0 && random.Next(100) < percentPacketLoss;
+        }
+    }
+}
diff --git a/crypto/test/src/math/ec/test/AllTests.cs b/crypto/test/src/math/ec/test/AllTests.cs
index d4c7dc768..3e014ffd2 100644
--- a/crypto/test/src/math/ec/test/AllTests.cs
+++ b/crypto/test/src/math/ec/test/AllTests.cs
@@ -7,22 +7,21 @@ namespace Org.BouncyCastle.Math.EC.Tests
 {
     public class AllTests
     {
-        public static void Main(
-            string[] args)
+        public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-        public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("EC Math tests");
-
-            suite.Add(new ECAlgorithmsTest());
-            suite.Add(new ECPointTest());
-
-            return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("EC Math tests");
+                suite.Add(new ECAlgorithmsTest());
+                suite.Add(new ECPointTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/math/test/AllTests.cs b/crypto/test/src/math/test/AllTests.cs
index 6f2b50140..40cfe3774 100644
--- a/crypto/test/src/math/test/AllTests.cs
+++ b/crypto/test/src/math/test/AllTests.cs
@@ -7,21 +7,20 @@ namespace Org.BouncyCastle.Math.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-//            junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-        public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-			TestSuite suite = new TestSuite("Math tests");
-
-			suite.Add(new BigIntegerTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("Math tests");
+                suite.Add(new BigIntegerTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/ocsp/test/AllTests.cs b/crypto/test/src/ocsp/test/AllTests.cs
index 2b30e3ad4..5e919fd91 100644
--- a/crypto/test/src/ocsp/test/AllTests.cs
+++ b/crypto/test/src/ocsp/test/AllTests.cs
@@ -9,21 +9,20 @@ namespace Org.BouncyCastle.Ocsp.Tests
 {
 	public class AllTests
 	{
-		public static void Main(
-			string[] args)
-		{
-			//junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("OCSP Tests");
-
-			suite.Add(new OcspTest());
-
-			return suite;
-		}
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OCSP Tests");
+                suite.Add(new OcspTest());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/openpgp/examples/test/AllTests.cs b/crypto/test/src/openpgp/examples/test/AllTests.cs
index 180d2fa80..e9a7a744d 100644
--- a/crypto/test/src/openpgp/examples/test/AllTests.cs
+++ b/crypto/test/src/openpgp/examples/test/AllTests.cs
@@ -382,22 +382,20 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp.Examples.Tests
 			return bRd.ReadLine();
 		}
 
-		public static void Main(
-			string[] args)
-		{
-			//junit.textui.TestRunner.run(suite());
-			EventListener el = new NullListener();
-			suite().Run(el);
-		}
-
-		public static TestSuite suite()
-		{
-			TestSuite suite = new TestSuite("OpenPGP Example Tests");
-
-			suite.Add(new AllTests());
-
-			return suite;
-		}
-
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
+
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OpenPGP Example Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
 	}
 }
diff --git a/crypto/test/src/openssl/test/AllTests.cs b/crypto/test/src/openssl/test/AllTests.cs
index 921208179..f843d0479 100644
--- a/crypto/test/src/openssl/test/AllTests.cs
+++ b/crypto/test/src/openssl/test/AllTests.cs
@@ -32,19 +32,24 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 				return (char[]) password.Clone();
 			}
 		}
-		
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("OpenSSL Tests");
-				suite.Add(new AllTests());
-				return suite;
-			}
-		}
 
-		[Test]
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
+
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("OpenSSL Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestOpenSsl()
 		{
 			Org.BouncyCastle.Utilities.Test.ITest[] tests = new Org.BouncyCastle.Utilities.Test.ITest[]{
@@ -121,13 +126,5 @@ namespace Org.BouncyCastle.OpenSsl.Tests
 
 			Assert.AreEqual(privKey, rdKey);
 		}
-
-        public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }
diff --git a/crypto/test/src/tsp/test/AllTests.cs b/crypto/test/src/tsp/test/AllTests.cs
index 66dc9c480..3e8b0cd5e 100644
--- a/crypto/test/src/tsp/test/AllTests.cs
+++ b/crypto/test/src/tsp/test/AllTests.cs
@@ -9,24 +9,23 @@ namespace Org.BouncyCastle.Tsp.Tests
 {
     public class AllTests
     {
-        public static void Main(
-			string[] args)
+        public static void Main(string[] args)
         {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            suite().Run(el);
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
         }
 
-		public static TestSuite suite()
+        [Suite]
+        public static TestSuite Suite
         {
-            TestSuite suite = new TestSuite("TSP Tests");
-
-			suite.Add(new GenTimeAccuracyUnitTest());
-			suite.Add(new ParseTest());
-			suite.Add(new TimeStampTokenInfoUnitTest());
-			suite.Add(new TspTest());
-
-			return suite;
+            get
+            {
+                TestSuite suite = new TestSuite("TSP Tests");
+                suite.Add(new GenTimeAccuracyUnitTest());
+                suite.Add(new ParseTest());
+                suite.Add(new TimeStampTokenInfoUnitTest());
+                suite.Add(new TspTest());
+                return suite;
+            }
         }
     }
 }
diff --git a/crypto/test/src/util/io/pem/test/AllTests.cs b/crypto/test/src/util/io/pem/test/AllTests.cs
index b44949383..c36f79304 100644
--- a/crypto/test/src/util/io/pem/test/AllTests.cs
+++ b/crypto/test/src/util/io/pem/test/AllTests.cs
@@ -19,18 +19,23 @@ namespace Org.BouncyCastle.Utilities.IO.Pem.Tests
 	[TestFixture]
 	public class AllTests
 	{
-		[Suite]
-		public static TestSuite Suite
-		{
-			get
-			{
-				TestSuite suite = new TestSuite("PEM Utilities Tests");
-				suite.Add(new AllTests());
-				return suite;
-			}
-		}
+        public static void Main(string[] args)
+        {
+            Suite.Run(new NullListener(), NUnit.Core.TestFilter.Empty);
+        }
 
-		[Test]
+        [Suite]
+        public static TestSuite Suite
+        {
+            get
+            {
+                TestSuite suite = new TestSuite("PEM Utilities Tests");
+                suite.Add(new AllTests());
+                return suite;
+            }
+        }
+
+        [Test]
 		public void TestPemLength()
 		{
 			for (int i = 1; i != 60; i++)
@@ -65,13 +70,5 @@ namespace Org.BouncyCastle.Utilities.IO.Pem.Tests
 
 			Assert.AreEqual(sw.ToString().Length, pWrt.GetOutputSize(pemObj));
 		}
-
-		public static void Main(
-			string[] args)
-        {
-            //junit.textui.TestRunner.run(suite());
-            EventListener el = new NullListener();
-            Suite.Run(el);
-        }
 	}
 }