summary refs log tree commit diff
path: root/Crypto/src/crypto/tls
diff options
context:
space:
mode:
Diffstat (limited to 'Crypto/src/crypto/tls')
-rw-r--r--Crypto/src/crypto/tls/AlertDescription.cs47
-rw-r--r--Crypto/src/crypto/tls/AlertLevel.cs11
-rw-r--r--Crypto/src/crypto/tls/AlwaysValidVerifyer.cs24
-rw-r--r--Crypto/src/crypto/tls/ByteQueue.cs125
-rw-r--r--Crypto/src/crypto/tls/Certificate.cs111
-rw-r--r--Crypto/src/crypto/tls/CertificateRequest.cs28
-rw-r--r--Crypto/src/crypto/tls/CipherSuite.cs136
-rw-r--r--Crypto/src/crypto/tls/ClientCertificateType.cs20
-rw-r--r--Crypto/src/crypto/tls/CombinedHash.cs82
-rw-r--r--Crypto/src/crypto/tls/CompressionMethod.cs20
-rw-r--r--Crypto/src/crypto/tls/ContentType.cs13
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs67
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs73
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsClient.cs259
-rw-r--r--Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs76
-rw-r--r--Crypto/src/crypto/tls/DigestAlgorithm.cs21
-rw-r--r--Crypto/src/crypto/tls/ECCurveType.cs29
-rw-r--r--Crypto/src/crypto/tls/ECPointFormat.cs16
-rw-r--r--Crypto/src/crypto/tls/EncryptionAlgorithm.cs32
-rw-r--r--Crypto/src/crypto/tls/ExtensionType.cs31
-rw-r--r--Crypto/src/crypto/tls/HandshakeType.cs19
-rw-r--r--Crypto/src/crypto/tls/ICertificateVerifyer.cs18
-rw-r--r--Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs36
-rw-r--r--Crypto/src/crypto/tls/LegacyTlsAuthentication.cs30
-rw-r--r--Crypto/src/crypto/tls/LegacyTlsClient.cs26
-rw-r--r--Crypto/src/crypto/tls/NamedCurve.cs72
-rw-r--r--Crypto/src/crypto/tls/PskTlsClient.cs182
-rw-r--r--Crypto/src/crypto/tls/RecordStream.cs166
-rw-r--r--Crypto/src/crypto/tls/SecurityParameters.cs26
-rw-r--r--Crypto/src/crypto/tls/SrpTlsClient.cs188
-rw-r--r--Crypto/src/crypto/tls/Ssl3Mac.cs114
-rw-r--r--Crypto/src/crypto/tls/TlsAgreementCredentials.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsAuthentication.cs31
-rw-r--r--Crypto/src/crypto/tls/TlsBlockCipher.cs248
-rw-r--r--Crypto/src/crypto/tls/TlsCipher.cs14
-rw-r--r--Crypto/src/crypto/tls/TlsCipherFactory.cs12
-rw-r--r--Crypto/src/crypto/tls/TlsClient.cs129
-rw-r--r--Crypto/src/crypto/tls/TlsClientContext.cs15
-rw-r--r--Crypto/src/crypto/tls/TlsClientContextImpl.cs37
-rw-r--r--Crypto/src/crypto/tls/TlsCompression.cs12
-rw-r--r--Crypto/src/crypto/tls/TlsCredentials.cs9
-rw-r--r--Crypto/src/crypto/tls/TlsDHKeyExchange.cs201
-rw-r--r--Crypto/src/crypto/tls/TlsDHUtilities.cs70
-rw-r--r--Crypto/src/crypto/tls/TlsDeflateCompression.cs45
-rw-r--r--Crypto/src/crypto/tls/TlsDheKeyExchange.cs56
-rw-r--r--Crypto/src/crypto/tls/TlsDsaSigner.cs51
-rw-r--r--Crypto/src/crypto/tls/TlsDssSigner.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsECDHKeyExchange.cs230
-rw-r--r--Crypto/src/crypto/tls/TlsECDheKeyExchange.cs110
-rw-r--r--Crypto/src/crypto/tls/TlsECDsaSigner.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsException.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsFatalAlert.cs21
-rw-r--r--Crypto/src/crypto/tls/TlsKeyExchange.cs38
-rw-r--r--Crypto/src/crypto/tls/TlsMac.cs106
-rw-r--r--Crypto/src/crypto/tls/TlsNullCipher.cs28
-rw-r--r--Crypto/src/crypto/tls/TlsNullCompression.cs19
-rw-r--r--Crypto/src/crypto/tls/TlsProtocolHandler.cs1259
-rw-r--r--Crypto/src/crypto/tls/TlsPskIdentity.cs15
-rw-r--r--Crypto/src/crypto/tls/TlsPskKeyExchange.cs149
-rw-r--r--Crypto/src/crypto/tls/TlsRsaKeyExchange.cs165
-rw-r--r--Crypto/src/crypto/tls/TlsRsaSigner.cs53
-rw-r--r--Crypto/src/crypto/tls/TlsRsaUtilities.cs42
-rw-r--r--Crypto/src/crypto/tls/TlsSigner.cs18
-rw-r--r--Crypto/src/crypto/tls/TlsSignerCredentials.cs11
-rw-r--r--Crypto/src/crypto/tls/TlsSrpKeyExchange.cs203
-rw-r--r--Crypto/src/crypto/tls/TlsStream.cs89
-rw-r--r--Crypto/src/crypto/tls/TlsUtilities.cs286
67 files changed, 5934 insertions, 0 deletions
diff --git a/Crypto/src/crypto/tls/AlertDescription.cs b/Crypto/src/crypto/tls/AlertDescription.cs
new file mode 100644
index 000000000..e1229a4a3
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlertDescription.cs
@@ -0,0 +1,47 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 7.2
+	/// </summary>
+	public enum AlertDescription : byte
+	{
+		close_notify = 0,
+		unexpected_message = 10,
+		bad_record_mac = 20,
+		decryption_failed = 21,
+		record_overflow = 22,
+		decompression_failure = 30,
+		handshake_failure = 40,
+		/* 41 is not defined, for historical reasons */
+		bad_certificate = 42,
+		unsupported_certificate = 43,
+		certificate_revoked = 44,
+		certificate_expired = 45,
+		certificate_unknown = 46,
+		illegal_parameter = 47,
+		unknown_ca = 48,
+		access_denied = 49,
+		decode_error = 50,
+		decrypt_error = 51,
+		export_restriction = 60,
+		protocol_version = 70,
+		insufficient_security = 71,
+		internal_error = 80,
+		user_canceled = 90,
+		no_renegotiation = 100,
+
+		/*
+		 *  RFC 3546
+		 */
+		unsupported_extension = 110,
+		certificate_unobtainable = 111,
+		unrecognized_name = 112,
+		bad_certificate_status_response = 113,
+		bad_certificate_hash_value = 114,
+
+		/*
+		 *  RFC 4279
+		 */
+		unknown_psk_identity = 115,
+	}
+}
diff --git a/Crypto/src/crypto/tls/AlertLevel.cs b/Crypto/src/crypto/tls/AlertLevel.cs
new file mode 100644
index 000000000..afb04308b
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlertLevel.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 7.2
+	/// </summary>
+    public enum AlertLevel : byte
+	{
+	    warning = 1,
+	    fatal = 2,
+	}
+}
diff --git a/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs
new file mode 100644
index 000000000..e26c6fc3f
--- /dev/null
+++ b/Crypto/src/crypto/tls/AlwaysValidVerifyer.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>
+	/// A certificate verifyer, that will always return true.
+	/// <pre>
+	/// DO NOT USE THIS FILE UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING.
+	/// </pre>
+	/// </remarks>
+	[Obsolete("Perform certificate verification in TlsAuthentication implementation")]
+	public class AlwaysValidVerifyer
+		: ICertificateVerifyer
+	{
+		/// <summary>Return true.</summary>
+		public bool IsValid(
+			X509CertificateStructure[] certs)
+		{
+			return true;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/ByteQueue.cs b/Crypto/src/crypto/tls/ByteQueue.cs
new file mode 100644
index 000000000..96062402b
--- /dev/null
+++ b/Crypto/src/crypto/tls/ByteQueue.cs
@@ -0,0 +1,125 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>
+	/// A queue for bytes.
+	/// <p>
+	/// This file could be more optimized.
+	/// </p>
+	/// </remarks>
+	public class ByteQueue
+	{
+		/// <returns>The smallest number which can be written as 2^x which is bigger than i.</returns>
+		public static int NextTwoPow(
+			int i)
+		{
+			/*
+			* This code is based of a lot of code I found on the Internet
+			* which mostly referenced a book called "Hacking delight".
+			*
+			*/
+			i |= (i >> 1);
+			i |= (i >> 2);
+			i |= (i >> 4);
+			i |= (i >> 8);
+			i |= (i >> 16);
+			return i + 1;
+		}
+
+		/**
+		 * The initial size for our buffer.
+		 */
+		private const int InitBufSize = 1024;
+
+		/**
+		 * The buffer where we store our data.
+		 */
+		private byte[] databuf = new byte[ByteQueue.InitBufSize];
+
+		/**
+		 * How many bytes at the beginning of the buffer are skipped.
+		 */
+		private int skipped = 0;
+
+		/**
+		 * How many bytes in the buffer are valid data.
+		 */
+		private int available = 0;
+
+		/// <summary>Read data from the buffer.</summary>
+		/// <param name="buf">The buffer where the read data will be copied to.</param>
+		/// <param name="offset">How many bytes to skip at the beginning of buf.</param>
+		/// <param name="len">How many bytes to read at all.</param>
+		/// <param name="skip">How many bytes from our data to skip.</param>
+		public void Read(
+			byte[]	buf,
+			int		offset,
+			int		len,
+			int		skip)
+		{
+			if ((available - skip) < len)
+			{
+				throw new TlsException("Not enough data to read");
+			}
+			if ((buf.Length - offset) < len)
+			{
+				throw new TlsException("Buffer size of " + buf.Length + " is too small for a read of " + len + " bytes");
+			}
+			Array.Copy(databuf, skipped + skip, buf, offset, len);
+		}
+
+		/// <summary>Add some data to our buffer.</summary>
+		/// <param name="data">A byte-array to read data from.</param>
+		/// <param name="offset">How many bytes to skip at the beginning of the array.</param>
+		/// <param name="len">How many bytes to read from the array.</param>
+		public void AddData(
+			byte[]	data,
+			int		offset,
+			int		len)
+		{
+			if ((skipped + available + len) > databuf.Length)
+			{
+				byte[] tmp = new byte[ByteQueue.NextTwoPow(data.Length)];
+				Array.Copy(databuf, skipped, tmp, 0, available);
+				skipped = 0;
+				databuf = tmp;
+			}
+			Array.Copy(data, offset, databuf, skipped + available, len);
+			available += len;
+		}
+
+		/// <summary>Remove some bytes from our data from the beginning.</summary>
+		/// <param name="i">How many bytes to remove.</param>
+		public void RemoveData(
+			int i)
+		{
+			if (i > available)
+			{
+				throw new TlsException("Cannot remove " + i + " bytes, only got " + available);
+			}
+
+			/*
+			* Skip the data.
+			*/
+			available -= i;
+			skipped += i;
+
+			/*
+			* If more than half of our data is skipped, we will move the data
+			* in the buffer.
+			*/
+			if (skipped > (databuf.Length / 2))
+			{
+				Array.Copy(databuf, skipped, databuf, 0, available);
+				skipped = 0;
+			}
+		}
+
+		/// <summary>The number of bytes which are available in this buffer.</summary>
+		public int Available
+		{
+			get { return available; }
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/Certificate.cs b/Crypto/src/crypto/tls/Certificate.cs
new file mode 100644
index 000000000..e4df041e2
--- /dev/null
+++ b/Crypto/src/crypto/tls/Certificate.cs
@@ -0,0 +1,111 @@
+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
+{
+	/**
+	* A representation for a certificate chain.
+	*/
+	public class Certificate
+	{
+		public static readonly Certificate EmptyChain = new Certificate(new X509CertificateStructure[0]);
+
+		/**
+		* The certificates.
+		*/
+		internal X509CertificateStructure[] certs;
+
+		/**
+		* Parse the ServerCertificate message.
+		*
+		* @param inStr The stream where to parse from.
+		* @return A Certificate object with the certs, the server has sended.
+		* @throws IOException If something goes wrong during parsing.
+		*/
+		internal static Certificate Parse(
+			Stream inStr)
+		{
+			int left = TlsUtilities.ReadUint24(inStr);
+			if (left == 0)
+			{
+				return EmptyChain;
+			}
+			IList tmp = Platform.CreateArrayList();
+			while (left > 0)
+			{
+				int size = TlsUtilities.ReadUint24(inStr);
+				left -= 3 + size;
+				byte[] buf = new byte[size];
+				TlsUtilities.ReadFully(buf, inStr);
+				MemoryStream bis = new MemoryStream(buf, false);
+				Asn1Object o = Asn1Object.FromStream(bis);
+				tmp.Add(X509CertificateStructure.GetInstance(o));
+				if (bis.Position < bis.Length)
+				{
+					throw new ArgumentException("Sorry, there is garbage data left after the certificate");
+				}
+			}
+            X509CertificateStructure[] certs = new X509CertificateStructure[tmp.Count];
+            for (int i = 0; i < tmp.Count; ++i)
+            {
+                certs[i] = (X509CertificateStructure)tmp[i];
+            }
+			return new Certificate(certs);
+		}
+
+		/**
+		 * Encodes version of the ClientCertificate message
+		 *
+		 * @param outStr stream to write the message to
+		 * @throws IOException If something goes wrong
+		 */
+		internal void Encode(
+			Stream outStr)
+		{
+			IList encCerts = Platform.CreateArrayList();
+			int totalSize = 0;
+			foreach (X509CertificateStructure cert in certs)
+			{
+				byte[] encCert = cert.GetEncoded(Asn1Encodable.Der);
+				encCerts.Add(encCert);
+				totalSize += encCert.Length + 3;
+			}
+
+			TlsUtilities.WriteUint24(totalSize, outStr);
+
+			foreach (byte[] encCert in encCerts)
+			{
+				TlsUtilities.WriteOpaque24(encCert, outStr);
+			}
+		}
+
+		/**
+		* Private constructor from a cert array.
+		*
+		* @param certs The certs the chain should contain.
+		*/
+		public Certificate(X509CertificateStructure[] certs)
+		{
+			if (certs == null)
+				throw new ArgumentNullException("certs");
+
+			this.certs = certs;
+		}
+
+		/// <returns>An array which contains the certs, this chain contains.</returns>
+		public X509CertificateStructure[] GetCerts()
+		{
+			return (X509CertificateStructure[]) certs.Clone();
+		}
+
+		public bool IsEmpty
+		{
+			get { return certs.Length == 0; }
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/CertificateRequest.cs b/Crypto/src/crypto/tls/CertificateRequest.cs
new file mode 100644
index 000000000..49d8ba6fb
--- /dev/null
+++ b/Crypto/src/crypto/tls/CertificateRequest.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class CertificateRequest
+	{
+		private ClientCertificateType[] certificateTypes;
+		private IList certificateAuthorities;
+
+		public CertificateRequest(ClientCertificateType[] certificateTypes, IList certificateAuthorities)
+		{
+			this.certificateTypes = certificateTypes;
+			this.certificateAuthorities = certificateAuthorities;
+		}
+
+		public ClientCertificateType[] CertificateTypes
+		{
+			get { return certificateTypes; }
+		}
+
+		/// <returns>A <see cref="IList"/> of X509Name</returns>
+		public IList CertificateAuthorities
+		{
+			get { return certificateAuthorities; }
+		}
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/CipherSuite.cs b/Crypto/src/crypto/tls/CipherSuite.cs
new file mode 100644
index 000000000..6e1f7a545
--- /dev/null
+++ b/Crypto/src/crypto/tls/CipherSuite.cs
@@ -0,0 +1,136 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 A.5
+	/// </summary>
+	public enum CipherSuite : int
+	{
+		TLS_NULL_WITH_NULL_NULL = 0x0000,
+		TLS_RSA_WITH_NULL_MD5 = 0x0001,
+		TLS_RSA_WITH_NULL_SHA = 0x0002,
+		TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003,
+		TLS_RSA_WITH_RC4_128_MD5 = 0x0004,
+		TLS_RSA_WITH_RC4_128_SHA = 0x0005,
+		TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006,
+		TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007,
+		TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008,
+		TLS_RSA_WITH_DES_CBC_SHA = 0x0009,
+		TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A,
+		TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B,
+		TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C,
+		TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D,
+		TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E,
+		TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F,
+		TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010,
+		TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011,
+		TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012,
+		TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013,
+		TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014,
+		TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015,
+		TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016,
+		TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017,
+		TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018,
+		TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019,
+		TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A,
+		TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B,
+
+		/*
+		 * RFC 3268
+		 */
+		TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F,
+		TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030,
+		TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031,
+		TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032,
+		TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033,
+		TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034,
+		TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035,
+		TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036,
+		TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037,
+		TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038,
+		TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039,
+		TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A,
+
+		/*
+		 * RFC 4279
+		 */
+		TLS_PSK_WITH_RC4_128_SHA = 0x008A,
+		TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B,
+		TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
+		TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D,
+		TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E,
+		TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F,
+		TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090,
+		TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091,
+		TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092,
+		TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093,
+		TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094,
+		TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095,
+
+		/*
+		 * RFC 4492
+		 */
+		TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001,
+		TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002,
+		TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003,
+		TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004,
+		TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005,
+		TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006,
+		TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007,
+		TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008,
+		TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009,
+		TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A,
+		TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B,
+		TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C,
+		TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D,
+		TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E,
+		TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F,
+		TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010,
+		TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011,
+		TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012,
+		TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+		TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+		TLS_ECDH_anon_WITH_NULL_SHA = 0xC015,
+		TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016,
+		TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017,
+		TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018,
+		TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019,
+
+		/*
+		 * RFC 5054
+		 */
+		TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A,
+		TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B,
+		TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C,
+		TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D,
+		TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E,
+		TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F,
+		TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020,
+		TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021,
+		TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022,
+
+		/*
+		 * RFC 5289
+		 */
+		TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+		TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024,
+		TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025,
+		TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026,
+		TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+		TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028,
+		TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029,
+		TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A,
+		TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
+		TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C,
+		TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D,
+		TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E,
+		TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
+		TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030,
+		TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031,
+		TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032,
+
+		/*
+		 * RFC 5746
+		 */
+		TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF,
+	}
+}
diff --git a/Crypto/src/crypto/tls/ClientCertificateType.cs b/Crypto/src/crypto/tls/ClientCertificateType.cs
new file mode 100644
index 000000000..58f5d4276
--- /dev/null
+++ b/Crypto/src/crypto/tls/ClientCertificateType.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 7.4.4
+	/// </summary>
+    public enum ClientCertificateType : byte
+	{
+		rsa_sign = 1,
+		dss_sign = 2,
+		rsa_fixed_dh = 3,
+		dss_fixed_dh = 4,
+
+		/*
+		 * RFC 4492 5.5
+		 */
+		ecdsa_sign = 64,
+		rsa_fixed_ecdh = 65,
+		ecdsa_fixed_ecdh = 66,
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/CombinedHash.cs b/Crypto/src/crypto/tls/CombinedHash.cs
new file mode 100644
index 000000000..59ad87a7b
--- /dev/null
+++ b/Crypto/src/crypto/tls/CombinedHash.cs
@@ -0,0 +1,82 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>A combined hash, which implements md5(m) || sha1(m).</remarks>
+	internal class CombinedHash
+		: IDigest
+	{
+		private readonly MD5Digest md5;
+		private readonly Sha1Digest sha1;
+
+		internal CombinedHash()
+		{
+			this.md5 = new MD5Digest();
+			this.sha1 = new Sha1Digest();
+		}
+
+		internal CombinedHash(CombinedHash t)
+		{
+			this.md5 = new MD5Digest(t.md5);
+			this.sha1 = new Sha1Digest(t.sha1);
+		}
+
+		/// <seealso cref="IDigest.AlgorithmName"/>
+		public string AlgorithmName
+		{
+			get
+			{
+				return md5.AlgorithmName + " and " + sha1.AlgorithmName + " for TLS 1.0";
+			}
+		}
+
+		/// <seealso cref="IDigest.GetByteLength"/>
+		public int GetByteLength()
+		{
+			return System.Math.Max(md5.GetByteLength(), sha1.GetByteLength());
+		}
+
+		/// <seealso cref="IDigest.GetDigestSize"/>
+		public int GetDigestSize()
+		{
+			return md5.GetDigestSize() + sha1.GetDigestSize();
+		}
+
+		/// <seealso cref="IDigest.Update"/>
+		public void Update(
+			byte input)
+		{
+			md5.Update(input);
+			sha1.Update(input);
+		}
+
+		/// <seealso cref="IDigest.BlockUpdate"/>
+		public void BlockUpdate(
+			byte[]	input,
+			int		inOff,
+			int		len)
+		{
+			md5.BlockUpdate(input, inOff, len);
+			sha1.BlockUpdate(input, inOff, len);
+		}
+
+		/// <seealso cref="IDigest.DoFinal"/>
+		public int DoFinal(
+			byte[]	output,
+			int		outOff)
+		{
+			int i1 = md5.DoFinal(output, outOff);
+			int i2 = sha1.DoFinal(output, outOff + i1);
+			return i1 + i2;
+		}
+
+		/// <seealso cref="IDigest.Reset"/>
+		public void Reset()
+		{
+			md5.Reset();
+			sha1.Reset();
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/CompressionMethod.cs b/Crypto/src/crypto/tls/CompressionMethod.cs
new file mode 100644
index 000000000..4a127a63e
--- /dev/null
+++ b/Crypto/src/crypto/tls/CompressionMethod.cs
@@ -0,0 +1,20 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 6.1
+	/// </summary>
+    public enum CompressionMethod : byte
+	{
+		NULL = 0,
+
+		/*
+		 * RFC 3749 2
+		 */
+		DEFLATE = 1
+
+		/*
+		 * Values from 224 decimal (0xE0) through 255 decimal (0xFF)
+		 * inclusive are reserved for private use.
+		 */
+	}
+}
diff --git a/Crypto/src/crypto/tls/ContentType.cs b/Crypto/src/crypto/tls/ContentType.cs
new file mode 100644
index 000000000..a664e3a38
--- /dev/null
+++ b/Crypto/src/crypto/tls/ContentType.cs
@@ -0,0 +1,13 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 6.2.1
+	/// </summary>
+    public enum ContentType : byte
+	{
+		change_cipher_spec = 20,
+		alert = 21,
+		handshake = 22,
+		application_data = 23,
+	}
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
new file mode 100644
index 000000000..2dfe526d1
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class DefaultTlsAgreementCredentials
+		: TlsAgreementCredentials
+	{
+		protected Certificate clientCert;
+		protected AsymmetricKeyParameter clientPrivateKey;
+
+		protected IBasicAgreement basicAgreement;
+
+		public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+		{
+			if (clientCertificate == null)
+			{
+				throw new ArgumentNullException("clientCertificate");
+			}
+			if (clientCertificate.certs.Length == 0)
+			{
+				throw new ArgumentException("cannot be empty", "clientCertificate");
+			}
+			if (clientPrivateKey == null)
+			{
+				throw new ArgumentNullException("clientPrivateKey");
+			}
+			if (!clientPrivateKey.IsPrivate)
+			{
+				throw new ArgumentException("must be private", "clientPrivateKey");
+			}
+
+			if (clientPrivateKey is DHPrivateKeyParameters)
+			{
+				basicAgreement = new DHBasicAgreement();
+			}
+			else if (clientPrivateKey is ECPrivateKeyParameters)
+			{
+				basicAgreement = new ECDHBasicAgreement();
+			}
+			else
+			{
+				throw new ArgumentException("type not supported: "
+					+ clientPrivateKey.GetType().FullName, "clientPrivateKey");
+			}
+
+			this.clientCert = clientCertificate;
+			this.clientPrivateKey = clientPrivateKey;
+		}
+
+		public virtual Certificate Certificate
+		{
+			get { return clientCert; }
+		}
+
+		public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey)
+		{
+			basicAgreement.Init(clientPrivateKey);
+			BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey);
+			return BigIntegers.AsUnsignedByteArray(agreementValue);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
new file mode 100644
index 000000000..53e3438d9
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -0,0 +1,73 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class DefaultTlsCipherFactory
+		: TlsCipherFactory
+	{
+		public virtual TlsCipher CreateCipher(TlsClientContext context,
+			EncryptionAlgorithm encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
+		{
+			switch (encryptionAlgorithm)
+			{
+				case EncryptionAlgorithm.cls_3DES_EDE_CBC:
+					return CreateDesEdeCipher(context, 24, digestAlgorithm);
+				case EncryptionAlgorithm.AES_128_CBC:
+					return CreateAesCipher(context, 16, digestAlgorithm);
+				case EncryptionAlgorithm.AES_256_CBC:
+					return CreateAesCipher(context, 32, digestAlgorithm);
+				default:
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		/// <exception cref="IOException"></exception>
+		protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize,
+			DigestAlgorithm digestAlgorithm)
+		{
+			return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
+				CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+		}
+
+		/// <exception cref="IOException"></exception>
+		protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize,
+			DigestAlgorithm digestAlgorithm)
+		{
+			return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(),
+				CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
+		}
+
+		protected virtual IBlockCipher CreateAesBlockCipher()
+		{
+			return new CbcBlockCipher(new AesFastEngine());
+		}
+
+		protected virtual IBlockCipher CreateDesEdeBlockCipher()
+		{
+			return new CbcBlockCipher(new DesEdeEngine());
+		}
+
+		/// <exception cref="IOException"></exception>
+		protected virtual IDigest CreateDigest(DigestAlgorithm digestAlgorithm)
+		{
+			switch (digestAlgorithm)
+			{
+				case DigestAlgorithm.MD5:
+					return new MD5Digest();
+				case DigestAlgorithm.SHA:
+					return new Sha1Digest();
+				case DigestAlgorithm.SHA256:
+					return new Sha256Digest();
+				case DigestAlgorithm.SHA384:
+					return new Sha384Digest();
+				default:
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsClient.cs b/Crypto/src/crypto/tls/DefaultTlsClient.cs
new file mode 100644
index 000000000..c5b59a06b
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsClient.cs
@@ -0,0 +1,259 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Modes;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public abstract class DefaultTlsClient
+		: TlsClient
+	{
+		protected TlsCipherFactory cipherFactory;
+
+		protected TlsClientContext context;
+
+        protected CompressionMethod selectedCompressionMethod;
+        protected CipherSuite selectedCipherSuite;
+
+		public DefaultTlsClient()
+			: this(new DefaultTlsCipherFactory())
+		{
+		}
+
+		public DefaultTlsClient(TlsCipherFactory cipherFactory)
+		{
+			this.cipherFactory = cipherFactory;
+		}
+
+		public virtual void Init(TlsClientContext context)
+		{
+			this.context = context;
+		}
+
+        public virtual CipherSuite[] GetCipherSuites()
+		{
+			return new CipherSuite[] {
+				CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
+			};
+		}
+
+		public virtual CompressionMethod[] GetCompressionMethods()
+        {
+			/*
+			 * To offer DEFLATE compression, override this method:
+			 *     return new CompressionMethod[] { CompressionMethod.DEFLATE, CompressionMethod.NULL };
+			 */
+
+            return new CompressionMethod[] { CompressionMethod.NULL };
+        }
+
+        public virtual IDictionary GetClientExtensions()
+		{
+			return null;
+		}
+
+        public virtual void NotifySessionID(byte[] sessionID)
+		{
+			// Currently ignored
+		}
+
+        public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+		{
+			this.selectedCipherSuite = selectedCipherSuite;
+		}
+
+        public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+        {
+            this.selectedCompressionMethod = selectedCompressionMethod;
+        }
+
+        public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+		{
+			if (!secureRenegotiation)
+			{
+				/*
+				 * RFC 5746 3.4.
+				 * If the extension is not present, the server does not support
+				 * secure renegotiation; set secure_renegotiation flag to FALSE.
+				 * In this case, some clients may want to terminate the handshake
+				 * instead of continuing; see Section 4.1 for discussion.
+				 */
+//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
+			}
+		}
+
+        public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+		{
+		}
+
+        public virtual TlsKeyExchange GetKeyExchange()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+					return CreateRsaKeyExchange();
+
+				case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+					return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_DSS);
+
+				case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+					return CreateDHKeyExchange(KeyExchangeAlgorithm.DH_RSA);
+
+				case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+					return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_DSS);
+
+				case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+					return CreateDheKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
+
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+                    return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_ECDSA);
+
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+                    return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
+
+                case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+                    return CreateECDHKeyExchange(KeyExchangeAlgorithm.ECDH_RSA);
+
+                case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+                    return CreateECDheKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
+
+				default:
+					/*
+					* Note: internal error here; the TlsProtocolHandler verifies that the
+					* server-selected cipher suite was in the list of client-offered cipher
+					* suites, so if we now can't produce an implementation, we shouldn't have
+					* offered it!
+					*/
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public abstract TlsAuthentication GetAuthentication();
+
+		public virtual TlsCompression GetCompression()
+		{
+			switch (selectedCompressionMethod)
+			{
+				case CompressionMethod.NULL:
+					return new TlsNullCompression();
+
+				case CompressionMethod.DEFLATE:
+					return new TlsDeflateCompression();
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected compression method was in the list of client-offered compression
+					 * methods, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual TlsCipher GetCipher()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+
+				case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+
+				case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+                case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
+                case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA:
+                case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+
+				default:
+					/*
+					* Note: internal error here; the TlsProtocolHandler verifies that the
+					* server-selected cipher suite was in the list of client-offered cipher
+					* suites, so if we now can't produce an implementation, we shouldn't have
+					* offered it!
+					*/
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		protected virtual TlsKeyExchange CreateDHKeyExchange(KeyExchangeAlgorithm keyExchange)
+		{
+			return new TlsDHKeyExchange(context, keyExchange);
+		}
+
+        protected virtual TlsKeyExchange CreateDheKeyExchange(KeyExchangeAlgorithm keyExchange)
+		{
+			return new TlsDheKeyExchange(context, keyExchange);
+		}
+
+        protected virtual TlsKeyExchange CreateECDHKeyExchange(KeyExchangeAlgorithm keyExchange)
+        {
+            return new TlsECDHKeyExchange(context, keyExchange);
+        }
+
+        protected virtual TlsKeyExchange CreateECDheKeyExchange(KeyExchangeAlgorithm keyExchange)
+        {
+            return new TlsECDheKeyExchange(context, keyExchange);
+        }
+
+        protected virtual TlsKeyExchange CreateRsaKeyExchange()
+		{
+			return new TlsRsaKeyExchange(context);
+		}
+    }
+}
diff --git a/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
new file mode 100644
index 000000000..23d607d85
--- /dev/null
+++ b/Crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -0,0 +1,76 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class DefaultTlsSignerCredentials
+		: TlsSignerCredentials
+	{
+		protected TlsClientContext context;
+		protected Certificate clientCert;
+		protected AsymmetricKeyParameter clientPrivateKey;
+
+		protected TlsSigner clientSigner;
+
+		public DefaultTlsSignerCredentials(TlsClientContext context,
+			Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+		{
+			if (clientCertificate == null)
+			{
+				throw new ArgumentNullException("clientCertificate");
+			}
+			if (clientCertificate.certs.Length == 0)
+			{
+				throw new ArgumentException("cannot be empty", "clientCertificate");
+			}
+			if (clientPrivateKey == null)
+			{
+				throw new ArgumentNullException("clientPrivateKey");
+			}
+			if (!clientPrivateKey.IsPrivate)
+			{
+				throw new ArgumentException("must be private", "clientPrivateKey");
+			}
+
+			if (clientPrivateKey is RsaKeyParameters)
+			{
+				clientSigner = new TlsRsaSigner();
+			}
+			else if (clientPrivateKey is DsaPrivateKeyParameters)
+			{
+				clientSigner = new TlsDssSigner();
+			}
+			else if (clientPrivateKey is ECPrivateKeyParameters)
+			{
+				clientSigner = new TlsECDsaSigner();
+			}
+			else
+			{
+				throw new ArgumentException("type not supported: "
+					+ clientPrivateKey.GetType().FullName, "clientPrivateKey");
+			}
+
+			this.context = context;
+			this.clientCert = clientCertificate;
+			this.clientPrivateKey = clientPrivateKey;
+		}
+
+		public virtual Certificate Certificate
+		{
+			get { return clientCert; }
+		}
+
+		public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1)
+		{
+			try
+			{
+				return clientSigner.CalculateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1);
+			}
+			catch (CryptoException)
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/DigestAlgorithm.cs b/Crypto/src/crypto/tls/DigestAlgorithm.cs
new file mode 100644
index 000000000..cede6b7f8
--- /dev/null
+++ b/Crypto/src/crypto/tls/DigestAlgorithm.cs
@@ -0,0 +1,21 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public enum DigestAlgorithm
+	{
+		/*
+		 * Note that the values here are implementation-specific and arbitrary.
+		 * It is recommended not to depend on the particular values (e.g. serialization).
+		 */
+		NULL,
+		MD5,
+		SHA,
+
+		/*
+		 * RFC 5289
+		 */
+		SHA256,
+		SHA384,
+	}
+}
diff --git a/Crypto/src/crypto/tls/ECCurveType.cs b/Crypto/src/crypto/tls/ECCurveType.cs
new file mode 100644
index 000000000..15d5d7b42
--- /dev/null
+++ b/Crypto/src/crypto/tls/ECCurveType.cs
@@ -0,0 +1,29 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 4492 5.4
+	/// </summary>
+    public enum ECCurveType : byte
+	{
+		/**
+		 * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+		 * underlying finite field is a prime field.
+		 */
+		explicit_prime = 1,
+
+		/**
+		 * Indicates the elliptic curve domain parameters are conveyed verbosely, and the
+		 * underlying finite field is a characteristic-2 field.
+		 */
+		explicit_char2 = 2,
+
+		/**
+		 * Indicates that a named curve is used. This option SHOULD be used when applicable.
+		 */
+		named_curve = 3,
+
+		/*
+		 * Values 248 through 255 are reserved for private use.
+		 */
+	}
+}
diff --git a/Crypto/src/crypto/tls/ECPointFormat.cs b/Crypto/src/crypto/tls/ECPointFormat.cs
new file mode 100644
index 000000000..4e0dd0067
--- /dev/null
+++ b/Crypto/src/crypto/tls/ECPointFormat.cs
@@ -0,0 +1,16 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 4492 5.1.2
+	/// </summary>
+    public enum ECPointFormat : byte
+	{
+		uncompressed = 0,
+		ansiX962_compressed_prime = 1,
+		ansiX962_compressed_char2 = 2,
+
+		/*
+		 * reserved (248..255)
+		 */
+	}
+}
diff --git a/Crypto/src/crypto/tls/EncryptionAlgorithm.cs b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs
new file mode 100644
index 000000000..79d3b63b5
--- /dev/null
+++ b/Crypto/src/crypto/tls/EncryptionAlgorithm.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public enum EncryptionAlgorithm
+	{
+		/*
+		 * Note that the values here are implementation-specific and arbitrary.
+		 * It is recommended not to depend on the particular values (e.g. serialization).
+		 */
+		NULL,
+		RC4_40,
+		RC4_128,
+		RC2_CBC_40,
+		IDEA_CBC,
+		DES40_CBC,
+		DES_CBC,
+		cls_3DES_EDE_CBC,
+
+		/*
+		 * RFC 3268
+		 */
+		AES_128_CBC,
+		AES_256_CBC,
+
+		/*
+		 * RFC 5289
+		 */
+		AES_128_GCM,
+		AES_256_GCM,
+	}
+}
diff --git a/Crypto/src/crypto/tls/ExtensionType.cs b/Crypto/src/crypto/tls/ExtensionType.cs
new file mode 100644
index 000000000..f00e34e3f
--- /dev/null
+++ b/Crypto/src/crypto/tls/ExtensionType.cs
@@ -0,0 +1,31 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 4366 2.3
+	/// </summary>
+	public enum ExtensionType : int
+	{
+		server_name = 0,
+		max_fragment_length = 1,
+		client_certificate_url = 2,
+		trusted_ca_keys = 3,
+		truncated_hmac = 4,
+		status_request = 5,
+
+		/*
+		 * RFC 4492
+		 */
+		elliptic_curves = 10,
+		ec_point_formats = 11,
+
+		/*
+		 * RFC 5054 2.8.1
+		 */
+		srp = 12,
+
+		/*
+		 * RFC 5746 6
+		 */
+		renegotiation_info = 0xff01,
+	}
+}
diff --git a/Crypto/src/crypto/tls/HandshakeType.cs b/Crypto/src/crypto/tls/HandshakeType.cs
new file mode 100644
index 000000000..deedb1f84
--- /dev/null
+++ b/Crypto/src/crypto/tls/HandshakeType.cs
@@ -0,0 +1,19 @@
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 2246 7.4
+	/// </summary>
+    public enum HandshakeType : byte
+	{
+		hello_request = 0,
+		client_hello = 1,
+		server_hello = 2,
+		certificate = 11,
+		server_key_exchange = 12,
+		certificate_request = 13,
+		server_hello_done = 14,
+		certificate_verify = 15,
+		client_key_exchange = 16,
+		finished = 20,
+	}
+}
diff --git a/Crypto/src/crypto/tls/ICertificateVerifyer.cs b/Crypto/src/crypto/tls/ICertificateVerifyer.cs
new file mode 100644
index 000000000..df5ea51d7
--- /dev/null
+++ b/Crypto/src/crypto/tls/ICertificateVerifyer.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>
+	/// This should be implemented by any class which can find out, if a given
+	/// certificate chain is being accepted by an client.
+	/// </remarks>
+	[Obsolete("Perform certificate verification in TlsAuthentication implementation")]
+	public interface ICertificateVerifyer
+	{
+		/// <param name="certs">The certs, which are part of the chain.</param>
+		/// <returns>True, if the chain is accepted, false otherwise</returns>
+		bool IsValid(X509CertificateStructure[] certs);
+	}
+}
diff --git a/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
new file mode 100644
index 000000000..3fdbeb2a6
--- /dev/null
+++ b/Crypto/src/crypto/tls/KeyExchangeAlgorithm.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public enum KeyExchangeAlgorithm
+	{
+		/*
+		 * Note that the values here are implementation-specific and arbitrary.
+		 * It is recommended not to depend on the particular values (e.g. serialization).
+		 */
+		NULL,
+		RSA,
+		RSA_EXPORT,
+		DHE_DSS,
+		DHE_DSS_EXPORT,
+		DHE_RSA,
+		DHE_RSA_EXPORT,
+		DH_DSS,
+		DH_DSS_EXPORT,
+		DH_RSA,
+		DH_RSA_EXPORT,
+		DH_anon,
+		DH_anon_export,
+		PSK,
+		DHE_PSK,
+		RSA_PSK,
+		ECDH_ECDSA,
+		ECDHE_ECDSA,
+		ECDH_RSA,
+		ECDHE_RSA,
+		ECDH_anon,
+		SRP,
+		SRP_DSS,
+		SRP_RSA,
+	}
+}
diff --git a/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs
new file mode 100644
index 000000000..395f94208
--- /dev/null
+++ b/Crypto/src/crypto/tls/LegacyTlsAuthentication.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// A temporary class to wrap old CertificateVerifyer stuff for new TlsAuthentication.
+	/// </summary>
+	[Obsolete]
+	public class LegacyTlsAuthentication
+		: TlsAuthentication
+	{
+		protected ICertificateVerifyer verifyer;
+
+		public LegacyTlsAuthentication(ICertificateVerifyer verifyer)
+		{
+			this.verifyer = verifyer;
+		}
+
+		public virtual void NotifyServerCertificate(Certificate serverCertificate)
+		{
+			if (!this.verifyer.IsValid(serverCertificate.GetCerts()))
+				throw new TlsFatalAlert(AlertDescription.user_canceled);
+		}
+
+		public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+		{
+			return null;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/LegacyTlsClient.cs b/Crypto/src/crypto/tls/LegacyTlsClient.cs
new file mode 100644
index 000000000..fbb9a732e
--- /dev/null
+++ b/Crypto/src/crypto/tls/LegacyTlsClient.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// A temporary class to use LegacyTlsAuthentication 
+	/// </summary>
+	[Obsolete]
+	public class LegacyTlsClient
+		: DefaultTlsClient
+	{
+		[Obsolete]
+		protected ICertificateVerifyer verifyer;
+
+		[Obsolete]
+		public LegacyTlsClient(ICertificateVerifyer verifyer)
+		{
+			this.verifyer = verifyer;
+		}
+
+		public override TlsAuthentication GetAuthentication()
+		{
+			return new LegacyTlsAuthentication(verifyer);
+		}
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/NamedCurve.cs b/Crypto/src/crypto/tls/NamedCurve.cs
new file mode 100644
index 000000000..c8ee189aa
--- /dev/null
+++ b/Crypto/src/crypto/tls/NamedCurve.cs
@@ -0,0 +1,72 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Sec;
+using Org.BouncyCastle.Asn1.X9;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// RFC 4492 5.1.1
+	/// The named curves defined here are those specified in SEC 2 [13]. Note that many of
+ 	/// these curves are also recommended in ANSI X9.62 [7] and FIPS 186-2 [11]. Values 0xFE00
+	/// through 0xFEFF are reserved for private use. Values 0xFF01 and 0xFF02 indicate that the
+	/// client supports arbitrary prime and characteristic-2 curves, respectively (the curve
+	/// parameters must be encoded explicitly in ECParameters).
+	/// </summary>
+	public enum NamedCurve : int
+	{
+		sect163k1 = 1,
+		sect163r1 = 2,
+		sect163r2 = 3,
+		sect193r1 = 4,
+		sect193r2 = 5,
+		sect233k1 = 6,
+		sect233r1 = 7,
+		sect239k1 = 8,
+		sect283k1 = 9,
+		sect283r1 = 10,
+		sect409k1 = 11,
+		sect409r1 = 12,
+		sect571k1 = 13,
+		sect571r1 = 14,
+		secp160k1 = 15,
+		secp160r1 = 16,
+		secp160r2 = 17,
+		secp192k1 = 18,
+		secp192r1 = 19,
+		secp224k1 = 20,
+		secp224r1 = 21,
+		secp256k1 = 22,
+		secp256r1 = 23,
+		secp384r1 = 24,
+		secp521r1 = 25,
+
+		/*
+		 * reserved (0xFE00..0xFEFF)
+		 */
+
+		arbitrary_explicit_prime_curves = 0xFF01,
+		arbitrary_explicit_char2_curves = 0xFF02,
+	}
+
+	internal class NamedCurveHelper
+	{
+	    internal static ECDomainParameters GetECParameters(NamedCurve namedCurve)
+	    {
+            if (!Enum.IsDefined(typeof(NamedCurve), namedCurve))
+                return null;
+
+            string curveName = namedCurve.ToString();
+
+            // Lazily created the first time a particular curve is accessed
+	        X9ECParameters ecP = SecNamedCurves.GetByName(curveName);
+
+            if (ecP == null)
+                return null;
+
+	        // It's a bit inefficient to do this conversion every time
+	        return new ECDomainParameters(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed());
+	    }
+	}
+}
diff --git a/Crypto/src/crypto/tls/PskTlsClient.cs b/Crypto/src/crypto/tls/PskTlsClient.cs
new file mode 100644
index 000000000..16975e713
--- /dev/null
+++ b/Crypto/src/crypto/tls/PskTlsClient.cs
@@ -0,0 +1,182 @@
+using System;
+using System.Collections;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class PskTlsClient
+		:TlsClient
+	{
+		protected TlsCipherFactory cipherFactory;
+		protected TlsPskIdentity pskIdentity;
+
+		protected TlsClientContext context;
+
+		protected CompressionMethod selectedCompressionMethod;
+		protected CipherSuite selectedCipherSuite;
+
+		public PskTlsClient(TlsPskIdentity pskIdentity)
+			: this(new DefaultTlsCipherFactory(), pskIdentity)
+		{
+		}
+
+		public PskTlsClient(TlsCipherFactory cipherFactory, TlsPskIdentity pskIdentity)
+		{
+			this.cipherFactory = cipherFactory;
+			this.pskIdentity = pskIdentity;
+		}
+
+		public virtual void Init(TlsClientContext context)
+		{
+			this.context = context;
+		}
+
+		public virtual CipherSuite[] GetCipherSuites()
+		{
+			return new CipherSuite[] {
+				CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA,
+			};
+		}
+
+		public virtual IDictionary GetClientExtensions()
+		{
+			return null;
+		}
+
+		public virtual CompressionMethod[] GetCompressionMethods()
+		{
+			return new CompressionMethod[] { CompressionMethod.NULL };
+		}
+
+		public virtual void NotifySessionID(byte[] sessionID)
+		{
+			// Currently ignored 
+		}
+
+		public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+		{
+			this.selectedCipherSuite = selectedCipherSuite;
+		}
+
+		public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+		{
+			this.selectedCompressionMethod = selectedCompressionMethod;
+		}
+
+		public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+		{
+			if (!secureRenegotiation)
+			{
+				/*
+				 * RFC 5746 3.4. If the extension is not present, the server does not support
+				 * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
+				 * some clients may want to terminate the handshake instead of continuing; see
+				 * Section 4.1 for discussion.
+				 */
+//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
+			}
+		}
+
+		public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+		{
+		}
+
+		public virtual TlsKeyExchange GetKeyExchange()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+					return CreatePskKeyExchange(KeyExchangeAlgorithm.PSK);
+
+				case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+					return CreatePskKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
+
+				case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+					return CreatePskKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected cipher suite was in the list of client-offered cipher
+					 * suites, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual TlsAuthentication GetAuthentication()
+		{
+			return null;
+		}
+
+		public virtual TlsCompression GetCompression()
+		{
+			switch (selectedCompressionMethod)
+			{
+				case CompressionMethod.NULL:
+					return new TlsNullCompression();
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected compression method was in the list of client-offered compression
+					 * methods, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual TlsCipher GetCipher()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC,
+						DigestAlgorithm.SHA);
+				
+				case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC,
+						DigestAlgorithm.SHA);
+
+				case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC,
+						DigestAlgorithm.SHA);
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected cipher suite was in the list of client-offered cipher
+					 * suites, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		protected virtual TlsKeyExchange CreatePskKeyExchange(KeyExchangeAlgorithm keyExchange)
+		{
+			return new TlsPskKeyExchange(context, keyExchange, pskIdentity);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/RecordStream.cs b/Crypto/src/crypto/tls/RecordStream.cs
new file mode 100644
index 000000000..e18894b4e
--- /dev/null
+++ b/Crypto/src/crypto/tls/RecordStream.cs
@@ -0,0 +1,166 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>An implementation of the TLS 1.0 record layer.</remarks>
+	internal class RecordStream
+	{
+		private TlsProtocolHandler handler;
+		private Stream inStr;
+		private Stream outStr;
+		private CombinedHash hash;
+		private TlsCompression readCompression = null;
+		private TlsCompression writeCompression = null;
+		private TlsCipher readCipher = null;
+		private TlsCipher writeCipher = null;
+		private MemoryStream buffer = new MemoryStream();
+
+		internal RecordStream(
+			TlsProtocolHandler	handler,
+			Stream				inStr,
+			Stream				outStr)
+		{
+			this.handler = handler;
+			this.inStr = inStr;
+			this.outStr = outStr;
+			this.hash = new CombinedHash();
+			this.readCompression = new TlsNullCompression();
+			this.writeCompression = this.readCompression;
+			this.readCipher = new TlsNullCipher();
+			this.writeCipher = this.readCipher;
+		}
+
+		internal void ClientCipherSpecDecided(TlsCompression tlsCompression, TlsCipher tlsCipher)
+		{
+			this.writeCompression = tlsCompression;
+			this.writeCipher = tlsCipher;
+		}
+
+		internal void ServerClientSpecReceived()
+		{
+			this.readCompression = this.writeCompression;
+			this.readCipher = this.writeCipher;
+		}
+
+		public void ReadData()
+		{
+			ContentType type = (ContentType)TlsUtilities.ReadUint8(inStr);
+			TlsUtilities.CheckVersion(inStr);
+			int size = TlsUtilities.ReadUint16(inStr);
+			byte[] buf = DecodeAndVerify(type, inStr, size);
+			handler.ProcessData(type, buf, 0, buf.Length);
+		}
+
+		internal byte[] DecodeAndVerify(
+			ContentType	type,
+			Stream		inStr,
+			int			len)
+		{
+			byte[] buf = new byte[len];
+			TlsUtilities.ReadFully(buf, inStr);
+			byte[] decoded = readCipher.DecodeCiphertext(type, buf, 0, buf.Length);
+
+			Stream cOut = readCompression.Decompress(buffer);
+
+			if (cOut == buffer)
+			{
+				return decoded;
+			}
+
+			cOut.Write(decoded, 0, decoded.Length);
+			cOut.Flush();
+			byte[] contents = buffer.ToArray();
+			buffer.SetLength(0);
+			return contents;
+		}
+
+		internal void WriteMessage(
+			ContentType	type,
+			byte[]		message,
+			int			offset,
+			int			len)
+		{
+			if (type == ContentType.handshake)
+			{
+				UpdateHandshakeData(message, offset, len);
+			}
+
+			Stream cOut = writeCompression.Compress(buffer);
+
+			byte[] ciphertext;
+			if (cOut == buffer)
+			{
+				ciphertext = writeCipher.EncodePlaintext(type, message, offset, len);
+			}
+			else
+			{
+				cOut.Write(message, offset, len);
+				cOut.Flush();
+				ciphertext = writeCipher.EncodePlaintext(type, buffer.ToArray(), 0, (int)buffer.Position);
+				buffer.SetLength(0);
+			}
+
+			byte[] writeMessage = new byte[ciphertext.Length + 5];
+            TlsUtilities.WriteUint8((byte)type, writeMessage, 0);
+			TlsUtilities.WriteVersion(writeMessage, 1);
+			TlsUtilities.WriteUint16(ciphertext.Length, writeMessage, 3);
+			Array.Copy(ciphertext, 0, writeMessage, 5, ciphertext.Length);
+			outStr.Write(writeMessage, 0, writeMessage.Length);
+			outStr.Flush();
+		}
+
+		internal void UpdateHandshakeData(
+			byte[]	message,
+			int		offset,
+			int		len)
+		{
+			hash.BlockUpdate(message, offset, len);
+		}
+
+		internal byte[] GetCurrentHash()
+		{
+			return DoFinal(new CombinedHash(hash));
+		}
+
+		internal void Close()
+		{
+			IOException e = null;
+			try
+			{
+                inStr.Dispose();
+			}
+			catch (IOException ex)
+			{
+				e = ex;
+			}
+
+			try
+			{
+				// NB: This is harmless if outStr == inStr
+                outStr.Dispose();
+			}
+			catch (IOException ex)
+			{
+				e = ex;
+			}
+
+			if (e != null)
+			{
+				throw e;
+			}
+		}
+
+		internal void Flush()
+		{
+			outStr.Flush();
+		}
+
+		private static byte[] DoFinal(CombinedHash ch)
+		{
+			byte[] bs = new byte[ch.GetDigestSize()];
+			ch.DoFinal(bs, 0);
+			return bs;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/SecurityParameters.cs b/Crypto/src/crypto/tls/SecurityParameters.cs
new file mode 100644
index 000000000..9ed3969eb
--- /dev/null
+++ b/Crypto/src/crypto/tls/SecurityParameters.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class SecurityParameters
+	{
+		internal byte[] clientRandom = null;
+		internal byte[] serverRandom = null;
+		internal byte[] masterSecret = null;
+
+		public byte[] ClientRandom
+		{
+			get { return clientRandom; }
+		}
+
+		public byte[] ServerRandom
+		{
+			get { return serverRandom; }
+		}
+
+		public byte[] MasterSecret
+		{
+			get { return masterSecret; }
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/SrpTlsClient.cs b/Crypto/src/crypto/tls/SrpTlsClient.cs
new file mode 100644
index 000000000..6c2638bb3
--- /dev/null
+++ b/Crypto/src/crypto/tls/SrpTlsClient.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public abstract class SrpTlsClient
+		: TlsClient
+	{
+		protected TlsCipherFactory cipherFactory;
+		protected byte[] identity;
+		protected byte[] password;
+
+		protected TlsClientContext context;
+
+        protected CompressionMethod selectedCompressionMethod;
+        protected CipherSuite selectedCipherSuite;
+
+		public SrpTlsClient(byte[] identity, byte[] password)
+			: this(new DefaultTlsCipherFactory(), identity, password)
+		{
+		}
+
+		public SrpTlsClient(TlsCipherFactory cipherFactory, byte[] identity, byte[] password)
+		{
+			this.cipherFactory = cipherFactory;
+			this.identity = Arrays.Clone(identity);
+			this.password = Arrays.Clone(password);
+		}
+
+		public virtual void Init(TlsClientContext context)
+		{
+			this.context = context;
+		}
+
+		public virtual CipherSuite[] GetCipherSuites()
+		{
+			return new CipherSuite[] {
+				CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
+				CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,
+			};
+		}
+
+		public virtual IDictionary GetClientExtensions()
+		{
+			IDictionary clientExtensions = Platform.CreateHashtable();
+
+			MemoryStream srpData = new MemoryStream();
+			TlsUtilities.WriteOpaque8(this.identity, srpData);
+			clientExtensions[ExtensionType.srp] = srpData.ToArray();
+
+			return clientExtensions;
+		}
+
+		public virtual CompressionMethod[] GetCompressionMethods()
+		{
+			return new CompressionMethod[] { CompressionMethod.NULL };
+		}
+
+		public virtual void NotifySessionID(byte[] sessionID)
+		{
+			// Currently ignored 
+		}
+
+		public virtual void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite)
+		{
+			this.selectedCipherSuite = selectedCipherSuite;
+		}
+
+		public virtual void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod)
+		{
+            this.selectedCompressionMethod = selectedCompressionMethod;
+        }
+
+		public virtual void NotifySecureRenegotiation(bool secureRenegotiation)
+		{
+			if (!secureRenegotiation)
+			{
+				/*
+				 * RFC 5746 3.4. If the extension is not present, the server does not support
+				 * secure renegotiation; set secure_renegotiation flag to FALSE. In this case,
+				 * some clients may want to terminate the handshake instead of continuing; see
+				 * Section 4.1 for discussion.
+				 */
+//				throw new TlsFatalAlert(AlertDescription.handshake_failure);
+			}
+		}
+
+		public virtual void ProcessServerExtensions(IDictionary serverExtensions)
+		{
+			// There is no server response for the SRP extension
+		}
+
+		public virtual TlsKeyExchange GetKeyExchange()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+					return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP);
+
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+					return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_RSA);
+
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+					return CreateSrpKeyExchange(KeyExchangeAlgorithm.SRP_DSS);
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected cipher suite was in the list of client-offered cipher
+					 * suites, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+		
+		public abstract TlsAuthentication GetAuthentication();
+
+		public virtual TlsCompression GetCompression()
+		{
+			switch (selectedCompressionMethod)
+			{
+				case CompressionMethod.NULL:
+					return new TlsNullCompression();
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected compression method was in the list of client-offered compression
+					 * methods, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual TlsCipher GetCipher()
+		{
+			switch (selectedCipherSuite)
+			{
+				case CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.cls_3DES_EDE_CBC, DigestAlgorithm.SHA);
+
+				case CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_128_CBC, DigestAlgorithm.SHA);
+
+				case CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA:
+				case CipherSuite.TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA:
+					return cipherFactory.CreateCipher(context, EncryptionAlgorithm.AES_256_CBC, DigestAlgorithm.SHA);
+
+				default:
+					/*
+					 * Note: internal error here; the TlsProtocolHandler verifies that the
+					 * server-selected cipher suite was in the list of client-offered cipher
+					 * suites, so if we now can't produce an implementation, we shouldn't have
+					 * offered it!
+					 */
+					throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		protected virtual TlsKeyExchange CreateSrpKeyExchange(KeyExchangeAlgorithm keyExchange)
+		{
+			return new TlsSrpKeyExchange(context, keyExchange, identity, password);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/Ssl3Mac.cs b/Crypto/src/crypto/tls/Ssl3Mac.cs
new file mode 100644
index 000000000..b2f3f309e
--- /dev/null
+++ b/Crypto/src/crypto/tls/Ssl3Mac.cs
@@ -0,0 +1,114 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/**
+	 * HMAC implementation based on original internet draft for HMAC (RFC 2104)
+	 * 
+	 * The difference is that padding is concatentated versus XORed with the key
+	 * 
+	 * H(K + opad, H(K + ipad, text))
+	 */
+	public class Ssl3Mac
+		: IMac
+	{
+		private const byte IPAD = 0x36;
+		private const byte OPAD = 0x5C;
+
+		internal static readonly byte[] MD5_IPAD = GenPad(IPAD, 48);
+		internal static readonly byte[] MD5_OPAD = GenPad(OPAD, 48);
+		internal static readonly byte[] SHA1_IPAD = GenPad(IPAD, 40);
+		internal static readonly byte[] SHA1_OPAD = GenPad(OPAD, 40);
+
+		private IDigest digest;
+
+		private byte[] secret;
+		private byte[] ipad, opad;
+
+		/**
+		 * Base constructor for one of the standard digest algorithms that the byteLength of
+		 * the algorithm is know for. Behaviour is undefined for digests other than MD5 or SHA1.
+		 * 
+		 * @param digest the digest.
+		 */
+		public Ssl3Mac(IDigest digest)
+		{
+			this.digest = digest;
+
+	        if (digest.GetDigestSize() == 20)
+	        {
+	            this.ipad = SHA1_IPAD;
+	            this.opad = SHA1_OPAD;
+	        }
+	        else
+	        {
+	            this.ipad = MD5_IPAD;
+	            this.opad = MD5_OPAD;
+	        }
+		}
+
+		public virtual string AlgorithmName
+		{
+			get { return digest.AlgorithmName + "/SSL3MAC"; }
+		}
+
+		public virtual void Init(ICipherParameters parameters)
+		{
+			secret = Arrays.Clone(((KeyParameter)parameters).GetKey());
+
+			Reset();
+		}
+
+		public virtual int GetMacSize()
+		{
+			return digest.GetDigestSize();
+		}
+
+		public virtual void Update(byte input)
+		{
+			digest.Update(input);
+		}
+
+		public virtual void BlockUpdate(byte[] input, int inOff, int len)
+		{
+			digest.BlockUpdate(input, inOff, len);
+		}
+
+		public virtual int DoFinal(byte[] output, int outOff)
+		{
+			byte[] tmp = new byte[digest.GetDigestSize()];
+			digest.DoFinal(tmp, 0);
+
+			digest.BlockUpdate(secret, 0, secret.Length);
+			digest.BlockUpdate(opad, 0, opad.Length);
+			digest.BlockUpdate(tmp, 0, tmp.Length);
+
+			int len = digest.DoFinal(output, outOff);
+
+			Reset();
+
+			return len;
+		}
+
+		/**
+		 * Reset the mac generator.
+		 */
+		public virtual void Reset()
+		{
+			digest.Reset();
+			digest.BlockUpdate(secret, 0, secret.Length);
+			digest.BlockUpdate(ipad, 0, ipad.Length);
+		}
+
+		private static byte[] GenPad(byte b, int count)
+		{
+			byte[] padding = new byte[count];
+			Arrays.Fill(padding, b);
+			return padding;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsAgreementCredentials.cs b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs
new file mode 100644
index 000000000..46ee4f90e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsAgreementCredentials : TlsCredentials
+	{
+		/// <exception cref="IOException"></exception>
+		byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsAuthentication.cs b/Crypto/src/crypto/tls/TlsAuthentication.cs
new file mode 100644
index 000000000..9aea5e449
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsAuthentication.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsAuthentication
+	{
+		/// <summary>
+		/// Called by the protocol handler to report the server certificate.
+		/// </summary>
+		/// <remarks>
+		/// This method is responsible for certificate verification and validation
+		/// </remarks>
+		/// <param name="serverCertificate">The server <see cref="Certificate"/> received</param>
+		/// <exception cref="IOException"></exception>
+		void NotifyServerCertificate(Certificate serverCertificate);
+
+		/// <summary>
+		/// Return client credentials in response to server's certificate request
+		/// </summary>
+		/// <param name="certificateRequest">
+		/// A <see cref="CertificateRequest"/> containing server certificate request details
+		/// </param>
+		/// <returns>
+		/// A <see cref="TlsCredentials"/> to be used for client authentication
+		/// (or <c>null</c> for no client authentication)
+		/// </returns>
+		/// <exception cref="IOException"></exception>
+		TlsCredentials GetClientCredentials(CertificateRequest certificateRequest);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsBlockCipher.cs b/Crypto/src/crypto/tls/TlsBlockCipher.cs
new file mode 100644
index 000000000..ef7be1913
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -0,0 +1,248 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// A generic TLS 1.0 block cipher. This can be used for AES or 3DES for example.
+	/// </summary>
+	public class TlsBlockCipher
+        : TlsCipher
+	{
+		protected TlsClientContext context;
+
+        protected IBlockCipher encryptCipher;
+        protected IBlockCipher decryptCipher;
+
+        protected TlsMac wMac;
+        protected TlsMac rMac;
+
+		public virtual TlsMac WriteMac
+		{
+            get { return wMac; }
+		}
+
+		public virtual TlsMac ReadMac
+		{
+            get { return rMac; }
+		}
+
+		public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher,
+			IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
+		{
+			this.context = context;
+			this.encryptCipher = encryptCipher;
+			this.decryptCipher = decryptCipher;
+
+			int prfSize = (2 * cipherKeySize) + writeDigest.GetDigestSize()
+				+ readDigest.GetDigestSize() + encryptCipher.GetBlockSize()
+				+ decryptCipher.GetBlockSize();
+
+			SecurityParameters securityParameters = context.SecurityParameters;
+
+			byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
+				TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
+				prfSize);
+
+			int offset = 0;
+
+			// Init MACs
+			wMac = CreateTlsMac(writeDigest, keyBlock, ref offset);
+            rMac = CreateTlsMac(readDigest, keyBlock, ref offset);
+
+			// Build keys
+			KeyParameter encryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+			KeyParameter decryptKey = CreateKeyParameter(keyBlock, ref offset, cipherKeySize);
+
+			// Add IVs
+			ParametersWithIV encryptParams = CreateParametersWithIV(encryptKey,
+				keyBlock, ref offset, encryptCipher.GetBlockSize());
+			ParametersWithIV decryptParams = CreateParametersWithIV(decryptKey,
+				keyBlock, ref offset, decryptCipher.GetBlockSize());
+
+			if (offset != prfSize)
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+
+			// Init Ciphers
+			encryptCipher.Init(true, encryptParams);
+			decryptCipher.Init(false, decryptParams);
+		}
+
+        protected virtual TlsMac CreateTlsMac(IDigest digest, byte[] buf, ref int off)
+		{
+			int len = digest.GetDigestSize();
+			TlsMac mac = new TlsMac(digest, buf, off, len);
+			off += len;
+			return mac;
+		}
+
+        protected virtual KeyParameter CreateKeyParameter(byte[] buf, ref int off, int len)
+		{
+			KeyParameter key = new KeyParameter(buf, off, len);
+			off += len;
+			return key;
+		}
+
+        protected virtual ParametersWithIV CreateParametersWithIV(KeyParameter key,
+			byte[] buf, ref int off, int len)
+		{
+			ParametersWithIV ivParams = new ParametersWithIV(key, buf, off, len);
+			off += len;
+			return ivParams;
+		}
+
+		public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len)
+		{
+			int blocksize = encryptCipher.GetBlockSize();
+
+			// Add a random number of extra blocks worth of padding
+            int minPaddingSize = blocksize - ((len + wMac.Size + 1) % blocksize);
+			int maxExtraPadBlocks = (255 - minPaddingSize) / blocksize;
+			int actualExtraPadBlocks = ChooseExtraPadBlocks(context.SecureRandom, maxExtraPadBlocks);
+			int paddingsize = minPaddingSize + (actualExtraPadBlocks * blocksize);
+
+            int totalsize = len + wMac.Size + paddingsize + 1;
+			byte[] outbuf = new byte[totalsize];
+			Array.Copy(plaintext, offset, outbuf, 0, len);
+            byte[] mac = wMac.CalculateMac(type, plaintext, offset, len);
+			Array.Copy(mac, 0, outbuf, len, mac.Length);
+			int paddoffset = len + mac.Length;
+			for (int i = 0; i <= paddingsize; i++)
+			{
+				outbuf[i + paddoffset] = (byte)paddingsize;
+			}
+			for (int i = 0; i < totalsize; i += blocksize)
+			{
+				encryptCipher.ProcessBlock(outbuf, i, outbuf, i);
+			}
+			return outbuf;
+		}
+
+        public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len)
+		{
+			// TODO TLS 1.1 (RFC 4346) introduces an explicit IV
+
+            int minLength = rMac.Size + 1;
+			int blocksize = decryptCipher.GetBlockSize();
+			bool decrypterror = false;
+
+			/*
+			* ciphertext must be at least (macsize + 1) bytes long
+			*/
+			if (len < minLength)
+			{
+				throw new TlsFatalAlert(AlertDescription.decode_error);
+			}
+
+			/*
+			* ciphertext must be a multiple of blocksize
+			*/
+			if (len % blocksize != 0)
+			{
+				throw new TlsFatalAlert(AlertDescription.decryption_failed);
+			}
+
+			/*
+			* Decrypt all the ciphertext using the blockcipher
+			*/
+			for (int i = 0; i < len; i += blocksize)
+			{
+				decryptCipher.ProcessBlock(ciphertext, i + offset, ciphertext, i + offset);
+			}
+
+			/*
+			* Check if padding is correct
+			*/
+			int lastByteOffset = offset + len - 1;
+
+			byte paddingsizebyte = ciphertext[lastByteOffset];
+
+			int paddingsize = paddingsizebyte;
+
+			int maxPaddingSize = len - minLength;
+			if (paddingsize > maxPaddingSize)
+			{
+				decrypterror = true;
+				paddingsize = 0;
+			}
+			else
+			{
+				/*
+				* Now, check all the padding-bytes (constant-time comparison).
+				*/
+				byte diff = 0;
+				for (int i = lastByteOffset - paddingsize; i < lastByteOffset; ++i)
+				{
+					diff |= (byte)(ciphertext[i] ^ paddingsizebyte);
+				}
+				if (diff != 0)
+				{
+					/* Wrong padding */
+					decrypterror = true;
+					paddingsize = 0;
+				}
+			}
+
+			/*
+			* We now don't care if padding verification has failed or not, we will calculate
+			* the mac to give an attacker no kind of timing profile he can use to find out if
+			* mac verification failed or padding verification failed.
+			*/
+			int plaintextlength = len - minLength - paddingsize;
+            byte[] calculatedMac = rMac.CalculateMac(type, ciphertext, offset, plaintextlength);
+
+			/*
+			* Check all bytes in the mac (constant-time comparison).
+			*/
+			byte[] decryptedMac = new byte[calculatedMac.Length];
+			Array.Copy(ciphertext, offset + plaintextlength, decryptedMac, 0, calculatedMac.Length);
+
+			if (!Arrays.ConstantTimeAreEqual(calculatedMac, decryptedMac))
+			{
+				decrypterror = true;
+			}
+
+			/*
+			* Now, it is safe to fail.
+			*/
+			if (decrypterror)
+			{
+				throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+			}
+
+			byte[] plaintext = new byte[plaintextlength];
+			Array.Copy(ciphertext, offset, plaintext, 0, plaintextlength);
+			return plaintext;
+		}
+
+		protected virtual int ChooseExtraPadBlocks(SecureRandom r, int max)
+		{
+//			return r.NextInt(max + 1);
+
+			uint x = (uint)r.NextInt();
+			int n = LowestBitSet(x);
+			return System.Math.Min(n, max);
+		}
+
+        private int LowestBitSet(uint x)
+		{
+			if (x == 0)
+			{
+				return 32;
+			}
+
+			int n = 0;
+			while ((x & 1) == 0)
+			{
+				++n;
+				x >>= 1;
+			}
+			return n;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsCipher.cs b/Crypto/src/crypto/tls/TlsCipher.cs
new file mode 100644
index 000000000..22c769d82
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCipher.cs
@@ -0,0 +1,14 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsCipher
+	{
+		/// <exception cref="IOException"></exception>
+		byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len);
+
+		/// <exception cref="IOException"></exception>
+		byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsCipherFactory.cs b/Crypto/src/crypto/tls/TlsCipherFactory.cs
new file mode 100644
index 000000000..0756603f4
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsCipherFactory
+	{
+		/// <exception cref="IOException"></exception>
+		TlsCipher CreateCipher(TlsClientContext context, EncryptionAlgorithm encryptionAlgorithm,
+			DigestAlgorithm digestAlgorithm);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsClient.cs b/Crypto/src/crypto/tls/TlsClient.cs
new file mode 100644
index 000000000..eceaa3cd3
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClient.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsClient
+	{
+		/// <summary>
+		/// Called at the start of a new TLS session, before any other methods.
+		/// </summary>
+		/// <param name="context">
+		/// A <see cref="TlsProtocolHandler"/>
+		/// </param>
+		void Init(TlsClientContext context);
+
+		/// <summary>
+		/// Get the list of cipher suites that this client supports.
+		/// </summary>
+		/// <returns>
+        /// An array of <see cref="CipherSuite"/>, each specifying a supported cipher suite.
+		/// </returns>
+		CipherSuite[] GetCipherSuites();
+
+        /// <summary>
+        /// Get the list of compression methods that this client supports.
+        /// </summary>
+        /// <returns>
+        /// An array of <see cref="CompressionMethod"/>, each specifying a supported compression method.
+        /// </returns>
+        CompressionMethod[] GetCompressionMethods();
+
+		/// <summary>
+		/// Get the (optional) table of client extensions to be included in (extended) client hello.
+		/// </summary>
+		/// <returns>
+        /// A <see cref="IDictionary"/> (<see cref="ExtensionType"/> -> byte[]). May be null.
+		/// </returns>
+		/// <exception cref="IOException"></exception>
+		IDictionary GetClientExtensions();
+
+		/// <summary>
+		/// Reports the session ID once it has been determined.
+		/// </summary>
+		/// <param name="sessionID">
+		/// A <see cref="System.Byte"/>
+		/// </param>
+		void NotifySessionID(byte[] sessionID);
+
+		/// <summary>
+		/// Report the cipher suite that was selected by the server.
+		/// </summary>
+		/// <remarks>
+		/// The protocol handler validates this value against the offered cipher suites
+		/// <seealso cref="GetCipherSuites"/>
+		/// </remarks>
+		/// <param name="selectedCipherSuite">
+		/// A <see cref="CipherSuite"/>
+		/// </param>
+		void NotifySelectedCipherSuite(CipherSuite selectedCipherSuite);
+
+        /// <summary>
+        /// Report the compression method that was selected by the server.
+        /// </summary>
+        /// <remarks>
+        /// The protocol handler validates this value against the offered compression methods
+        /// <seealso cref="GetCompressionMethods"/>
+        /// </remarks>
+        /// <param name="selectedCompressionMethod">
+        /// A <see cref="CompressionMethod"/>
+        /// </param>
+        void NotifySelectedCompressionMethod(CompressionMethod selectedCompressionMethod);
+
+		/// <summary>
+		/// Report whether the server supports secure renegotiation
+		/// </summary>
+		/// <remarks>
+		/// The protocol handler automatically processes the relevant extensions
+		/// </remarks>
+		/// <param name="secureRenegotiation">
+		/// A <see cref="System.Boolean"/>, true if the server supports secure renegotiation
+		/// </param>
+		/// <exception cref="IOException"></exception>
+		void NotifySecureRenegotiation(bool secureRenegotiation);
+
+		/// <summary>
+		/// Report the extensions from an extended server hello.
+		/// </summary>
+		/// <remarks>
+		/// Will only be called if we returned a non-null result from <see cref="GetClientExtensions"/>.
+		/// </remarks>
+		/// <param name="serverExtensions">
+        /// A <see cref="IDictionary"/>  (<see cref="ExtensionType"/> -> byte[])
+		/// </param>
+		void ProcessServerExtensions(IDictionary serverExtensions);
+
+		/// <summary>
+		/// Return an implementation of <see cref="TlsKeyExchange"/> to negotiate the key exchange
+		/// part of the protocol.
+		/// </summary>
+		/// <returns>
+		/// A <see cref="TlsKeyExchange"/>
+		/// </returns>
+		/// <exception cref="IOException"/>
+		TlsKeyExchange GetKeyExchange();
+
+		/// <summary>
+		/// Return an implementation of <see cref="TlsAuthentication"/> to handle authentication
+		/// part of the protocol.
+		/// </summary>
+		/// <exception cref="IOException"/>
+		TlsAuthentication GetAuthentication();
+
+		/// <summary>
+		/// Return an implementation of <see cref="TlsCompression"/> to handle record compression.
+		/// </summary>
+		/// <exception cref="IOException"/>
+		TlsCompression GetCompression();
+
+		/// <summary>
+		/// Return an implementation of <see cref="TlsCipher"/> to use for encryption/decryption.
+		/// </summary>
+		/// <returns>
+		/// A <see cref="TlsCipher"/>
+		/// </returns>
+		/// <exception cref="IOException"/>
+		TlsCipher GetCipher();
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsClientContext.cs b/Crypto/src/crypto/tls/TlsClientContext.cs
new file mode 100644
index 000000000..dbb10aa76
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClientContext.cs
@@ -0,0 +1,15 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsClientContext
+	{
+		SecureRandom SecureRandom { get; }
+
+		SecurityParameters SecurityParameters { get; }
+
+		object UserObject { get; set; }
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsClientContextImpl.cs b/Crypto/src/crypto/tls/TlsClientContextImpl.cs
new file mode 100644
index 000000000..9d5dee232
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -0,0 +1,37 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsClientContextImpl
+		: TlsClientContext
+	{
+		private readonly SecureRandom secureRandom;
+		private readonly SecurityParameters securityParameters;
+
+		private object userObject = null;
+
+		internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+		{
+			this.secureRandom = secureRandom;
+			this.securityParameters = securityParameters;
+		}
+
+		public virtual SecureRandom SecureRandom
+		{
+			get { return secureRandom; }
+		}
+
+		public virtual SecurityParameters SecurityParameters
+		{
+			get { return securityParameters; }
+		}
+
+		public virtual object UserObject
+		{
+			get { return userObject; }
+			set { this.userObject = value; }
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsCompression.cs b/Crypto/src/crypto/tls/TlsCompression.cs
new file mode 100644
index 000000000..177d64b7e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCompression.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsCompression
+	{
+		Stream Compress(Stream output);
+
+		Stream Decompress(Stream output);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsCredentials.cs b/Crypto/src/crypto/tls/TlsCredentials.cs
new file mode 100644
index 000000000..5c5f1c02e
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsCredentials.cs
@@ -0,0 +1,9 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsCredentials
+	{
+		Certificate Certificate { get; }
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs
new file mode 100644
index 000000000..40ac416e0
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -0,0 +1,201 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// TLS 1.0 DH key exchange.
+	/// </summary>
+	internal class TlsDHKeyExchange
+		: TlsKeyExchange
+	{
+		protected TlsClientContext context;
+		protected KeyExchangeAlgorithm keyExchange;
+		protected TlsSigner tlsSigner;
+
+		protected AsymmetricKeyParameter serverPublicKey = null;
+		protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
+		protected TlsAgreementCredentials agreementCredentials;
+		protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+
+		internal TlsDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+		{
+			switch (keyExchange)
+			{
+				case KeyExchangeAlgorithm.DH_RSA:
+				case KeyExchangeAlgorithm.DH_DSS:
+					this.tlsSigner = null;
+					break;
+				case KeyExchangeAlgorithm.DHE_RSA:
+					this.tlsSigner = new TlsRsaSigner();
+					break;
+				case KeyExchangeAlgorithm.DHE_DSS:
+					this.tlsSigner = new TlsDssSigner();
+					break;
+				default:
+					throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+			}
+
+			this.context = context;
+			this.keyExchange = keyExchange;
+		}
+
+		public virtual void SkipServerCertificate()
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void ProcessServerCertificate(Certificate serverCertificate)
+		{
+			X509CertificateStructure x509Cert = serverCertificate.certs[0];
+			SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+			try
+			{
+				this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+			}
+			catch (Exception)
+			{
+				throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+			}
+
+			if (tlsSigner == null)
+			{
+				try
+				{
+					this.dhAgreeServerPublicKey = ValidateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+				}
+				catch (InvalidCastException)
+				{
+					throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+				}
+
+				TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
+			}
+			else
+			{
+				if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+				{
+					throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+				}
+
+				TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+			}
+
+			// TODO
+			/*
+			* Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+			* signing algorithm for the certificate must be the same as the algorithm for the
+			* certificate key."
+			*/
+		}
+
+		public virtual void SkipServerKeyExchange()
+		{
+			// OK
+		}
+
+		public virtual void ProcessServerKeyExchange(Stream input)
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			ClientCertificateType[] types = certificateRequest.CertificateTypes;
+			foreach (ClientCertificateType type in types)
+			{
+				switch (type)
+				{
+					case ClientCertificateType.rsa_sign:
+					case ClientCertificateType.dss_sign:
+					case ClientCertificateType.rsa_fixed_dh:
+					case ClientCertificateType.dss_fixed_dh:
+					case ClientCertificateType.ecdsa_sign:
+						break;
+					default:
+						throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+				}
+			}
+		}
+
+		public virtual void SkipClientCredentials()
+		{
+			this.agreementCredentials = null;
+		}
+
+		public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			if (clientCredentials is TlsAgreementCredentials)
+			{
+				// TODO Validate client cert has matching parameters (see 'areCompatibleParameters')?
+
+				this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+			}
+			else if (clientCredentials is TlsSignerCredentials)
+			{
+				// OK
+			}
+			else
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual void GenerateClientKeyExchange(Stream output)
+		{
+			/*
+			 * RFC 2246 7.4.7.2 If the client certificate already contains a suitable
+			 * Diffie-Hellman key, then Yc is implicit and does not need to be sent again. In
+			 * this case, the Client Key Exchange message will be sent, but will be empty.
+			 */
+			if (agreementCredentials == null)
+			{
+				GenerateEphemeralClientKeyExchange(dhAgreeServerPublicKey.Parameters, output);
+			}
+		}
+
+        public virtual byte[] GeneratePremasterSecret()
+		{
+			if (agreementCredentials != null)
+			{
+				return agreementCredentials.GenerateAgreement(dhAgreeServerPublicKey);
+			}
+
+			return CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+		}
+		
+		protected virtual bool AreCompatibleParameters(DHParameters a, DHParameters b)
+		{
+			return a.P.Equals(b.P) && a.G.Equals(b.G);
+		}
+
+		protected virtual byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
+			DHPrivateKeyParameters privateKey)
+		{
+			return TlsDHUtilities.CalculateDHBasicAgreement(publicKey, privateKey);
+		}
+
+		protected virtual AsymmetricCipherKeyPair GenerateDHKeyPair(DHParameters dhParams)
+		{
+			return TlsDHUtilities.GenerateDHKeyPair(context.SecureRandom, dhParams);
+		}
+
+		protected virtual void GenerateEphemeralClientKeyExchange(DHParameters dhParams, Stream output)
+		{
+			this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
+				context.SecureRandom, dhParams, output);
+		}
+
+		protected virtual DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
+		{
+			return TlsDHUtilities.ValidateDHPublicKey(key);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsDHUtilities.cs b/Crypto/src/crypto/tls/TlsDHUtilities.cs
new file mode 100644
index 000000000..733749ea1
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDHUtilities.cs
@@ -0,0 +1,70 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public abstract class TlsDHUtilities
+	{
+		public static byte[] CalculateDHBasicAgreement(DHPublicKeyParameters publicKey,
+			DHPrivateKeyParameters privateKey)
+		{
+			DHBasicAgreement dhAgree = new DHBasicAgreement();
+			dhAgree.Init(privateKey);
+			BigInteger agreement = dhAgree.CalculateAgreement(publicKey);
+			return BigIntegers.AsUnsignedByteArray(agreement);
+		}
+
+		public static AsymmetricCipherKeyPair GenerateDHKeyPair(SecureRandom random, DHParameters dhParams)
+		{
+			DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
+			dhGen.Init(new DHKeyGenerationParameters(random, dhParams));
+			return dhGen.GenerateKeyPair();
+		}
+
+		public static DHPrivateKeyParameters GenerateEphemeralClientKeyExchange(SecureRandom random,
+			DHParameters dhParams, Stream output)
+		{
+			AsymmetricCipherKeyPair dhAgreeClientKeyPair = GenerateDHKeyPair(random, dhParams);
+			DHPrivateKeyParameters dhAgreeClientPrivateKey =
+				(DHPrivateKeyParameters)dhAgreeClientKeyPair.Private;
+
+			BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.Public).Y;
+			byte[] keData = BigIntegers.AsUnsignedByteArray(Yc);
+			TlsUtilities.WriteOpaque16(keData, output);
+
+			return dhAgreeClientPrivateKey;
+		}
+		
+		public static DHPublicKeyParameters ValidateDHPublicKey(DHPublicKeyParameters key)
+		{
+			BigInteger Y = key.Y;
+			DHParameters parameters = key.Parameters;
+			BigInteger p = parameters.P;
+			BigInteger g = parameters.G;
+
+			if (!p.IsProbablePrime(2))
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+			if (g.CompareTo(BigInteger.Two) < 0 || g.CompareTo(p.Subtract(BigInteger.Two)) > 0)
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+			if (Y.CompareTo(BigInteger.Two) < 0 || Y.CompareTo(p.Subtract(BigInteger.One)) > 0)
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+
+			// TODO See RFC 2631 for more discussion of Diffie-Hellman validation
+
+			return key;
+		}
+	}
+}
\ No newline at end of file
diff --git a/Crypto/src/crypto/tls/TlsDeflateCompression.cs b/Crypto/src/crypto/tls/TlsDeflateCompression.cs
new file mode 100644
index 000000000..146c961c7
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDeflateCompression.cs
@@ -0,0 +1,45 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities.Zlib;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class TlsDeflateCompression
+		: TlsCompression
+	{
+		protected ZStream zIn, zOut;
+
+		public TlsDeflateCompression()
+		{
+			this.zIn = new ZStream();
+			this.zIn.inflateInit();
+
+			this.zOut = new ZStream();
+			// TODO Allow custom setting
+			this.zOut.deflateInit(JZlib.Z_DEFAULT_COMPRESSION);
+		}
+
+		public virtual Stream Compress(Stream output)
+		{
+			return new DeflateOutputStream(output, zOut, true);
+		}
+
+		public virtual Stream Decompress(Stream output)
+		{
+			return new DeflateOutputStream(output, zIn, false);
+		}
+
+		protected class DeflateOutputStream : ZOutputStream
+		{
+			public DeflateOutputStream(Stream output, ZStream z, bool compress)
+				: base(output)
+			{
+				this.z = z;
+				this.compress = compress;
+                // TODO http://www.bolet.org/~pornin/deflate-flush.html says we should use Z_SYNC_FLUSH
+				this.FlushMode = JZlib.Z_PARTIAL_FLUSH;
+			}
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs
new file mode 100644
index 000000000..edadaeb38
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsDheKeyExchange
+		: TlsDHKeyExchange
+	{
+		internal TlsDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+			: base(context, keyExchange)
+		{
+		}
+
+		public override void SkipServerKeyExchange()
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public override void ProcessServerKeyExchange(Stream input)
+		{
+			SecurityParameters securityParameters = context.SecurityParameters;
+
+			ISigner signer = InitSigner(tlsSigner, securityParameters);
+			Stream sigIn = new SignerStream(input, signer, null);
+
+			byte[] pBytes = TlsUtilities.ReadOpaque16(sigIn);
+			byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
+			byte[] YsBytes = TlsUtilities.ReadOpaque16(sigIn);
+
+			byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+			if (!signer.VerifySignature(sigByte))
+			{
+				throw new TlsFatalAlert(AlertDescription.bad_certificate);
+			}
+
+			BigInteger p = new BigInteger(1, pBytes);
+			BigInteger g = new BigInteger(1, gBytes);
+			BigInteger Ys = new BigInteger(1, YsBytes);
+
+			this.dhAgreeServerPublicKey = ValidateDHPublicKey(
+				new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+		}
+
+		protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+		{
+			ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+			signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+			signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+			return signer;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsDsaSigner.cs b/Crypto/src/crypto/tls/TlsDsaSigner.cs
new file mode 100644
index 000000000..27d7b1f91
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -0,0 +1,51 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal abstract class TlsDsaSigner
+		:	TlsSigner
+	{
+		public virtual byte[] CalculateRawSignature(SecureRandom random,
+			AsymmetricKeyParameter privateKey, byte[] md5andsha1)
+		{
+			ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random));
+			// Note: Only use the SHA1 part of the hash
+			s.BlockUpdate(md5andsha1, 16, 20);
+			return s.GenerateSignature();
+		}
+
+		public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1)
+		{
+			ISigner s = MakeSigner(new NullDigest(), false, publicKey);
+			// Note: Only use the SHA1 part of the hash
+			s.BlockUpdate(md5andsha1, 16, 20);
+			return s.VerifySignature(sigBytes);
+		}
+
+		public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+		{
+			return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random));
+		}
+
+		public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+		{
+			return MakeSigner(new Sha1Digest(), false, publicKey);
+		}
+
+		public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+
+		protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+		{
+			ISigner s = new DsaDigestSigner(CreateDsaImpl(), d);
+			s.Init(forSigning, cp);
+			return s;
+		}
+
+		protected abstract IDsa CreateDsaImpl();
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsDssSigner.cs b/Crypto/src/crypto/tls/TlsDssSigner.cs
new file mode 100644
index 000000000..c6f1abcec
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsDssSigner.cs
@@ -0,0 +1,21 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsDssSigner
+		: TlsDsaSigner
+	{
+		public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+		{
+			return publicKey is DsaPublicKeyParameters;
+		}
+
+	    protected override IDsa CreateDsaImpl()
+	    {
+			return new DsaSigner();
+	    }
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs
new file mode 100644
index 000000000..83983ba47
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -0,0 +1,230 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    /**
+    * ECDH key exchange (see RFC 4492)
+    */
+    internal class TlsECDHKeyExchange
+		: TlsKeyExchange
+    {
+		protected TlsClientContext context;
+		protected KeyExchangeAlgorithm keyExchange;
+		protected TlsSigner tlsSigner;
+
+		protected AsymmetricKeyParameter serverPublicKey;
+		protected ECPublicKeyParameters ecAgreeServerPublicKey;
+		protected TlsAgreementCredentials agreementCredentials;
+		protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null;
+
+		internal TlsECDHKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+        {
+			switch (keyExchange)
+			{
+				case KeyExchangeAlgorithm.ECDHE_RSA:
+					this.tlsSigner = new TlsRsaSigner();
+					break;
+				case KeyExchangeAlgorithm.ECDHE_ECDSA:
+					this.tlsSigner = new TlsECDsaSigner();
+					break;
+				case KeyExchangeAlgorithm.ECDH_RSA:
+				case KeyExchangeAlgorithm.ECDH_ECDSA:
+					this.tlsSigner = null;
+					break;
+				default:
+                    throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+			}
+
+			this.context = context;
+			this.keyExchange = keyExchange;
+        }
+
+        public virtual void SkipServerCertificate()
+        {
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        public virtual void ProcessServerCertificate(Certificate serverCertificate)
+        {
+            X509CertificateStructure x509Cert = serverCertificate.certs[0];
+            SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+            try
+            {
+                this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+            }
+            catch (Exception)
+            {
+                throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+            }
+
+			if (tlsSigner == null)
+			{
+				try
+				{
+					this.ecAgreeServerPublicKey = ValidateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+				}
+				catch (InvalidCastException)
+				{
+					throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+				}
+
+				TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyAgreement);
+			}
+			else
+			{
+				if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+				{
+					throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+				}
+
+				TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+			}
+			
+			// TODO
+            /*
+            * Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+            * signing algorithm for the certificate must be the same as the algorithm for the
+            * certificate key."
+            */
+        }
+		
+        public virtual void SkipServerKeyExchange()
+        {
+            // do nothing
+        }
+
+        public virtual void ProcessServerKeyExchange(Stream input)
+        {
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+		public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			/*
+			 * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
+			 * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
+			 * prohibited because the use of a long-term ECDH client key would jeopardize the
+			 * forward secrecy property of these algorithms.
+			 */
+			ClientCertificateType[] types = certificateRequest.CertificateTypes;
+			foreach (ClientCertificateType type in types)
+			{
+				switch (type)
+				{
+					case ClientCertificateType.rsa_sign:
+					case ClientCertificateType.dss_sign:
+					case ClientCertificateType.ecdsa_sign:
+					case ClientCertificateType.rsa_fixed_ecdh:
+					case ClientCertificateType.ecdsa_fixed_ecdh:
+						break;
+					default:
+						throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+				}
+			}
+		}
+
+		public virtual void SkipClientCredentials()
+		{
+			this.agreementCredentials = null;
+		}
+
+		public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			if (clientCredentials is TlsAgreementCredentials)
+			{
+				// TODO Validate client cert has matching parameters (see 'AreOnSameCurve')?
+
+				this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+			}
+			else if (clientCredentials is TlsSignerCredentials)
+			{
+				// OK
+			}
+			else
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		public virtual void GenerateClientKeyExchange(Stream output)
+        {
+			if (agreementCredentials == null)
+			{
+				GenerateEphemeralClientKeyExchange(ecAgreeServerPublicKey.Parameters, output);
+			}
+        }
+
+        public virtual byte[] GeneratePremasterSecret()
+        {
+			if (agreementCredentials != null)
+			{
+				return agreementCredentials.GenerateAgreement(ecAgreeServerPublicKey);
+			}
+
+			return CalculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
+        }
+
+		protected virtual bool AreOnSameCurve(ECDomainParameters a, ECDomainParameters b)
+		{
+			// TODO Move to ECDomainParameters.Equals() or other utility method?
+			return a.Curve.Equals(b.Curve) && a.G.Equals(b.G) && a.N.Equals(b.N) && a.H.Equals(b.H);
+		}
+
+		protected virtual byte[] ExternalizeKey(ECPublicKeyParameters keyParameters)
+		{
+			// TODO Add support for compressed encoding and SPF extension
+
+			/*
+			 * RFC 4492 5.7. ...an elliptic curve point in uncompressed or compressed format.
+			 * Here, the format MUST conform to what the server has requested through a
+			 * Supported Point Formats Extension if this extension was used, and MUST be
+			 * uncompressed if this extension was not used.
+			 */
+			return keyParameters.Q.GetEncoded();
+		}
+
+		protected virtual AsymmetricCipherKeyPair GenerateECKeyPair(ECDomainParameters ecParams)
+		{
+			ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
+			ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams,
+				context.SecureRandom);
+			keyPairGenerator.Init(keyGenerationParameters);
+			return keyPairGenerator.GenerateKeyPair();
+		}
+
+		protected virtual void GenerateEphemeralClientKeyExchange(ECDomainParameters ecParams, Stream output)
+		{
+			AsymmetricCipherKeyPair ecAgreeClientKeyPair = GenerateECKeyPair(ecParams);
+			this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.Private;
+
+			byte[] keData = ExternalizeKey((ECPublicKeyParameters)ecAgreeClientKeyPair.Public);
+			TlsUtilities.WriteOpaque8(keData, output);
+		}
+
+		protected virtual byte[] CalculateECDHBasicAgreement(ECPublicKeyParameters publicKey,
+			ECPrivateKeyParameters privateKey)
+		{
+			ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
+			basicAgreement.Init(privateKey);
+			BigInteger agreement = basicAgreement.CalculateAgreement(publicKey);
+			return BigIntegers.AsUnsignedByteArray(agreement);
+		}
+
+		protected virtual ECPublicKeyParameters ValidateECPublicKey(ECPublicKeyParameters key)
+		{
+			// TODO Check RFC 4492 for validation
+			return key;
+		}
+    }
+}
diff --git a/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs
new file mode 100644
index 000000000..5516154ce
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math.EC;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    /**
+    * ECDHE key exchange (see RFC 4492)
+    */
+    internal class TlsECDheKeyExchange : TlsECDHKeyExchange
+    {
+        internal TlsECDheKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange)
+            : base(context, keyExchange)
+        {
+        }
+
+		public override void SkipServerKeyExchange()
+        {
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        public override void ProcessServerKeyExchange(Stream input)
+        {
+			SecurityParameters securityParameters = context.SecurityParameters;
+
+            ISigner signer = InitSigner(tlsSigner, securityParameters);
+            Stream sigIn = new SignerStream(input, signer, null);
+
+            ECCurveType curveType = (ECCurveType)TlsUtilities.ReadUint8(sigIn);
+            ECDomainParameters curve_params;
+
+            //  Currently, we only support named curves
+            if (curveType == ECCurveType.named_curve)
+            {
+                NamedCurve namedCurve = (NamedCurve)TlsUtilities.ReadUint16(sigIn);
+
+                // TODO Check namedCurve is one we offered?
+
+                curve_params = NamedCurveHelper.GetECParameters(namedCurve);
+            }
+            else
+            {
+                // TODO Add support for explicit curve parameters (read from sigIn)
+
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
+            }
+
+            byte[] publicBytes = TlsUtilities.ReadOpaque8(sigIn);
+
+            byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+            if (!signer.VerifySignature(sigByte))
+            {
+                throw new TlsFatalAlert(AlertDescription.bad_certificate);
+            }
+
+            // TODO Check curve_params not null
+
+            ECPoint Q = curve_params.Curve.DecodePoint(publicBytes);
+
+			this.ecAgreeServerPublicKey = ValidateECPublicKey(new ECPublicKeyParameters(Q, curve_params));
+        }
+		
+		public override void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			/*
+			 * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable
+			 * with ECDH_ECDSA and ECDH_RSA. Their use with ECDHE_ECDSA and ECDHE_RSA is
+			 * prohibited because the use of a long-term ECDH client key would jeopardize the
+			 * forward secrecy property of these algorithms.
+			 */
+			ClientCertificateType[] types = certificateRequest.CertificateTypes;
+			foreach (ClientCertificateType type in types)
+			{
+				switch (type)
+				{
+					case ClientCertificateType.rsa_sign:
+					case ClientCertificateType.dss_sign:
+					case ClientCertificateType.ecdsa_sign:
+						break;
+					default:
+						throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+				}
+			}
+		}
+		
+		public override void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			if (clientCredentials is TlsSignerCredentials)
+			{
+				// OK
+			}
+			else
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+
+		protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+        {
+            ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+            signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+            signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+            return signer;
+        }
+    }
+}
diff --git a/Crypto/src/crypto/tls/TlsECDsaSigner.cs b/Crypto/src/crypto/tls/TlsECDsaSigner.cs
new file mode 100644
index 000000000..3c30fdc0c
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -0,0 +1,21 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsECDsaSigner
+		: TlsDsaSigner
+	{
+		public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+		{
+			return publicKey is ECPublicKeyParameters;
+		}
+
+		protected override IDsa CreateDsaImpl()
+		{
+			return new ECDsaSigner();
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsException.cs b/Crypto/src/crypto/tls/TlsException.cs
new file mode 100644
index 000000000..fa3e73273
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsException.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class TlsException : Exception
+	{
+		public TlsException() : base() { }
+		public TlsException(string message) : base(message) { }
+		public TlsException(string message, Exception exception) : base(message, exception) { }
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsFatalAlert.cs b/Crypto/src/crypto/tls/TlsFatalAlert.cs
new file mode 100644
index 000000000..0a9cc6f3a
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsFatalAlert.cs
@@ -0,0 +1,21 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class TlsFatalAlert
+		: IOException
+	{
+		private readonly AlertDescription alertDescription;
+
+		public TlsFatalAlert(AlertDescription alertDescription)
+		{
+			this.alertDescription = alertDescription;
+		}
+
+		public AlertDescription AlertDescription
+		{
+			get { return alertDescription; }
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsKeyExchange.cs b/Crypto/src/crypto/tls/TlsKeyExchange.cs
new file mode 100644
index 000000000..5102edbec
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsKeyExchange.cs
@@ -0,0 +1,38 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// A generic interface for key exchange implementations in TLS 1.0.
+	/// </summary>
+	public interface TlsKeyExchange
+	{
+		/// <exception cref="IOException"/>
+		void SkipServerCertificate();
+
+		/// <exception cref="IOException"/>
+		void ProcessServerCertificate(Certificate serverCertificate);
+
+		/// <exception cref="IOException"/>
+		void SkipServerKeyExchange();
+
+		/// <exception cref="IOException"/>
+		void ProcessServerKeyExchange(Stream input);
+
+		/// <exception cref="IOException"/>
+		void ValidateCertificateRequest(CertificateRequest certificateRequest);
+
+		/// <exception cref="IOException"/>
+		void SkipClientCredentials();
+
+		/// <exception cref="IOException"/>
+		void ProcessClientCredentials(TlsCredentials clientCredentials);
+		
+		/// <exception cref="IOException"/>
+		void GenerateClientKeyExchange(Stream output);
+
+		/// <exception cref="IOException"/>
+		byte[] GeneratePremasterSecret();
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsMac.cs b/Crypto/src/crypto/tls/TlsMac.cs
new file mode 100644
index 000000000..0e58b89dc
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsMac.cs
@@ -0,0 +1,106 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>
+	/// A generic TLS MAC implementation, which can be used with any kind of
+	/// IDigest to act as an HMAC.
+	/// </remarks>
+	public class TlsMac
+	{
+		protected long seqNo;
+		protected byte[] secret;
+		protected HMac mac;
+
+		/**
+		* Generate a new instance of an TlsMac.
+		*
+		* @param digest    The digest to use.
+		* @param key_block A byte-array where the key for this mac is located.
+		* @param offset    The number of bytes to skip, before the key starts in the buffer.
+		* @param len       The length of the key.
+		*/
+		public TlsMac(
+			IDigest	digest,
+			byte[]	key_block,
+			int		offset,
+			int		len)
+		{
+			this.seqNo = 0;
+
+			KeyParameter param = new KeyParameter(key_block, offset, len);
+
+			this.secret = Arrays.Clone(param.GetKey());
+
+			this.mac = new HMac(digest);
+			this.mac.Init(param);
+		}
+
+		/**
+		 * @return the MAC write secret
+		 */
+		public virtual byte[] GetMacSecret()
+		{
+			return this.secret;
+		}
+
+		/**
+		 * @return the current write sequence number
+		 */
+		public virtual long SequenceNumber
+		{
+			get { return this.seqNo; }
+		}
+
+		/**
+		 * Increment the current write sequence number
+		 */
+		public virtual void IncSequenceNumber()
+		{
+			this.seqNo++;
+		}
+
+		/**
+		* @return The Keysize of the mac.
+		*/
+		public virtual int Size
+		{
+			get { return mac.GetMacSize(); }
+		}
+
+		/**
+		* Calculate the mac for some given data.
+		* <p/>
+		* TlsMac will keep track of the sequence number internally.
+		*
+		* @param type    The message type of the message.
+		* @param message A byte-buffer containing the message.
+		* @param offset  The number of bytes to skip, before the message starts.
+		* @param len     The length of the message.
+		* @return A new byte-buffer containing the mac value.
+		*/
+		public virtual byte[] CalculateMac(
+			ContentType	type,
+			byte[]		message,
+			int			offset,
+			int			len)
+		{
+			byte[] macHeader = new byte[13];
+			TlsUtilities.WriteUint64(seqNo++, macHeader, 0);
+			TlsUtilities.WriteUint8((byte)type, macHeader, 8);
+			TlsUtilities.WriteVersion(macHeader, 9);
+			TlsUtilities.WriteUint16(len, macHeader, 11);
+
+			mac.BlockUpdate(macHeader, 0, macHeader.Length);
+			mac.BlockUpdate(message, offset, len);
+			return MacUtilities.DoFinal(mac);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsNullCipher.cs b/Crypto/src/crypto/tls/TlsNullCipher.cs
new file mode 100644
index 000000000..b76f76d9c
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsNullCipher.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// A NULL cipher suite, for use during handshake.
+	/// </summary>
+	public class TlsNullCipher
+		: TlsCipher
+	{
+		public virtual byte[] EncodePlaintext(ContentType type, byte[] plaintext, int offset, int len)
+		{
+			return CopyData(plaintext, offset, len);
+		}
+
+        public virtual byte[] DecodeCiphertext(ContentType type, byte[] ciphertext, int offset, int len)
+		{
+			return CopyData(ciphertext, offset, len);
+		}
+
+		protected virtual byte[] CopyData(byte[] text, int offset, int len)
+		{
+			byte[] result = new byte[len];
+			Array.Copy(text, offset, result, 0, len);
+			return result;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsNullCompression.cs b/Crypto/src/crypto/tls/TlsNullCompression.cs
new file mode 100644
index 000000000..45f8fc708
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsNullCompression.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public class TlsNullCompression
+		: TlsCompression
+	{
+		public virtual Stream Compress(Stream output)
+		{
+			return output;
+		}
+
+		public virtual Stream Decompress(Stream output)
+		{
+			return output;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsProtocolHandler.cs b/Crypto/src/crypto/tls/TlsProtocolHandler.cs
new file mode 100644
index 000000000..6d2b0b144
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -0,0 +1,1259 @@
+using System;
+using System.Collections;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>An implementation of all high level protocols in TLS 1.0.</remarks>
+	public class TlsProtocolHandler
+	{
+		/*
+		* Our Connection states
+		*/
+		private const short CS_CLIENT_HELLO_SEND = 1;
+		private const short CS_SERVER_HELLO_RECEIVED = 2;
+		private const short CS_SERVER_CERTIFICATE_RECEIVED = 3;
+		private const short CS_SERVER_KEY_EXCHANGE_RECEIVED = 4;
+		private const short CS_CERTIFICATE_REQUEST_RECEIVED = 5;
+		private const short CS_SERVER_HELLO_DONE_RECEIVED = 6;
+		private const short CS_CLIENT_KEY_EXCHANGE_SEND = 7;
+		private const short CS_CERTIFICATE_VERIFY_SEND = 8;
+		private const short CS_CLIENT_CHANGE_CIPHER_SPEC_SEND = 9;
+		private const short CS_CLIENT_FINISHED_SEND = 10;
+		private const short CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED = 11;
+		private const short CS_DONE = 12;
+
+		private static readonly byte[] emptybuf = new byte[0];
+
+		private static readonly string TLS_ERROR_MESSAGE = "Internal TLS error, this could be an attack";
+
+		/*
+		* Queues for data from some protocols.
+		*/
+
+		private ByteQueue applicationDataQueue = new ByteQueue();
+		private ByteQueue changeCipherSpecQueue = new ByteQueue();
+		private ByteQueue alertQueue = new ByteQueue();
+		private ByteQueue handshakeQueue = new ByteQueue();
+
+		/*
+		* The Record Stream we use
+		*/
+		private RecordStream rs;
+		private SecureRandom random;
+
+		private TlsStream tlsStream = null;
+
+		private bool closed = false;
+		private bool failedWithError = false;
+		private bool appDataReady = false;
+		private IDictionary clientExtensions;
+
+		private SecurityParameters securityParameters = null;
+
+		private TlsClientContextImpl tlsClientContext = null;
+		private TlsClient tlsClient = null;
+		private CipherSuite[] offeredCipherSuites = null;
+		private CompressionMethod[] offeredCompressionMethods = null;
+        private TlsKeyExchange keyExchange = null;
+		private TlsAuthentication authentication = null;
+		private CertificateRequest certificateRequest = null;
+
+		private short connection_state = 0;
+
+		private static SecureRandom CreateSecureRandom()
+		{
+			/*
+			 * We use our threaded seed generator to generate a good random seed. If the user
+			 * has a better random seed, he should use the constructor with a SecureRandom.
+			 *
+			 * Hopefully, 20 bytes in fast mode are good enough.
+			 */
+			byte[] seed = new ThreadedSeedGenerator().GenerateSeed(20, true);
+
+			return new SecureRandom(seed);
+		}
+
+		public TlsProtocolHandler(
+			Stream s)
+			: this(s, s)
+		{
+		}
+
+		public TlsProtocolHandler(
+			Stream			s,
+			SecureRandom	sr)
+			: this(s, s, sr)
+		{
+		}
+
+		/// <remarks>Both streams can be the same object</remarks>
+		public TlsProtocolHandler(
+			Stream	inStr,
+			Stream	outStr)
+			: this(inStr, outStr, CreateSecureRandom())
+		{
+		}
+
+		/// <remarks>Both streams can be the same object</remarks>
+		public TlsProtocolHandler(
+			Stream			inStr,
+			Stream			outStr,
+			SecureRandom	sr)
+		{
+			this.rs = new RecordStream(this, inStr, outStr);
+			this.random = sr;
+		}
+
+		internal void ProcessData(
+			ContentType	protocol,
+			byte[]		buf,
+			int			offset,
+			int			len)
+		{
+			/*
+			* Have a look at the protocol type, and add it to the correct queue.
+			*/
+			switch (protocol)
+			{
+				case ContentType.change_cipher_spec:
+					changeCipherSpecQueue.AddData(buf, offset, len);
+					ProcessChangeCipherSpec();
+					break;
+				case ContentType.alert:
+					alertQueue.AddData(buf, offset, len);
+					ProcessAlert();
+					break;
+				case ContentType.handshake:
+					handshakeQueue.AddData(buf, offset, len);
+					ProcessHandshake();
+					break;
+				case ContentType.application_data:
+					if (!appDataReady)
+					{
+						this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+					}
+					applicationDataQueue.AddData(buf, offset, len);
+					ProcessApplicationData();
+					break;
+				default:
+					/*
+					* Uh, we don't know this protocol.
+					*
+					* RFC2246 defines on page 13, that we should ignore this.
+					*/
+					break;
+			}
+		}
+
+		private void ProcessHandshake()
+		{
+			bool read;
+			do
+			{
+				read = false;
+
+				/*
+				* We need the first 4 bytes, they contain type and length of
+				* the message.
+				*/
+				if (handshakeQueue.Available >= 4)
+				{
+					byte[] beginning = new byte[4];
+					handshakeQueue.Read(beginning, 0, 4, 0);
+					MemoryStream bis = new MemoryStream(beginning, false);
+					HandshakeType type = (HandshakeType)TlsUtilities.ReadUint8(bis);
+					int len = TlsUtilities.ReadUint24(bis);
+
+					/*
+					* Check if we have enough bytes in the buffer to read
+					* the full message.
+					*/
+					if (handshakeQueue.Available >= (len + 4))
+					{
+						/*
+						* Read the message.
+						*/
+						byte[] buf = new byte[len];
+						handshakeQueue.Read(buf, 0, len, 4);
+						handshakeQueue.RemoveData(len + 4);
+
+						/*
+						 * 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. [..] Note: [Also,] Hello Request
+						 * messages are omitted from handshake hashes.
+						 */
+						switch (type)
+						{
+							case HandshakeType.hello_request:
+							case HandshakeType.finished:
+								break;
+							default:
+								rs.UpdateHandshakeData(beginning, 0, 4);
+								rs.UpdateHandshakeData(buf, 0, len);
+								break;
+						}
+
+						/*
+						* Now, parse the message.
+						*/
+						ProcessHandshakeMessage(type, buf);
+						read = true;
+					}
+				}
+			}
+			while (read);
+		}
+
+		private void ProcessHandshakeMessage(HandshakeType type, byte[] buf)
+		{
+			MemoryStream inStr = new MemoryStream(buf, false);
+
+			/*
+			* Check the type.
+			*/
+			switch (type)
+			{
+				case HandshakeType.certificate:
+				{
+					switch (connection_state)
+					{
+						case CS_SERVER_HELLO_RECEIVED:
+						{
+							// Parse the Certificate message and send to cipher suite
+
+							Certificate serverCertificate = Certificate.Parse(inStr);
+
+							AssertEmpty(inStr);
+
+							this.keyExchange.ProcessServerCertificate(serverCertificate);
+
+							this.authentication = tlsClient.GetAuthentication();
+							this.authentication.NotifyServerCertificate(serverCertificate);
+
+							break;
+						}
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+							break;
+					}
+
+					connection_state = CS_SERVER_CERTIFICATE_RECEIVED;
+					break;
+				}
+				case HandshakeType.finished:
+					switch (connection_state)
+					{
+						case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED:
+							/*
+							 * Read the checksum from the finished message, it has always 12 bytes.
+							 */
+							byte[] serverVerifyData = new byte[12];
+							TlsUtilities.ReadFully(serverVerifyData, inStr);
+
+							AssertEmpty(inStr);
+
+							/*
+							 * Calculate our own checksum.
+							 */
+							byte[] expectedServerVerifyData = TlsUtilities.PRF(
+								securityParameters.masterSecret, "server finished",
+								rs.GetCurrentHash(), 12);
+
+							/*
+							 * Compare both checksums.
+							 */
+							if (!Arrays.ConstantTimeAreEqual(expectedServerVerifyData, serverVerifyData))
+							{
+								/*
+								 * Wrong checksum in the finished message.
+								 */
+								this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+							}
+
+							connection_state = CS_DONE;
+
+							/*
+							* We are now ready to receive application data.
+							*/
+							this.appDataReady = true;
+							break;
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+							break;
+					}
+					break;
+				case HandshakeType.server_hello:
+					switch (connection_state)
+					{
+						case CS_CLIENT_HELLO_SEND:
+							/*
+							 * Read the server hello message
+							 */
+							TlsUtilities.CheckVersion(inStr);
+
+							/*
+							 * Read the server random
+							 */
+							securityParameters.serverRandom = new byte[32];
+							TlsUtilities.ReadFully(securityParameters.serverRandom, inStr);
+
+							byte[] sessionID = TlsUtilities.ReadOpaque8(inStr);
+							if (sessionID.Length > 32)
+							{
+								this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+							}
+
+							this.tlsClient.NotifySessionID(sessionID);
+
+							/*
+							 * Find out which CipherSuite the server has chosen and check that
+							 * it was one of the offered ones.
+							 */
+							CipherSuite selectedCipherSuite = (CipherSuite)TlsUtilities.ReadUint16(inStr);
+							if (!ArrayContains(offeredCipherSuites, selectedCipherSuite)
+								|| selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+							{
+								this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+							}
+
+							this.tlsClient.NotifySelectedCipherSuite(selectedCipherSuite);
+
+                            /*
+                             * Find out which CompressionMethod the server has chosen and check that
+                             * it was one of the offered ones.
+                             */
+                            CompressionMethod selectedCompressionMethod = (CompressionMethod)TlsUtilities.ReadUint8(inStr);
+							if (!ArrayContains(offeredCompressionMethods, selectedCompressionMethod))
+							{
+								this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+							}
+
+                            this.tlsClient.NotifySelectedCompressionMethod(selectedCompressionMethod);
+
+	                        /*
+	                         * RFC3546 2.2 The extended server hello message format MAY be
+	                         * sent in place of the server hello message when the client has
+	                         * requested extended functionality via the extended client hello
+	                         * message specified in Section 2.1.
+							 * ...
+							 * Note that the extended server hello message is only sent in response
+							 * to an extended client hello message.  This prevents the possibility
+							 * that the extended server hello message could "break" existing TLS 1.0
+							 * clients.
+	                         */
+
+							/*
+							 * TODO 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.
+							 */
+
+							// ExtensionType -> byte[]
+							IDictionary serverExtensions = Platform.CreateHashtable();
+
+							if (inStr.Position < inStr.Length)
+							{
+								// Process extensions from extended server hello
+                                byte[] extBytes = TlsUtilities.ReadOpaque16(inStr);
+
+                                MemoryStream ext = new MemoryStream(extBytes, false);
+                                while (ext.Position < ext.Length)
+								{
+                                    ExtensionType extType = (ExtensionType)TlsUtilities.ReadUint16(ext);
+                                    byte[] extValue = TlsUtilities.ReadOpaque16(ext);
+
+									// Note: RFC 5746 makes a special case for EXT_RenegotiationInfo
+                                    if (extType != ExtensionType.renegotiation_info
+										&& !clientExtensions.Contains(extType))
+									{
+										/*
+										 * RFC 3546 2.3
+										 * Note that for all extension types (including those defined in
+										 * future), the extension type MUST NOT appear in the extended server
+										 * hello unless the same extension type appeared in the corresponding
+										 * client hello.  Thus clients MUST abort the handshake if they receive
+										 * an extension type in the extended server hello that they did not
+										 * request in the associated (extended) client hello.
+										 */
+										this.FailWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
+									}
+
+									if (serverExtensions.Contains(extType))
+									{
+										/*
+										 * RFC 3546 2.3
+										 * Also note that when multiple extensions of different types are
+										 * present in the extended client hello or the extended server hello,
+										 * the extensions may appear in any order. There MUST NOT be more than
+										 * one extension of the same type.
+										 */
+										this.FailWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+									}
+
+									serverExtensions.Add(extType, extValue);
+								}
+							}
+
+							AssertEmpty(inStr);
+
+							/*
+							 * RFC 5746 3.4. When a ServerHello is received, the client MUST check if it
+							 * includes the "renegotiation_info" extension:
+							 */
+							{
+								bool secure_negotiation = serverExtensions.Contains(ExtensionType.renegotiation_info);
+
+								/*
+								 * If the extension is present, set the secure_renegotiation flag
+								 * to TRUE.  The client MUST then verify that the length of the
+								 * "renegotiated_connection" field is zero, and if it is not, MUST
+								 * abort the handshake (by sending a fatal handshake_failure
+								 * alert).
+								 */
+								if (secure_negotiation)
+								{
+                                    byte[] renegExtValue = (byte[])serverExtensions[ExtensionType.renegotiation_info];
+
+                                    if (!Arrays.ConstantTimeAreEqual(renegExtValue,
+                                        CreateRenegotiationInfo(emptybuf)))
+									{
+										this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+									}
+								}
+
+								tlsClient.NotifySecureRenegotiation(secure_negotiation);
+							}
+
+							if (clientExtensions != null)
+							{
+								tlsClient.ProcessServerExtensions(serverExtensions);
+							}
+
+							this.keyExchange = tlsClient.GetKeyExchange();
+
+	                        connection_state = CS_SERVER_HELLO_RECEIVED;
+	                        break;
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+							break;
+					}
+					break;
+				case HandshakeType.server_hello_done:
+					switch (connection_state)
+					{
+						case CS_SERVER_HELLO_RECEIVED:
+						case CS_SERVER_CERTIFICATE_RECEIVED:
+						case CS_SERVER_KEY_EXCHANGE_RECEIVED:
+						case CS_CERTIFICATE_REQUEST_RECEIVED:
+
+							// NB: Original code used case label fall-through
+
+							if (connection_state == CS_SERVER_HELLO_RECEIVED)
+							{
+								// There was no server certificate message; check it's OK
+								this.keyExchange.SkipServerCertificate();
+								this.authentication = null;
+
+								// There was no server key exchange message; check it's OK
+								this.keyExchange.SkipServerKeyExchange();
+							}
+							else if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
+							{
+								// There was no server key exchange message; check it's OK
+								this.keyExchange.SkipServerKeyExchange();
+							}
+
+							AssertEmpty(inStr);
+
+							connection_state = CS_SERVER_HELLO_DONE_RECEIVED;
+
+							TlsCredentials clientCreds = null;
+							if (certificateRequest == null)
+							{
+								this.keyExchange.SkipClientCredentials();
+							}
+							else
+							{
+								clientCreds = this.authentication.GetClientCredentials(certificateRequest);
+
+								Certificate clientCert;
+								if (clientCreds == null)
+								{
+									this.keyExchange.SkipClientCredentials();
+									clientCert = Certificate.EmptyChain;
+								}
+								else
+								{
+									this.keyExchange.ProcessClientCredentials(clientCreds);
+									clientCert = clientCreds.Certificate;
+								}
+
+								SendClientCertificate(clientCert);
+							}
+
+							/*
+							 * Send the client key exchange message, depending on the key
+							 * exchange we are using in our CipherSuite.
+							 */
+							SendClientKeyExchange();
+
+							connection_state = CS_CLIENT_KEY_EXCHANGE_SEND;
+
+							if (clientCreds != null && clientCreds is TlsSignerCredentials)
+							{
+								TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
+								byte[] md5andsha1 = rs.GetCurrentHash();
+								byte[] clientCertificateSignature = signerCreds.GenerateCertificateSignature(
+									md5andsha1);
+								SendCertificateVerify(clientCertificateSignature);
+
+								connection_state = CS_CERTIFICATE_VERIFY_SEND;
+							}
+					
+							/*
+							* Now, we send change cipher state
+							*/
+							byte[] cmessage = new byte[1];
+							cmessage[0] = 1;
+							rs.WriteMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.Length);
+
+							connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND;
+
+							/*
+							 * Calculate the master_secret
+							 */
+							byte[] pms = this.keyExchange.GeneratePremasterSecret();
+
+							securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret",
+								TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom),
+								48);
+
+							// TODO Is there a way to ensure the data is really overwritten?
+							/*
+							 * RFC 2246 8.1. The pre_master_secret should be deleted from
+							 * memory once the master_secret has been computed.
+							 */
+							Array.Clear(pms, 0, pms.Length);
+
+							/*
+							 * Initialize our cipher suite
+							 */
+	                        rs.ClientCipherSpecDecided(tlsClient.GetCompression(), tlsClient.GetCipher());
+
+							/*
+							 * Send our finished message.
+							 */
+							byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret,
+								"client finished", rs.GetCurrentHash(), 12);
+
+							MemoryStream bos = new MemoryStream();
+							TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
+							TlsUtilities.WriteOpaque24(clientVerifyData, bos);
+							byte[] message = bos.ToArray();
+
+							rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+
+							this.connection_state = CS_CLIENT_FINISHED_SEND;
+							break;
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+							break;
+					}
+					break;
+				case HandshakeType.server_key_exchange:
+				{
+					switch (connection_state)
+					{
+						case CS_SERVER_HELLO_RECEIVED:
+						case CS_SERVER_CERTIFICATE_RECEIVED:
+						{
+							// NB: Original code used case label fall-through
+							if (connection_state == CS_SERVER_HELLO_RECEIVED)
+							{
+								// There was no server certificate message; check it's OK
+								this.keyExchange.SkipServerCertificate();
+								this.authentication = null;
+							}
+
+    	                    this.keyExchange.ProcessServerKeyExchange(inStr);
+
+	                        AssertEmpty(inStr);
+							break;
+						}
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+							break;
+					}
+
+					this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED;
+					break;
+				}
+				case HandshakeType.certificate_request:
+					switch (connection_state)
+					{
+						case CS_SERVER_CERTIFICATE_RECEIVED:
+						case CS_SERVER_KEY_EXCHANGE_RECEIVED:
+						{
+							// NB: Original code used case label fall-through
+							if (connection_state == CS_SERVER_CERTIFICATE_RECEIVED)
+							{
+								// There was no server key exchange message; check it's OK
+								this.keyExchange.SkipServerKeyExchange();
+							}
+
+							if (this.authentication == null)
+							{
+								/*
+								 * RFC 2246 7.4.4. It is a fatal handshake_failure alert
+								 * for an anonymous server to request client identification.
+								 */
+								this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+							}
+
+                            int numTypes = TlsUtilities.ReadUint8(inStr);
+                            ClientCertificateType[] certificateTypes = new ClientCertificateType[numTypes];
+                            for (int i = 0; i < numTypes; ++i)
+                            {
+                                certificateTypes[i] = (ClientCertificateType)TlsUtilities.ReadUint8(inStr);
+                            }
+
+                            byte[] authorities = TlsUtilities.ReadOpaque16(inStr);
+
+							AssertEmpty(inStr);
+
+                            IList authorityDNs = Platform.CreateArrayList();
+
+							MemoryStream bis = new MemoryStream(authorities, false);
+							while (bis.Position < bis.Length)
+							{
+								byte[] dnBytes = TlsUtilities.ReadOpaque16(bis);
+								// TODO Switch to X500Name when available
+								authorityDNs.Add(X509Name.GetInstance(Asn1Object.FromByteArray(dnBytes)));
+							}
+
+							this.certificateRequest = new CertificateRequest(certificateTypes,
+								authorityDNs);
+							this.keyExchange.ValidateCertificateRequest(this.certificateRequest);
+
+							break;
+						}
+						default:
+							this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+							break;
+					}
+
+					this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED;
+					break;
+				case HandshakeType.hello_request:
+					/*
+					 * RFC 2246 7.4.1.1 Hello request
+					 * This message will be ignored by the client if the client is currently
+					 * negotiating a session. This message may be ignored by the client if it
+					 * does not wish to renegotiate a session, or the client may, if it wishes,
+					 * respond with a no_renegotiation alert.
+					 */
+					if (connection_state == CS_DONE)
+					{
+						// Renegotiation not supported yet
+						SendAlert(AlertLevel.warning, AlertDescription.no_renegotiation);
+					}
+					break;
+				case HandshakeType.client_key_exchange:
+				case HandshakeType.certificate_verify:
+				case HandshakeType.client_hello:
+				default:
+					// We do not support this!
+					this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+					break;
+			}
+		}
+
+		private void ProcessApplicationData()
+		{
+			/*
+			* There is nothing we need to do here.
+			*
+			* This function could be used for callbacks when application
+			* data arrives in the future.
+			*/
+		}
+
+		private void ProcessAlert()
+		{
+			while (alertQueue.Available >= 2)
+			{
+				/*
+				* An alert is always 2 bytes. Read the alert.
+				*/
+				byte[] tmp = new byte[2];
+				alertQueue.Read(tmp, 0, 2, 0);
+				alertQueue.RemoveData(2);
+				byte level = tmp[0];
+				byte description = tmp[1];
+				if (level == (byte)AlertLevel.fatal)
+				{
+					/*
+					* This is a fatal error.
+					*/
+					this.failedWithError = true;
+					this.closed = true;
+					/*
+					* Now try to Close the stream, ignore errors.
+					*/
+					try
+					{
+						rs.Close();
+					}
+					catch (Exception)
+					{
+					}
+					throw new IOException(TLS_ERROR_MESSAGE);
+				}
+				else
+				{
+					/*
+					* This is just a warning.
+					*/
+					if (description == (byte)AlertDescription.close_notify)
+					{
+						/*
+						* Close notify
+						*/
+						this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+					}
+					/*
+					* If it is just a warning, we continue.
+					*/
+				}
+			}
+		}
+
+		/**
+		* This method is called, when a change cipher spec message is received.
+		*
+		* @throws IOException If the message has an invalid content or the
+		*                     handshake is not in the correct state.
+		*/
+		private void ProcessChangeCipherSpec()
+		{
+			while (changeCipherSpecQueue.Available > 0)
+			{
+				/*
+				 * A change cipher spec message is only one byte with the value 1.
+				 */
+				byte[] b = new byte[1];
+				changeCipherSpecQueue.Read(b, 0, 1, 0);
+				changeCipherSpecQueue.RemoveData(1);
+				if (b[0] != 1)
+				{
+					/*
+					* This should never happen.
+					*/
+					this.FailWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+				}
+
+				/*
+				 * Check if we are in the correct connection state.
+				 */
+				if (this.connection_state != CS_CLIENT_FINISHED_SEND)
+				{
+                	this.FailWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+				}
+
+				rs.ServerClientSpecReceived();
+
+            	this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED;
+			}
+		}
+
+		private void SendClientCertificate(Certificate clientCert)
+		{
+			MemoryStream bos = new MemoryStream();
+			TlsUtilities.WriteUint8((byte)HandshakeType.certificate, bos);
+
+			// Reserve space for length
+			TlsUtilities.WriteUint24(0, bos);
+
+			clientCert.Encode(bos);
+			byte[] message = bos.ToArray();
+
+			// Patch actual length back in
+			TlsUtilities.WriteUint24(message.Length - 4, message, 1);
+
+			rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+		}
+
+		private void SendClientKeyExchange()
+		{
+			MemoryStream bos = new MemoryStream();
+            TlsUtilities.WriteUint8((byte)HandshakeType.client_key_exchange, bos);
+
+			// Reserve space for length
+			TlsUtilities.WriteUint24(0, bos);
+
+			this.keyExchange.GenerateClientKeyExchange(bos);
+			byte[] message = bos.ToArray();
+
+			// Patch actual length back in
+			TlsUtilities.WriteUint24(message.Length - 4, message, 1);
+
+			rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+		}
+
+		private void SendCertificateVerify(byte[] data)
+		{
+			/*
+			 * Send signature of handshake messages so far to prove we are the owner of
+			 * the cert See RFC 2246 sections 4.7, 7.4.3 and 7.4.8
+			 */
+			MemoryStream bos = new MemoryStream();
+            TlsUtilities.WriteUint8((byte)HandshakeType.certificate_verify, bos);
+			TlsUtilities.WriteUint24(data.Length + 2, bos);
+			TlsUtilities.WriteOpaque16(data, bos);
+			byte[] message = bos.ToArray();
+
+			rs.WriteMessage(ContentType.handshake, message, 0, message.Length);
+		}
+
+		/// <summary>Connects to the remote system.</summary>
+		/// <param name="verifyer">Will be used when a certificate is received to verify
+		/// that this certificate is accepted by the client.</param>
+		/// <exception cref="IOException">If handshake was not successful</exception>
+		[Obsolete("Use version taking TlsClient")]
+		public virtual void Connect(
+			ICertificateVerifyer verifyer)
+		{
+	        this.Connect(new LegacyTlsClient(verifyer));
+	    }
+
+		public virtual void Connect(TlsClient tlsClient)
+        {
+            if (tlsClient == null)
+                throw new ArgumentNullException("tlsClient");
+            if (this.tlsClient != null)
+                throw new InvalidOperationException("Connect can only be called once");
+
+			/*
+             * Send Client hello
+             *
+             * First, generate some random data.
+             */
+            this.securityParameters = new SecurityParameters();
+            this.securityParameters.clientRandom = new byte[32];
+            random.NextBytes(securityParameters.clientRandom, 4, 28);
+            TlsUtilities.WriteGmtUnixTime(securityParameters.clientRandom, 0);
+
+			this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
+			this.tlsClient = tlsClient;
+			this.tlsClient.Init(tlsClientContext);
+
+            MemoryStream outStr = new MemoryStream();
+            TlsUtilities.WriteVersion(outStr);
+            outStr.Write(securityParameters.clientRandom, 0, 32);
+
+            /*
+            * Length of Session id
+            */
+            TlsUtilities.WriteUint8(0, outStr);
+
+            this.offeredCipherSuites = this.tlsClient.GetCipherSuites();
+
+            // ExtensionType -> byte[]
+            this.clientExtensions = this.tlsClient.GetClientExtensions();
+
+            // Cipher Suites (and SCSV)
+            {
+                /*
+                 * RFC 5746 3.4.
+                 * The client MUST include either an empty "renegotiation_info"
+                 * extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
+                 * cipher suite value in the ClientHello.  Including both is NOT
+                 * RECOMMENDED.
+                 */
+                bool noRenegExt = clientExtensions == null
+                    || !clientExtensions.Contains(ExtensionType.renegotiation_info);
+
+                int count = offeredCipherSuites.Length;
+                if (noRenegExt)
+                {
+                    // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
+                    ++count;
+                }
+
+                TlsUtilities.WriteUint16(2 * count, outStr);
+
+                for (int i = 0; i < offeredCipherSuites.Length; ++i)
+                {
+                    TlsUtilities.WriteUint16((int)offeredCipherSuites[i], outStr);
+                }
+
+                if (noRenegExt)
+                {
+                    TlsUtilities.WriteUint16((int)CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, outStr);
+                }
+            }
+
+            /*
+             * Compression methods, just the null method.
+             */
+            this.offeredCompressionMethods = tlsClient.GetCompressionMethods();
+
+            {
+                TlsUtilities.WriteUint8((byte)offeredCompressionMethods.Length, outStr);
+                for (int i = 0; i < offeredCompressionMethods.Length; ++i)
+                {
+                    TlsUtilities.WriteUint8((byte)offeredCompressionMethods[i], outStr);
+                }
+            }
+
+            // Extensions
+            if (clientExtensions != null)
+            {
+                MemoryStream ext = new MemoryStream();
+
+                foreach (ExtensionType extType in clientExtensions.Keys)
+                {
+                    WriteExtension(ext, extType, (byte[])clientExtensions[extType]);
+                }
+
+                TlsUtilities.WriteOpaque16(ext.ToArray(), outStr);
+            }
+
+            MemoryStream bos = new MemoryStream();
+            TlsUtilities.WriteUint8((byte)HandshakeType.client_hello, bos);
+            TlsUtilities.WriteUint24((int)outStr.Length, bos);
+            byte[] outBytes = outStr.ToArray();
+            bos.Write(outBytes, 0, outBytes.Length);
+            byte[] message = bos.ToArray();
+            SafeWriteMessage(ContentType.handshake, message, 0, message.Length);
+            connection_state = CS_CLIENT_HELLO_SEND;
+
+            /*
+            * We will now read data, until we have completed the handshake.
+            */
+            while (connection_state != CS_DONE)
+            {
+				SafeReadData();
+            }
+
+            this.tlsStream = new TlsStream(this);
+        }
+
+		/**
+		* Read data from the network. The method will return immediately, if there is
+		* still some data left in the buffer, or block until some application
+		* data has been read from the network.
+		*
+		* @param buf    The buffer where the data will be copied to.
+		* @param offset The position where the data will be placed in the buffer.
+		* @param len    The maximum number of bytes to read.
+		* @return The number of bytes read.
+		* @throws IOException If something goes wrong during reading data.
+		*/
+		internal int ReadApplicationData(byte[] buf, int offset, int len)
+		{
+			while (applicationDataQueue.Available == 0)
+			{
+				if (this.closed)
+				{
+					/*
+					* We need to read some data.
+					*/
+					if (this.failedWithError)
+					{
+						/*
+						* Something went terribly wrong, we should throw an IOException
+						*/
+						throw new IOException(TLS_ERROR_MESSAGE);
+					}
+
+					/*
+					* Connection has been closed, there is no more data to read.
+					*/
+					return 0;
+				}
+
+				SafeReadData();
+			}
+			len = System.Math.Min(len, applicationDataQueue.Available);
+			applicationDataQueue.Read(buf, offset, len, 0);
+			applicationDataQueue.RemoveData(len);
+			return len;
+		}
+
+		private void SafeReadData()
+		{
+			try
+			{
+				rs.ReadData();
+			}
+			catch (TlsFatalAlert e)
+			{
+				if (!this.closed)
+				{
+					this.FailWithError(e.AlertDescription, e);
+				}
+				throw e;
+			}
+			catch (IOException e)
+			{
+				if (!this.closed)
+				{
+					this.FailWithError(AlertDescription.internal_error, e);
+				}
+				throw e;
+			}
+			catch (Exception e)
+			{
+				if (!this.closed)
+				{
+					this.FailWithError(AlertDescription.internal_error, e);
+				}
+				throw e;
+			}
+		}
+
+		private void SafeWriteMessage(ContentType type, byte[] buf, int offset, int len)
+		{
+			try
+			{
+				rs.WriteMessage(type, buf, offset, len);
+			}
+			catch (TlsFatalAlert e)
+			{
+				if (!this.closed)
+				{
+					this.FailWithError(e.AlertDescription, e);
+				}
+				throw e;
+			}
+			catch (IOException e)
+			{
+				if (!closed)
+				{
+					this.FailWithError(AlertDescription.internal_error, e);
+				}
+				throw e;
+			}
+			catch (Exception e)
+			{
+				if (!closed)
+				{
+					this.FailWithError(AlertDescription.internal_error, e);
+				}
+				throw e;
+			}
+		}
+
+		/**
+		* Send some application data to the remote system.
+		* <p/>
+		* The method will handle fragmentation internally.
+		*
+		* @param buf    The buffer with the data.
+		* @param offset The position in the buffer where the data is placed.
+		* @param len    The length of the data.
+		* @throws IOException If something goes wrong during sending.
+		*/
+		internal void WriteData(byte[] buf, int offset, int len)
+		{
+			if (this.closed)
+			{
+				if (this.failedWithError)
+					throw new IOException(TLS_ERROR_MESSAGE);
+
+				throw new IOException("Sorry, connection has been closed, you cannot write more data");
+			}
+
+			/*
+			* Protect against known IV attack!
+			*
+			* DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT
+			* YOU ARE DOING HERE.
+			*/
+			SafeWriteMessage(ContentType.application_data, emptybuf, 0, 0);
+
+			do
+			{
+				/*
+				* We are only allowed to write fragments up to 2^14 bytes.
+				*/
+				int toWrite = System.Math.Min(len, 1 << 14);
+
+				SafeWriteMessage(ContentType.application_data, buf, offset, toWrite);
+
+				offset += toWrite;
+				len -= toWrite;
+			}
+			while (len > 0);
+		}
+
+		/// <summary>A Stream which can be used to send data.</summary>
+		[Obsolete("Use 'Stream' property instead")]
+		public virtual Stream OutputStream
+		{
+			get { return this.tlsStream; }
+		}
+
+		/// <summary>A Stream which can be used to read data.</summary>
+		[Obsolete("Use 'Stream' property instead")]
+		public virtual Stream InputStream
+		{
+			get { return this.tlsStream; }
+		}
+
+		/// <summary>The secure bidirectional stream for this connection</summary>
+		public virtual Stream Stream
+		{
+			get { return this.tlsStream; }
+		}
+
+		/**
+		* Terminate this connection with an alert.
+		* <p/>
+		* Can be used for normal closure too.
+		*
+		* @param alertLevel       The level of the alert, an be AlertLevel.fatal or AL_warning.
+		* @param alertDescription The exact alert message.
+		* @throws IOException If alert was fatal.
+		*/
+        private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription)
+        {
+            this.FailWithError(alertLevel, alertDescription, null);
+        }
+
+        private void FailWithError(AlertDescription alertDescription, Exception ex)
+        {
+            this.FailWithError(AlertLevel.fatal, alertDescription, ex);
+        }
+
+        private void FailWithError(AlertLevel alertLevel, AlertDescription alertDescription, Exception ex)
+        {
+			/*
+			* Check if the connection is still open.
+			*/
+			if (!closed)
+			{
+				/*
+				* Prepare the message
+				*/
+				this.closed = true;
+
+				if (alertLevel == AlertLevel.fatal)
+				{
+					/*
+					* This is a fatal message.
+					*/
+					this.failedWithError = true;
+				}
+				SendAlert(alertLevel, alertDescription);
+				rs.Close();
+				if (alertLevel == AlertLevel.fatal)
+				{
+					throw new IOException(TLS_ERROR_MESSAGE, ex);
+				}
+			}
+			else
+			{
+				throw new IOException(TLS_ERROR_MESSAGE, ex);
+			}
+		}
+
+		internal void SendAlert(AlertLevel alertLevel, AlertDescription alertDescription)
+		{
+			byte[] error = new byte[2];
+			error[0] = (byte)alertLevel;
+			error[1] = (byte)alertDescription;
+
+			rs.WriteMessage(ContentType.alert, error, 0, 2);
+		}
+
+		/// <summary>Closes this connection</summary>
+		/// <exception cref="IOException">If something goes wrong during closing.</exception>
+		public virtual void Close()
+		{
+			if (!closed)
+			{
+				this.FailWithError(AlertLevel.warning, AlertDescription.close_notify);
+			}
+		}
+
+		/**
+		* Make sure the Stream is now empty. Fail otherwise.
+		*
+		* @param is The Stream to check.
+		* @throws IOException If is is not empty.
+		*/
+		internal void AssertEmpty(
+			MemoryStream inStr)
+		{
+			if (inStr.Position < inStr.Length)
+			{
+				throw new TlsFatalAlert(AlertDescription.decode_error);
+			}
+		}
+
+		internal void Flush()
+		{
+			rs.Flush();
+		}
+
+		internal bool IsClosed
+		{
+			get { return closed; }
+		}
+
+		private static bool ArrayContains(CipherSuite[] a, CipherSuite n)
+		{
+			for (int i = 0; i < a.Length; ++i)
+			{
+				if (a[i] == n)
+					return true;
+			}
+			return false;
+		}
+
+        private static bool ArrayContains(CompressionMethod[] a, CompressionMethod n)
+        {
+            for (int i = 0; i < a.Length; ++i)
+            {
+                if (a[i] == n)
+                    return true;
+            }
+            return false;
+        }
+
+		private static byte[] CreateRenegotiationInfo(byte[] renegotiated_connection)
+		{
+			MemoryStream buf = new MemoryStream();
+			TlsUtilities.WriteOpaque8(renegotiated_connection, buf);
+			return buf.ToArray();
+		}
+
+		private static void WriteExtension(Stream output, ExtensionType extType, byte[] extValue)
+		{
+			TlsUtilities.WriteUint16((int)extType, output);
+			TlsUtilities.WriteOpaque16(extValue, output);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsPskIdentity.cs b/Crypto/src/crypto/tls/TlsPskIdentity.cs
new file mode 100644
index 000000000..119064ee7
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsPskIdentity.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsPskIdentity
+	{
+		void SkipIdentityHint();
+
+		void NotifyIdentityHint(byte[] psk_identity_hint);
+
+		byte[] GetPskIdentity();
+
+		byte[] GetPsk();
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsPskKeyExchange.cs b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs
new file mode 100644
index 000000000..226153a97
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -0,0 +1,149 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsPskKeyExchange
+		: TlsKeyExchange
+	{
+		protected TlsClientContext context;
+		protected KeyExchangeAlgorithm keyExchange;
+		protected TlsPskIdentity pskIdentity;
+
+		protected byte[] psk_identity_hint = null;
+
+		protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
+		protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+
+		protected RsaKeyParameters rsaServerPublicKey = null;
+		protected byte[] premasterSecret;
+
+		internal TlsPskKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange,
+			TlsPskIdentity pskIdentity)
+		{
+			switch (keyExchange)
+			{
+				case KeyExchangeAlgorithm.PSK:
+				case KeyExchangeAlgorithm.RSA_PSK:
+				case KeyExchangeAlgorithm.DHE_PSK:
+					break;
+				default:
+					throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+			}
+
+			this.context = context;
+			this.keyExchange = keyExchange;
+			this.pskIdentity = pskIdentity;
+		}
+
+		public virtual void SkipServerCertificate()
+		{
+			// OK
+		}
+
+		public virtual void ProcessServerCertificate(Certificate serverCertificate)
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void SkipServerKeyExchange()
+		{
+			this.psk_identity_hint = new byte[0];
+		}
+
+		public virtual void ProcessServerKeyExchange(Stream input)
+		{
+			this.psk_identity_hint = TlsUtilities.ReadOpaque16(input);
+
+			if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+			{
+				byte[] pBytes = TlsUtilities.ReadOpaque16(input);
+				byte[] gBytes = TlsUtilities.ReadOpaque16(input);
+				byte[] YsBytes = TlsUtilities.ReadOpaque16(input);
+
+				BigInteger p = new BigInteger(1, pBytes);
+				BigInteger g = new BigInteger(1, gBytes);
+				BigInteger Ys = new BigInteger(1, YsBytes);
+				
+				this.dhAgreeServerPublicKey = TlsDHUtilities.ValidateDHPublicKey(
+					new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+			}
+			else if (this.psk_identity_hint.Length == 0)
+			{
+				// TODO Should we enforce that this message should have been skipped if hint is empty?
+				//throw new TlsFatalAlert(AlertDescription.unexpected_message);
+			}
+		}
+
+		public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void SkipClientCredentials()
+		{
+			// OK
+		}
+
+		public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			throw new TlsFatalAlert(AlertDescription.internal_error);
+		}
+
+		public virtual void GenerateClientKeyExchange(Stream output)
+		{
+			if (psk_identity_hint == null || psk_identity_hint.Length == 0)
+			{
+				pskIdentity.SkipIdentityHint();
+			}
+			else
+			{
+				pskIdentity.NotifyIdentityHint(psk_identity_hint);
+			}
+
+			byte[] psk_identity = pskIdentity.GetPskIdentity();
+
+			TlsUtilities.WriteOpaque16(psk_identity, output);
+
+			if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+			{
+				this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
+					context.SecureRandom, this.rsaServerPublicKey, output);
+			}
+			else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+			{
+				this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
+					context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output);
+			}
+		}
+
+		public virtual byte[] GeneratePremasterSecret()
+		{
+			byte[] psk = pskIdentity.GetPsk();
+			byte[] other_secret = GenerateOtherSecret(psk.Length);
+
+			MemoryStream buf = new MemoryStream(4 + other_secret.Length + psk.Length);
+			TlsUtilities.WriteOpaque16(other_secret, buf);
+			TlsUtilities.WriteOpaque16(psk, buf);
+			return buf.ToArray();
+		}
+
+		protected virtual byte[] GenerateOtherSecret(int pskLength)
+		{
+			if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+			{
+				return TlsDHUtilities.CalculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+			}
+
+			if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+			{
+				return this.premasterSecret;
+			}
+
+			return new byte[pskLength];
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs
new file mode 100644
index 000000000..4538a2a81
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -0,0 +1,165 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// TLS 1.0 RSA key exchange.
+	/// </summary>
+	internal class TlsRsaKeyExchange
+		: TlsKeyExchange
+	{
+		protected TlsClientContext context;
+
+		protected AsymmetricKeyParameter serverPublicKey = null;
+
+        protected RsaKeyParameters rsaServerPublicKey = null;
+
+        protected byte[] premasterSecret;
+
+		internal TlsRsaKeyExchange(TlsClientContext context)
+		{
+			this.context = context;
+		}
+
+		public virtual void SkipServerCertificate()
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void ProcessServerCertificate(Certificate serverCertificate)
+		{
+			X509CertificateStructure x509Cert = serverCertificate.certs[0];
+			SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+			try
+			{
+				this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+			}
+//			catch (RuntimeException)
+			catch (Exception)
+			{
+				throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+			}
+
+			// Sanity check the PublicKeyFactory
+			if (this.serverPublicKey.IsPrivate)
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+
+			this.rsaServerPublicKey = ValidateRsaPublicKey((RsaKeyParameters)this.serverPublicKey);
+
+			TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.KeyEncipherment);
+
+			// TODO
+			/*
+			* Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+			* signing algorithm for the certificate must be the same as the algorithm for the
+			* certificate key."
+			*/
+		}
+
+		public virtual void SkipServerKeyExchange()
+		{
+			// OK
+		}
+
+		public virtual void ProcessServerKeyExchange(Stream input)
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			ClientCertificateType[] types = certificateRequest.CertificateTypes;
+			foreach (ClientCertificateType type in types)
+			{
+				switch (type)
+				{
+					case ClientCertificateType.rsa_sign:
+					case ClientCertificateType.dss_sign:
+					case ClientCertificateType.ecdsa_sign:
+						break;
+					default:
+						throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+				}
+			}
+		}
+
+		public virtual void SkipClientCredentials()
+		{
+			// OK
+		}
+
+		public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			if (!(clientCredentials is TlsSignerCredentials))
+			{
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+		}
+		
+        public virtual void GenerateClientKeyExchange(Stream output)
+		{
+			this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
+				context.SecureRandom, this.rsaServerPublicKey, output);
+		}
+
+		public virtual byte[] GeneratePremasterSecret()
+		{
+			byte[] tmp = this.premasterSecret;
+			this.premasterSecret = null;
+			return tmp;
+		}
+
+    	// Would be needed to process RSA_EXPORT server key exchange
+//	    protected virtual void ProcessRsaServerKeyExchange(Stream input, ISigner signer)
+//	    {
+//	        Stream sigIn = input;
+//	        if (signer != null)
+//	        {
+//	            sigIn = new SignerStream(input, signer, null);
+//	        }
+//
+//	        byte[] modulusBytes = TlsUtilities.ReadOpaque16(sigIn);
+//	        byte[] exponentBytes = TlsUtilities.ReadOpaque16(sigIn);
+//
+//	        if (signer != null)
+//	        {
+//	            byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+//
+//	            if (!signer.VerifySignature(sigByte))
+//	            {
+//	                handler.FailWithError(AlertLevel.fatal, AlertDescription.bad_certificate);
+//	            }
+//	        }
+//
+//	        BigInteger modulus = new BigInteger(1, modulusBytes);
+//	        BigInteger exponent = new BigInteger(1, exponentBytes);
+//
+//	        this.rsaServerPublicKey = ValidateRSAPublicKey(new RsaKeyParameters(false, modulus, exponent));
+//	    }
+
+        protected virtual RsaKeyParameters ValidateRsaPublicKey(RsaKeyParameters key)
+		{
+			// TODO What is the minimum bit length required?
+//			key.Modulus.BitLength;
+
+			if (!key.Exponent.IsProbablePrime(2))
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+
+			return key;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaSigner.cs b/Crypto/src/crypto/tls/TlsRsaSigner.cs
new file mode 100644
index 000000000..a50ff9558
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -0,0 +1,53 @@
+using System;
+
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsRsaSigner
+    	: TlsSigner
+	{
+		public virtual byte[] CalculateRawSignature(SecureRandom random,
+			AsymmetricKeyParameter privateKey, byte[] md5andsha1)
+		{
+			ISigner s = MakeSigner(new NullDigest(), true, new ParametersWithRandom(privateKey, random));
+			s.BlockUpdate(md5andsha1, 0, md5andsha1.Length);
+			return s.GenerateSignature();
+		}
+
+		public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey,
+			byte[] md5andsha1)
+		{
+			ISigner s = MakeSigner(new NullDigest(), false, publicKey);
+			s.BlockUpdate(md5andsha1, 0, md5andsha1.Length);
+			return s.VerifySignature(sigBytes);
+		}
+
+		public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+		{
+			return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random));
+		}
+
+        public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+		{
+			return MakeSigner(new CombinedHash(), false, publicKey);
+		}
+
+		public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+		{
+			return publicKey is RsaKeyParameters && !publicKey.IsPrivate;
+		}
+
+		protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+		{
+			ISigner s = new GenericSigner(new Pkcs1Encoding(new RsaBlindedEngine()), d);
+			s.Init(forSigning, cp);
+			return s;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsRsaUtilities.cs b/Crypto/src/crypto/tls/TlsRsaUtilities.cs
new file mode 100644
index 000000000..4450ba452
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -0,0 +1,42 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Encodings;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public abstract class TlsRsaUtilities
+	{
+		public static byte[] GenerateEncryptedPreMasterSecret(SecureRandom random,
+			RsaKeyParameters rsaServerPublicKey, Stream output)
+		{
+			/*
+			 * Choose a PremasterSecret and send it encrypted to the server
+			 */
+			byte[] premasterSecret = new byte[48];
+			random.NextBytes(premasterSecret);
+			TlsUtilities.WriteVersion(premasterSecret, 0);
+
+			Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
+			encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, random));
+
+			try
+			{
+				byte[] keData = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);
+                TlsUtilities.WriteOpaque16(keData, output);
+			}
+			catch (InvalidCipherTextException)
+			{
+				/*
+				* This should never happen, only during decryption.
+				*/
+				throw new TlsFatalAlert(AlertDescription.internal_error);
+			}
+
+			return premasterSecret;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsSigner.cs b/Crypto/src/crypto/tls/TlsSigner.cs
new file mode 100644
index 000000000..e59b90705
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSigner.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsSigner
+	{
+    	byte[] CalculateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey,
+			byte[] md5andsha1);
+		bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1);
+
+		ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey);
+		ISigner CreateVerifyer(AsymmetricKeyParameter publicKey);
+
+		bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsSignerCredentials.cs b/Crypto/src/crypto/tls/TlsSignerCredentials.cs
new file mode 100644
index 000000000..2adb06c26
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	public interface TlsSignerCredentials : TlsCredentials
+	{
+		/// <exception cref="IOException"></exception>
+		byte[] GenerateCertificateSignature(byte[] md5andsha1);
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs
new file mode 100644
index 000000000..852aace41
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -0,0 +1,203 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Agreement;
+using Org.BouncyCastle.Crypto.Agreement.Srp;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <summary>
+	/// TLS 1.1 SRP key exchange.
+	/// </summary>
+	internal class TlsSrpKeyExchange
+		: TlsKeyExchange
+	{
+		protected TlsClientContext context;
+        protected KeyExchangeAlgorithm keyExchange;
+        protected TlsSigner tlsSigner;
+		protected byte[] identity;
+		protected byte[] password;
+
+        protected AsymmetricKeyParameter serverPublicKey = null;
+
+		protected byte[] s = null;
+        protected BigInteger B = null;
+        protected Srp6Client srpClient = new Srp6Client();
+
+		internal TlsSrpKeyExchange(TlsClientContext context, KeyExchangeAlgorithm keyExchange,
+			byte[] identity, byte[] password)
+		{
+			switch (keyExchange)
+			{
+				case KeyExchangeAlgorithm.SRP:
+					this.tlsSigner = null;
+					break;
+				case KeyExchangeAlgorithm.SRP_RSA:
+					this.tlsSigner = new TlsRsaSigner();
+					break;
+				case KeyExchangeAlgorithm.SRP_DSS:
+					this.tlsSigner = new TlsDssSigner();
+					break;
+				default:
+					throw new ArgumentException("unsupported key exchange algorithm", "keyExchange");
+			}
+
+			this.context = context;
+			this.keyExchange = keyExchange;
+			this.identity = identity;
+			this.password = password;
+		}
+
+		public virtual void SkipServerCertificate()
+		{
+			if (tlsSigner != null)
+			{
+				throw new TlsFatalAlert(AlertDescription.unexpected_message);
+			}
+		}
+
+		public virtual void ProcessServerCertificate(Certificate serverCertificate)
+		{
+			if (tlsSigner == null)
+			{
+				throw new TlsFatalAlert(AlertDescription.unexpected_message);
+			}
+
+			X509CertificateStructure x509Cert = serverCertificate.certs[0];
+			SubjectPublicKeyInfo keyInfo = x509Cert.SubjectPublicKeyInfo;
+
+			try
+			{
+				this.serverPublicKey = PublicKeyFactory.CreateKey(keyInfo);
+			}
+//			catch (RuntimeException)
+			catch (Exception)
+			{
+				throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+			}
+
+	        if (!tlsSigner.IsValidPublicKey(this.serverPublicKey))
+	        {
+	            throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+	        }
+
+			TlsUtilities.ValidateKeyUsage(x509Cert, KeyUsage.DigitalSignature);
+
+			// TODO
+			/*
+			* Perform various checks per RFC2246 7.4.2: "Unless otherwise specified, the
+			* signing algorithm for the certificate must be the same as the algorithm for the
+			* certificate key."
+			*/
+		}
+
+		public virtual void SkipServerKeyExchange()
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void ProcessServerKeyExchange(Stream input)
+		{
+			SecurityParameters securityParameters = context.SecurityParameters;
+
+			Stream sigIn = input;
+			ISigner signer = null;
+
+			if (tlsSigner != null)
+			{
+				signer = InitSigner(tlsSigner, securityParameters);
+				sigIn = new SignerStream(input, signer, null);
+			}
+
+			byte[] NBytes = TlsUtilities.ReadOpaque16(sigIn);
+			byte[] gBytes = TlsUtilities.ReadOpaque16(sigIn);
+			byte[] sBytes = TlsUtilities.ReadOpaque8(sigIn);
+			byte[] BBytes = TlsUtilities.ReadOpaque16(sigIn);
+
+			if (signer != null)
+			{
+				byte[] sigByte = TlsUtilities.ReadOpaque16(input);
+
+				if (!signer.VerifySignature(sigByte))
+				{
+					throw new TlsFatalAlert(AlertDescription.bad_certificate);
+				}
+			}
+
+			BigInteger N = new BigInteger(1, NBytes);
+			BigInteger g = new BigInteger(1, gBytes);
+
+			// TODO Validate group parameters (see RFC 5054)
+			//throw new TlsFatalAlert(AlertDescription.insufficient_security);
+
+			this.s = sBytes;
+
+			/*
+			* RFC 5054 2.5.3: The client MUST abort the handshake with an "illegal_parameter"
+			* alert if B % N = 0.
+			*/
+			try
+			{
+				this.B = Srp6Utilities.ValidatePublicValue(N, new BigInteger(1, BBytes));
+			}
+			catch (CryptoException)
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+
+			this.srpClient.Init(N, g, new Sha1Digest(), context.SecureRandom);
+		}
+
+		public virtual void ValidateCertificateRequest(CertificateRequest certificateRequest)
+		{
+			throw new TlsFatalAlert(AlertDescription.unexpected_message);
+		}
+
+		public virtual void SkipClientCredentials()
+		{
+			// OK
+		}
+		
+		public virtual void ProcessClientCredentials(TlsCredentials clientCredentials)
+		{
+			throw new TlsFatalAlert(AlertDescription.internal_error);
+		}
+
+        public virtual void GenerateClientKeyExchange(Stream output)
+		{
+			byte[] keData = BigIntegers.AsUnsignedByteArray(srpClient.GenerateClientCredentials(s,
+				this.identity, this.password));
+            TlsUtilities.WriteOpaque16(keData, output);
+		}
+
+		public virtual byte[] GeneratePremasterSecret()
+		{
+			try
+			{
+				// TODO Check if this needs to be a fixed size
+				return BigIntegers.AsUnsignedByteArray(srpClient.CalculateSecret(B));
+			}
+			catch (CryptoException)
+			{
+				throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+			}
+		}
+
+		protected virtual ISigner InitSigner(TlsSigner tlsSigner, SecurityParameters securityParameters)
+		{
+			ISigner signer = tlsSigner.CreateVerifyer(this.serverPublicKey);
+			signer.BlockUpdate(securityParameters.clientRandom, 0, securityParameters.clientRandom.Length);
+			signer.BlockUpdate(securityParameters.serverRandom, 0, securityParameters.serverRandom.Length);
+			return signer;
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsStream.cs b/Crypto/src/crypto/tls/TlsStream.cs
new file mode 100644
index 000000000..e3d05686b
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsStream.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	internal class TlsStream
+		: Stream
+	{
+		private readonly TlsProtocolHandler handler;
+
+		internal TlsStream(
+			TlsProtocolHandler handler)
+		{
+			this.handler = handler;
+		}
+
+		public override bool CanRead
+		{
+			get { return !handler.IsClosed; }
+		}
+
+		public override bool CanSeek
+		{
+			get { return false; }
+		}
+
+		public override bool CanWrite
+		{
+			get { return !handler.IsClosed; }
+		}
+
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                handler.Close();
+            }
+        }
+
+		public override void Flush()
+		{
+			handler.Flush();
+		}
+
+        public override long Length
+		{
+			get { throw new NotSupportedException(); }
+		}
+
+		public override long Position
+        {
+            get { throw new NotSupportedException(); }
+            set { throw new NotSupportedException(); }
+        }
+
+		public override int Read(byte[]	buf, int off, int len)
+		{
+			return this.handler.ReadApplicationData(buf, off, len);
+		}
+
+		public override int ReadByte()
+		{
+			byte[] buf = new byte[1];
+			if (this.Read(buf, 0, 1) <= 0)
+				return -1;
+			return buf[0];
+		}
+
+		public override long Seek(long offset, SeekOrigin origin)
+		{
+			throw new NotSupportedException();
+		}
+
+        public override void SetLength(long value)
+		{
+			throw new NotSupportedException();
+		}
+
+		public override void Write(byte[] buf, int off, int len)
+		{
+			this.handler.WriteData(buf, off, len);
+		}
+
+		public override void WriteByte(byte b)
+		{
+			this.handler.WriteData(new byte[] { b }, 0, 1);
+		}
+	}
+}
diff --git a/Crypto/src/crypto/tls/TlsUtilities.cs b/Crypto/src/crypto/tls/TlsUtilities.cs
new file mode 100644
index 000000000..0e2452689
--- /dev/null
+++ b/Crypto/src/crypto/tls/TlsUtilities.cs
@@ -0,0 +1,286 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Macs;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+	/// <remarks>Some helper fuctions for MicroTLS.</remarks>
+	public class TlsUtilities
+	{
+		internal static void WriteUint8(byte i, Stream os)
+		{
+			os.WriteByte(i);
+		}
+
+		internal static void WriteUint8(byte i, byte[] buf, int offset)
+		{
+			buf[offset] = i;
+		}
+
+		internal static void WriteUint16(int i, Stream os)
+		{
+			os.WriteByte((byte)(i >> 8));
+			os.WriteByte((byte)i);
+		}
+
+		internal static void WriteUint16(int i, byte[] buf, int offset)
+		{
+			buf[offset] = (byte)(i >> 8);
+			buf[offset + 1] = (byte)i;
+		}
+
+		internal static void WriteUint24(int i, Stream os)
+		{
+			os.WriteByte((byte)(i >> 16));
+			os.WriteByte((byte)(i >> 8));
+			os.WriteByte((byte)i);
+		}
+
+		internal static void WriteUint24(int i, byte[] buf, int offset)
+		{
+			buf[offset] = (byte)(i >> 16);
+			buf[offset + 1] = (byte)(i >> 8);
+			buf[offset + 2] = (byte)(i);
+		}
+
+		internal static void WriteUint64(long i, Stream os)
+		{
+			os.WriteByte((byte)(i >> 56));
+			os.WriteByte((byte)(i >> 48));
+			os.WriteByte((byte)(i >> 40));
+			os.WriteByte((byte)(i >> 32));
+			os.WriteByte((byte)(i >> 24));
+			os.WriteByte((byte)(i >> 16));
+			os.WriteByte((byte)(i >> 8));
+			os.WriteByte((byte)i);
+		}
+
+		internal static void WriteUint64(long i, byte[] buf, int offset)
+		{
+			buf[offset] = (byte)(i >> 56);
+			buf[offset + 1] = (byte)(i >> 48);
+			buf[offset + 2] = (byte)(i >> 40);
+			buf[offset + 3] = (byte)(i >> 32);
+			buf[offset + 4] = (byte)(i >> 24);
+			buf[offset + 5] = (byte)(i >> 16);
+			buf[offset + 6] = (byte)(i >> 8);
+			buf[offset + 7] = (byte)(i);
+		}
+
+		internal static void WriteOpaque8(byte[] buf, Stream os)
+		{
+			WriteUint8((byte)buf.Length, os);
+			os.Write(buf, 0, buf.Length);
+		}
+
+		internal static void WriteOpaque16(byte[] buf, Stream os)
+		{
+			WriteUint16(buf.Length, os);
+			os.Write(buf, 0, buf.Length);
+		}
+
+		internal static void WriteOpaque24(byte[] buf, Stream os)
+		{
+			WriteUint24(buf.Length, os);
+			os.Write(buf, 0, buf.Length);
+		}
+
+		internal static void WriteUint8Array(byte[] uints, Stream os)
+		{
+            os.Write(uints, 0, uints.Length);
+		}
+
+		internal static void WriteUint16Array(int[] uints, Stream os)
+		{
+			for (int i = 0; i < uints.Length; ++i)
+			{
+				WriteUint16(uints[i], os);
+			}
+		}
+
+		internal static byte ReadUint8(Stream inStr)
+		{
+			int i = inStr.ReadByte();
+			if (i < 0)
+			{
+				throw new EndOfStreamException();
+			}
+			return (byte)i;
+		}
+
+		internal static int ReadUint16(Stream inStr)
+		{
+			int i1 = inStr.ReadByte();
+			int i2 = inStr.ReadByte();
+			if ((i1 | i2) < 0)
+			{
+				throw new EndOfStreamException();
+			}
+			return i1 << 8 | i2;
+		}
+
+		internal static int ReadUint24(Stream inStr)
+		{
+			int i1 = inStr.ReadByte();
+			int i2 = inStr.ReadByte();
+			int i3 = inStr.ReadByte();
+			if ((i1 | i2 | i3) < 0)
+			{
+				throw new EndOfStreamException();
+			}
+			return (i1 << 16) | (i2 << 8) | i3;
+		}
+
+		internal static void ReadFully(byte[] buf, Stream inStr)
+		{
+			if (Streams.ReadFully(inStr, buf, 0, buf.Length) < buf.Length)
+				throw new EndOfStreamException();
+		}
+
+		internal static byte[] ReadOpaque8(Stream inStr)
+		{
+			byte length = ReadUint8(inStr);
+			byte[] bytes = new byte[length];
+			ReadFully(bytes, inStr);
+			return bytes;
+		}
+
+		internal static byte[] ReadOpaque16(Stream inStr)
+		{
+			int length = ReadUint16(inStr);
+			byte[] bytes = new byte[length];
+			ReadFully(bytes, inStr);
+			return bytes;
+		}
+
+		internal static void CheckVersion(byte[] readVersion)
+		{
+			if ((readVersion[0] != 3) || (readVersion[1] != 1))
+			{
+				throw new TlsFatalAlert(AlertDescription.protocol_version);
+			}
+		}
+
+		internal static void CheckVersion(Stream inStr)
+		{
+			int i1 = inStr.ReadByte();
+			int i2 = inStr.ReadByte();
+			if ((i1 != 3) || (i2 != 1))
+			{
+				throw new TlsFatalAlert(AlertDescription.protocol_version);
+			}
+		}
+
+		internal static void WriteGmtUnixTime(byte[] buf, int offset)
+	    {
+			int t = (int)(DateTimeUtilities.CurrentUnixMs() / 1000L);
+			buf[offset] = (byte)(t >> 24);
+			buf[offset + 1] = (byte)(t >> 16);
+			buf[offset + 2] = (byte)(t >> 8);
+			buf[offset + 3] = (byte)t;
+	    }
+
+		internal static void WriteVersion(Stream os)
+		{
+			os.WriteByte(3);
+			os.WriteByte(1);
+		}
+
+		internal static void WriteVersion(byte[] buf, int offset)
+		{
+			buf[offset] = 3;
+			buf[offset + 1] = 1;
+		}
+
+		private static void hmac_hash(IDigest digest, byte[] secret, byte[] seed, byte[] output)
+		{
+			HMac mac = new HMac(digest);
+			KeyParameter param = new KeyParameter(secret);
+			byte[] a = seed;
+			int size = digest.GetDigestSize();
+			int iterations = (output.Length + size - 1) / size;
+			byte[] buf = new byte[mac.GetMacSize()];
+			byte[] buf2 = new byte[mac.GetMacSize()];
+			for (int i = 0; i < iterations; i++)
+			{
+				mac.Init(param);
+				mac.BlockUpdate(a, 0, a.Length);
+				mac.DoFinal(buf, 0);
+				a = buf;
+				mac.Init(param);
+				mac.BlockUpdate(a, 0, a.Length);
+				mac.BlockUpdate(seed, 0, seed.Length);
+				mac.DoFinal(buf2, 0);
+				Array.Copy(buf2, 0, output, (size * i), System.Math.Min(size, output.Length - (size * i)));
+			}
+		}
+
+		internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size)
+		{
+            byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+
+			int s_half = (secret.Length + 1) / 2;
+			byte[] s1 = new byte[s_half];
+			byte[] s2 = new byte[s_half];
+			Array.Copy(secret, 0, s1, 0, s_half);
+			Array.Copy(secret, secret.Length - s_half, s2, 0, s_half);
+
+			byte[] ls = Concat(label, seed);
+
+			byte[] buf = new byte[size];
+			byte[] prf = new byte[size];
+			hmac_hash(new MD5Digest(), s1, ls, prf);
+			hmac_hash(new Sha1Digest(), s2, ls, buf);
+			for (int i = 0; i < size; i++)
+			{
+				buf[i] ^= prf[i];
+			}
+			return buf;
+		}
+
+		internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size)
+		{
+            byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+			byte[] labelSeed = Concat(label, seed);
+
+			byte[] buf = new byte[size];
+			hmac_hash(digest, secret, labelSeed, buf);
+			return buf;
+		}
+
+		internal static byte[] Concat(byte[] a, byte[] b)
+		{
+			byte[] c = new byte[a.Length + b.Length];
+			Array.Copy(a, 0, c, 0, a.Length);
+			Array.Copy(b, 0, c, a.Length, b.Length);
+			return c;
+		}
+
+		internal static void ValidateKeyUsage(X509CertificateStructure c, int keyUsageBits)
+		{
+			X509Extensions exts = c.TbsCertificate.Extensions;
+			if (exts != null)
+			{
+				X509Extension ext = exts.GetExtension(X509Extensions.KeyUsage);
+				if (ext != null)
+				{
+					DerBitString ku = KeyUsage.GetInstance(ext);
+                    //int bits = ku.GetBytes()[0];
+                    //if ((bits & keyUsageBits) != keyUsageBits)
+                    //{
+                    //    throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+                    //}
+				}
+			}
+		}
+	}
+}