summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/crypto.csproj50
-rw-r--r--crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsContext.cs117
-rw-r--r--crypto/src/crypto/tls/AbstractTlsCredentials.cs10
-rw-r--r--crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSigner.cs50
-rw-r--r--crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs20
-rw-r--r--crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs71
-rw-r--r--crypto/src/crypto/tls/DefaultTlsCipherFactory.cs8
-rw-r--r--crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs51
-rw-r--r--crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs88
-rw-r--r--crypto/src/crypto/tls/SecurityParameters.cs56
-rw-r--r--crypto/src/crypto/tls/TlsAgreementCredentials.cs11
-rw-r--r--crypto/src/crypto/tls/TlsBlockCipher.cs6
-rw-r--r--crypto/src/crypto/tls/TlsCipherFactory.cs2
-rw-r--r--crypto/src/crypto/tls/TlsClientContext.cs12
-rw-r--r--crypto/src/crypto/tls/TlsClientContextImpl.cs41
-rw-r--r--crypto/src/crypto/tls/TlsDHKeyExchange.cs4
-rw-r--r--crypto/src/crypto/tls/TlsDheKeyExchange.cs2
-rw-r--r--crypto/src/crypto/tls/TlsDsaSigner.cs76
-rw-r--r--crypto/src/crypto/tls/TlsDssSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsECDHKeyExchange.cs4
-rw-r--r--crypto/src/crypto/tls/TlsECDheKeyExchange.cs2
-rw-r--r--crypto/src/crypto/tls/TlsECDsaSigner.cs29
-rw-r--r--crypto/src/crypto/tls/TlsEncryptionCredentials.cs12
-rw-r--r--crypto/src/crypto/tls/TlsProtocolHandler.cs51
-rw-r--r--crypto/src/crypto/tls/TlsPskKeyExchange.cs23
-rw-r--r--crypto/src/crypto/tls/TlsRsaKeyExchange.cs7
-rw-r--r--crypto/src/crypto/tls/TlsRsaSigner.cs82
-rw-r--r--crypto/src/crypto/tls/TlsRsaUtilities.cs152
-rw-r--r--crypto/src/crypto/tls/TlsServerContext.cs11
-rw-r--r--crypto/src/crypto/tls/TlsServerContextImpl.cs20
-rw-r--r--crypto/src/crypto/tls/TlsSigner.cs23
-rw-r--r--crypto/src/crypto/tls/TlsSignerCredentials.cs13
-rw-r--r--crypto/src/crypto/tls/TlsSrpKeyExchange.cs4
-rw-r--r--crypto/src/crypto/tls/TlsStreamCipher.cs6
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs115
37 files changed, 957 insertions, 325 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index a8f95a2bf..7e0a21bda 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -4269,6 +4269,36 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\AbstractTlsAgreementCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsContext.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsSigner.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\AbstractTlsSignerCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\AlertDescription.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4389,6 +4419,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\DefaultTlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\DefaultTlsSignerCredentials.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4674,6 +4709,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsEncryptionCredentials.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsFatalAlert.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -4739,6 +4779,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\crypto\tls\TlsServerContext.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\crypto\tls\TlsServerContextImpl.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\crypto\tls\TlsSession.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
new file mode 100644
index 000000000..2d7af80e8
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsAgreementCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsAgreementCredentials
+        :   AbstractTlsCredentials, TlsAgreementCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        public abstract byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey);
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsContext.cs b/crypto/src/crypto/tls/AbstractTlsContext.cs
new file mode 100644
index 000000000..7a7e636d2
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsContext.cs
@@ -0,0 +1,117 @@
+using System;
+using System.Threading;
+
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    internal abstract class AbstractTlsContext
+        : TlsContext
+    {
+        private static long counter = Times.NanoTime();
+
+        private static long NextCounterValue()
+        {
+            return Interlocked.Increment(ref counter);
+        }
+
+        private readonly IRandomGenerator mNonceRandom;
+        private readonly SecureRandom mSecureRandom;
+        private readonly SecurityParameters mSecurityParameters;
+
+        private ProtocolVersion mClientVersion = null;
+        private ProtocolVersion mServerVersion = null;
+        private TlsSession mSession = null;
+        private object mUserObject = null;
+
+       internal AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters)
+        {
+            secureRandom.SetSeed(NextCounterValue());
+            secureRandom.SetSeed(Times.NanoTime());
+
+            this.mNonceRandom = new DigestRandomGenerator(TlsUtilities.CreateHash(HashAlgorithm.sha256));
+            this.mNonceRandom.AddSeedMaterial(secureRandom.GenerateSeed(32));
+
+            this.mSecureRandom = secureRandom;
+            this.mSecurityParameters = securityParameters;
+        }
+
+        public virtual IRandomGenerator NonceRandomGenerator
+        {
+            get { return mNonceRandom; }
+        }
+
+        public virtual SecureRandom SecureRandom
+        {
+            get { return mSecureRandom; }
+        }
+
+        public virtual SecurityParameters SecurityParameters
+        {
+            get { return mSecurityParameters; }
+        }
+
+        public abstract bool IsServer { get; }
+
+        public virtual ProtocolVersion ClientVersion
+        {
+            get { return mClientVersion; }
+            set { this.mClientVersion = value; }
+        }
+
+        public virtual ProtocolVersion ServerVersion
+        {
+            get { return mServerVersion; }
+            set { this.mServerVersion = value; }
+        }
+
+        public virtual TlsSession ResumableSession
+        {
+            get { return mSession; }
+            set { this.mSession = value; }
+        }
+
+        public virtual object UserObject
+        {
+            get { return mUserObject; }
+            set { this.mUserObject = value; }
+        }
+
+        public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context_value, int length)
+        {
+            if (context_value != null && !TlsUtilities.IsValidUint16(context_value.Length))
+                throw new ArgumentException("must have length less than 2^16 (or be null)", "context_value");
+
+            SecurityParameters sp = SecurityParameters;
+            byte[] cr = sp.ClientRandom, sr = sp.ServerRandom;
+
+            int seedLength = cr.Length + sr.Length;
+            if (context_value != null)
+            {
+                seedLength += (2 + context_value.Length);
+            }
+
+            byte[] seed = new byte[seedLength];
+            int seedPos = 0;
+
+            Array.Copy(cr, 0, seed, seedPos, cr.Length);
+            seedPos += cr.Length;
+            Array.Copy(sr, 0, seed, seedPos, sr.Length);
+            seedPos += sr.Length;
+            if (context_value != null)
+            {
+                TlsUtilities.WriteUint16(context_value.Length, seed, seedPos);
+                seedPos += 2;
+                Array.Copy(context_value, 0, seed, seedPos, context_value.Length);
+                seedPos += context_value.Length;
+            }
+
+            if (seedPos != seedLength)
+                throw new InvalidOperationException("error in calculation of seed for export");
+
+            return TlsUtilities.PRF(this, sp.MasterSecret, asciiLabel, seed, length);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsCredentials.cs b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
new file mode 100644
index 000000000..6411b811c
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsCredentials.cs
@@ -0,0 +1,10 @@
+using System;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsCredentials
+        :   TlsCredentials
+    {
+        public abstract Certificate Certificate { get; }
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
new file mode 100644
index 000000000..05b129c60
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsEncryptionCredentials
+        : AbstractTlsCredentials, TlsEncryptionCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        public abstract byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret);
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsSigner.cs b/crypto/src/crypto/tls/AbstractTlsSigner.cs
new file mode 100644
index 000000000..1f4aabf74
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsSigner.cs
@@ -0,0 +1,50 @@
+using System;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsSigner
+        :   TlsSigner
+    {
+        protected TlsContext mContext;
+
+        public virtual void Init(TlsContext context)
+        {
+            this.mContext = context;
+        }
+
+        public virtual byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+        {
+            return GenerateRawSignature(null, privateKey, md5AndSha1);
+        }
+
+        public abstract byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+            AsymmetricKeyParameter privateKey, byte[] hash);
+
+        public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+        {
+            return VerifyRawSignature(null, sigBytes, publicKey, md5AndSha1);
+        }
+
+        public abstract bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+            AsymmetricKeyParameter publicKey, byte[] hash);
+
+        public virtual ISigner CreateSigner(AsymmetricKeyParameter privateKey)
+        {
+            return CreateSigner(null, privateKey);
+        }
+
+        public abstract ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
+
+        public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+        {
+            return CreateVerifyer(null, publicKey);
+        }
+
+        public abstract ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
+        public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+    }
+}
diff --git a/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
new file mode 100644
index 000000000..886c46c6e
--- /dev/null
+++ b/crypto/src/crypto/tls/AbstractTlsSignerCredentials.cs
@@ -0,0 +1,20 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public abstract class AbstractTlsSignerCredentials
+        : AbstractTlsCredentials, TlsSignerCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        public abstract byte[] GenerateCertificateSignature(byte[] hash);
+
+        public virtual SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+        {
+            get
+            {
+                throw new InvalidOperationException("TlsSignerCredentials implementation does not support (D)TLS 1.2+");
+            }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
index 2bd2f40bf..5147a1990 100644
--- a/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
+++ b/crypto/src/crypto/tls/DefaultTlsAgreementCredentials.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Crypto.Agreement;
 using Org.BouncyCastle.Crypto.Parameters;
@@ -8,69 +9,61 @@ using Org.BouncyCastle.Utilities;
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public class DefaultTlsAgreementCredentials
-        : TlsAgreementCredentials
+        : AbstractTlsAgreementCredentials
     {
-        protected Certificate clientCert;
-        protected AsymmetricKeyParameter clientPrivateKey;
+        protected readonly Certificate mCertificate;
+        protected readonly AsymmetricKeyParameter mPrivateKey;
 
-        protected IBasicAgreement basicAgreement;
-        protected bool truncateAgreement;
+        protected readonly IBasicAgreement mBasicAgreement;
+        protected readonly bool mTruncateAgreement;
 
-        public DefaultTlsAgreementCredentials(Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+        public DefaultTlsAgreementCredentials(Certificate certificate, AsymmetricKeyParameter privateKey)
         {
-            if (clientCertificate == null)
-            {
-                throw new ArgumentNullException("clientCertificate");
-            }
-            if (clientCertificate.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 (certificate == null)
+                throw new ArgumentNullException("certificate");
+            if (certificate.IsEmpty)
+                throw new ArgumentException("cannot be empty", "certificate");
+            if (privateKey == null)
+                throw new ArgumentNullException("privateKey");
+            if (!privateKey.IsPrivate)
+                throw new ArgumentException("must be private", "privateKey");
 
-            if (clientPrivateKey is DHPrivateKeyParameters)
+            if (privateKey is DHPrivateKeyParameters)
             {
-                basicAgreement = new DHBasicAgreement();
-                truncateAgreement = true;
+                mBasicAgreement = new DHBasicAgreement();
+                mTruncateAgreement = true;
             }
-            else if (clientPrivateKey is ECPrivateKeyParameters)
+            else if (privateKey is ECPrivateKeyParameters)
             {
-                basicAgreement = new ECDHBasicAgreement();
-                truncateAgreement = false;
+                mBasicAgreement = new ECDHBasicAgreement();
+                mTruncateAgreement = false;
             }
             else
             {
-                throw new ArgumentException("type not supported: "
-                    + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+                throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
             }
 
-            this.clientCert = clientCertificate;
-            this.clientPrivateKey = clientPrivateKey;
+            this.mCertificate = certificate;
+            this.mPrivateKey = privateKey;
         }
 
-        public virtual Certificate Certificate
+        public override Certificate Certificate
         {
-            get { return clientCert; }
+            get { return mCertificate; }
         }
 
-        public virtual byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey)
+        /// <exception cref="IOException"></exception>
+        public override byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey)
         {
-            basicAgreement.Init(clientPrivateKey);
-            BigInteger agreementValue = basicAgreement.CalculateAgreement(serverPublicKey);
+            mBasicAgreement.Init(mPrivateKey);
+            BigInteger agreementValue = mBasicAgreement.CalculateAgreement(peerPublicKey);
 
-            if (truncateAgreement)
+            if (mTruncateAgreement)
             {
                 return BigIntegers.AsUnsignedByteArray(agreementValue);
             }
 
-            return BigIntegers.AsUnsignedByteArray(basicAgreement.GetFieldSize(), agreementValue);
+            return BigIntegers.AsUnsignedByteArray(mBasicAgreement.GetFieldSize(), agreementValue);
         }
     }
 }
diff --git a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
index 18b23a67b..cc34b3028 100644
--- a/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/DefaultTlsCipherFactory.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     public class DefaultTlsCipherFactory
         : TlsCipherFactory
     {
-        public virtual TlsCipher CreateCipher(TlsClientContext context,
+        public virtual TlsCipher CreateCipher(TlsContext context,
             int encryptionAlgorithm, DigestAlgorithm digestAlgorithm)
         {
             switch (encryptionAlgorithm)
@@ -29,13 +29,13 @@ namespace Org.BouncyCastle.Crypto.Tls
         }
 
         /// <exception cref="IOException"></exception>
-        protected virtual TlsCipher CreateRC4Cipher(TlsClientContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm)
+        protected virtual TlsCipher CreateRC4Cipher(TlsContext context, int cipherKeySize, DigestAlgorithm digestAlgorithm)
         {
             return new TlsStreamCipher(context, CreateRC4StreamCipher(), CreateRC4StreamCipher(), CreateDigest(digestAlgorithm), CreateDigest(digestAlgorithm), cipherKeySize);
         }
 
         /// <exception cref="IOException"></exception>
-        protected virtual TlsCipher CreateAesCipher(TlsClientContext context, int cipherKeySize,
+        protected virtual TlsCipher CreateAesCipher(TlsContext context, int cipherKeySize,
             DigestAlgorithm digestAlgorithm)
         {
             return new TlsBlockCipher(context, CreateAesBlockCipher(), CreateAesBlockCipher(),
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         }
 
         /// <exception cref="IOException"></exception>
-        protected virtual TlsCipher CreateDesEdeCipher(TlsClientContext context, int cipherKeySize,
+        protected virtual TlsCipher CreateDesEdeCipher(TlsContext context, int cipherKeySize,
             DigestAlgorithm digestAlgorithm)
         {
             return new TlsBlockCipher(context, CreateDesEdeBlockCipher(), CreateDesEdeBlockCipher(),
diff --git a/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
new file mode 100644
index 000000000..34d15d146
--- /dev/null
+++ b/crypto/src/crypto/tls/DefaultTlsEncryptionCredentials.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto.Parameters;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public class DefaultTlsEncryptionCredentials
+        :   AbstractTlsEncryptionCredentials
+    {
+        protected readonly TlsContext mContext;
+        protected readonly Certificate mCertificate;
+        protected readonly AsymmetricKeyParameter mPrivateKey;
+
+        public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate,
+            AsymmetricKeyParameter privateKey)
+        {
+            if (certificate == null)
+                throw new ArgumentNullException("certificate");
+            if (certificate.IsEmpty)
+                throw new ArgumentException("cannot be empty", "certificate");
+            if (privateKey == null)
+                throw new ArgumentNullException("'privateKey' cannot be null");
+            if (!privateKey.IsPrivate)
+                throw new ArgumentException("must be private", "privateKey");
+
+            if (privateKey is RsaKeyParameters)
+            {
+            }
+            else
+            {
+                throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
+            }
+
+            this.mContext = context;
+            this.mCertificate = certificate;
+            this.mPrivateKey = privateKey;
+        }
+
+        public override Certificate Certificate
+        {
+            get { return mCertificate; }
+        }
+
+        /// <exception cref="IOException"></exception>
+        public override byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret)
+        {
+            return TlsRsaUtilities.SafeDecryptPreMasterSecret(mContext, (RsaKeyParameters)mPrivateKey, encryptedPreMasterSecret);
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
index 2c5aa3524..8e609938f 100644
--- a/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
+++ b/crypto/src/crypto/tls/DefaultTlsSignerCredentials.cs
@@ -1,76 +1,90 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Crypto.Parameters;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public class DefaultTlsSignerCredentials
-        : TlsSignerCredentials
+        :   AbstractTlsSignerCredentials
     {
-        protected TlsClientContext context;
-        protected Certificate clientCert;
-        protected AsymmetricKeyParameter clientPrivateKey;
+        protected readonly TlsContext mContext;
+        protected readonly Certificate mCertificate;
+        protected readonly AsymmetricKeyParameter mPrivateKey;
+        protected readonly SignatureAndHashAlgorithm mSignatureAndHashAlgorithm;
 
-        protected TlsSigner clientSigner;
+        protected readonly TlsSigner mSigner;
 
-        public DefaultTlsSignerCredentials(TlsClientContext context,
-            Certificate clientCertificate, AsymmetricKeyParameter clientPrivateKey)
+        public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey)
+            :   this(context, certificate, privateKey, null)
         {
-            if (clientCertificate == null)
-            {
-                throw new ArgumentNullException("clientCertificate");
-            }
-            if (clientCertificate.Length == 0)
-            {
+        }
+
+        public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey,
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+        {
+            if (certificate == null)
+                throw new ArgumentNullException("certificate");
+            if (certificate.IsEmpty)
                 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 (privateKey == null)
+                throw new ArgumentNullException("privateKey");
+            if (!privateKey.IsPrivate)
+                throw new ArgumentException("must be private", "privateKey");
+            if (TlsUtilities.IsTlsV12(context) && signatureAndHashAlgorithm == null)
+                throw new ArgumentException("cannot be null for (D)TLS 1.2+", "signatureAndHashAlgorithm");
 
-            if (clientPrivateKey is RsaKeyParameters)
+            if (privateKey is RsaKeyParameters)
             {
-                clientSigner = new TlsRsaSigner();
+                mSigner = new TlsRsaSigner();
             }
-            else if (clientPrivateKey is DsaPrivateKeyParameters)
+            else if (privateKey is DsaPrivateKeyParameters)
             {
-                clientSigner = new TlsDssSigner();
+                mSigner = new TlsDssSigner();
             }
-            else if (clientPrivateKey is ECPrivateKeyParameters)
+            else if (privateKey is ECPrivateKeyParameters)
             {
-                clientSigner = new TlsECDsaSigner();
+                mSigner = new TlsECDsaSigner();
             }
             else
             {
-                throw new ArgumentException("type not supported: "
-                    + clientPrivateKey.GetType().FullName, "clientPrivateKey");
+                throw new ArgumentException("type not supported: " + privateKey.GetType().FullName, "privateKey");
             }
 
-            this.context = context;
-            this.clientCert = clientCertificate;
-            this.clientPrivateKey = clientPrivateKey;
+            this.mContext = context;
+            this.mCertificate = certificate;
+            this.mPrivateKey = privateKey;
+            this.mSignatureAndHashAlgorithm = signatureAndHashAlgorithm;
         }
 
-        public virtual Certificate Certificate
+        public override Certificate Certificate
         {
-            get { return clientCert; }
+            get { return mCertificate; }
         }
 
-        public virtual byte[] GenerateCertificateSignature(byte[] md5andsha1)
+        /// <exception cref="IOException"></exception>
+        public override byte[] GenerateCertificateSignature(byte[] hash)
         {
             try
             {
-                return clientSigner.GenerateRawSignature(context.SecureRandom, clientPrivateKey, md5andsha1);
+                if (TlsUtilities.IsTlsV12(mContext))
+                {
+                    return mSigner.GenerateRawSignature(mSignatureAndHashAlgorithm, mPrivateKey, hash);
+                }
+                else
+                {
+                    return mSigner.GenerateRawSignature(mPrivateKey, hash);
+                }
             }
             catch (CryptoException)
             {
                 throw new TlsFatalAlert(AlertDescription.internal_error);
             }
         }
+
+        public override SignatureAndHashAlgorithm SignatureAndHashAlgorithm
+        {
+            get { return mSignatureAndHashAlgorithm; }
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/SecurityParameters.cs b/crypto/src/crypto/tls/SecurityParameters.cs
index 9ed3969eb..3aa318da2 100644
--- a/crypto/src/crypto/tls/SecurityParameters.cs
+++ b/crypto/src/crypto/tls/SecurityParameters.cs
@@ -1,26 +1,46 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	public class SecurityParameters
-	{
-		internal byte[] clientRandom = null;
-		internal byte[] serverRandom = null;
-		internal byte[] masterSecret = null;
+    public class SecurityParameters
+    {
+        internal int prfAlgorithm = -1;
+        internal byte[] masterSecret = null;
+        internal byte[] clientRandom = null;
+        internal byte[] serverRandom = null;
+
+        internal virtual void Clear()
+        {
+            if (this.masterSecret != null)
+            {
+                Arrays.Fill(this.masterSecret, (byte)0);
+                this.masterSecret = null;
+            }
+        }
+
+        /**
+         * @return {@link PRFAlgorithm}
+         */
+        public virtual int PrfAlgorithm
+        {
+            get { return prfAlgorithm; }
+        }
 
-		public byte[] ClientRandom
-		{
-			get { return clientRandom; }
-		}
+        public virtual byte[] MasterSecret
+        {
+            get { return masterSecret; }
+        }
 
-		public byte[] ServerRandom
-		{
-			get { return serverRandom; }
-		}
+        public virtual byte[] ClientRandom
+        {
+            get { return clientRandom; }
+        }
 
-		public byte[] MasterSecret
-		{
-			get { return masterSecret; }
-		}
-	}
+        public virtual byte[] ServerRandom
+        {
+            get { return serverRandom; }
+        }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsAgreementCredentials.cs b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
index 46ee4f90e..7c64072e8 100644
--- a/crypto/src/crypto/tls/TlsAgreementCredentials.cs
+++ b/crypto/src/crypto/tls/TlsAgreementCredentials.cs
@@ -3,9 +3,10 @@ using System.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	public interface TlsAgreementCredentials : TlsCredentials
-	{
-		/// <exception cref="IOException"></exception>
-		byte[] GenerateAgreement(AsymmetricKeyParameter serverPublicKey);
-	}
+    public interface TlsAgreementCredentials
+        :   TlsCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        byte[] GenerateAgreement(AsymmetricKeyParameter peerPublicKey);
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsBlockCipher.cs b/crypto/src/crypto/tls/TlsBlockCipher.cs
index cfbceb25e..7adab1985 100644
--- a/crypto/src/crypto/tls/TlsBlockCipher.cs
+++ b/crypto/src/crypto/tls/TlsBlockCipher.cs
@@ -15,7 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     public class TlsBlockCipher
         : TlsCipher
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
         protected byte[] randomData;
 
         protected IBlockCipher encryptCipher;
@@ -34,7 +34,7 @@ namespace Org.BouncyCastle.Crypto.Tls
             get { return rMac; }
         }
 
-        public TlsBlockCipher(TlsClientContext context, IBlockCipher encryptCipher,
+        public TlsBlockCipher(TlsContext context, IBlockCipher encryptCipher,
             IBlockCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
         {
             this.context = context;
@@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             SecurityParameters securityParameters = context.SecurityParameters;
 
-            byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
+            byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, "key expansion",
                 TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
                 prfSize);
 
diff --git a/crypto/src/crypto/tls/TlsCipherFactory.cs b/crypto/src/crypto/tls/TlsCipherFactory.cs
index bd65f8b4b..e5cf96479 100644
--- a/crypto/src/crypto/tls/TlsCipherFactory.cs
+++ b/crypto/src/crypto/tls/TlsCipherFactory.cs
@@ -6,7 +6,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     public interface TlsCipherFactory
     {
         /// <exception cref="IOException"></exception>
-        TlsCipher CreateCipher(TlsClientContext context, int encryptionAlgorithm,
+        TlsCipher CreateCipher(TlsContext context, int encryptionAlgorithm,
             DigestAlgorithm digestAlgorithm);
     }
 }
diff --git a/crypto/src/crypto/tls/TlsClientContext.cs b/crypto/src/crypto/tls/TlsClientContext.cs
index dbb10aa76..b077d0aaf 100644
--- a/crypto/src/crypto/tls/TlsClientContext.cs
+++ b/crypto/src/crypto/tls/TlsClientContext.cs
@@ -4,12 +4,8 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	public interface TlsClientContext
-	{
-		SecureRandom SecureRandom { get; }
-
-		SecurityParameters SecurityParameters { get; }
-
-		object UserObject { get; set; }
-	}
+    public interface TlsClientContext
+        :   TlsContext
+    {
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsClientContextImpl.cs b/crypto/src/crypto/tls/TlsClientContextImpl.cs
index 9d5dee232..674d68937 100644
--- a/crypto/src/crypto/tls/TlsClientContextImpl.cs
+++ b/crypto/src/crypto/tls/TlsClientContextImpl.cs
@@ -4,34 +4,17 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	internal class TlsClientContextImpl
-		: TlsClientContext
-	{
-		private readonly SecureRandom secureRandom;
-		private readonly SecurityParameters securityParameters;
+    internal class TlsClientContextImpl
+        :   AbstractTlsContext, TlsClientContext
+    {
+        internal TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+            :   base(secureRandom, 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; }
-		}
-	}
+        public override bool IsServer
+        {
+            get { return false; }
+        }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsDHKeyExchange.cs b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
index 26d76fd3d..272e38143 100644
--- a/crypto/src/crypto/tls/TlsDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDHKeyExchange.cs
@@ -15,7 +15,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsDHKeyExchange
         : TlsKeyExchange
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
         protected int keyExchange;
         protected TlsSigner tlsSigner;
 
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected TlsAgreementCredentials agreementCredentials;
         protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
 
-        internal TlsDHKeyExchange(TlsClientContext context, int keyExchange)
+        internal TlsDHKeyExchange(TlsContext context, int keyExchange)
         {
             switch (keyExchange)
             {
diff --git a/crypto/src/crypto/tls/TlsDheKeyExchange.cs b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
index ee6d6eb44..a9e9a394c 100644
--- a/crypto/src/crypto/tls/TlsDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsDheKeyExchange.cs
@@ -10,7 +10,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsDheKeyExchange
         : TlsDHKeyExchange
     {
-        internal TlsDheKeyExchange(TlsClientContext context, int keyExchange)
+        internal TlsDheKeyExchange(TlsContext context, int keyExchange)
             : base(context, keyExchange)
         {
         }
diff --git a/crypto/src/crypto/tls/TlsDsaSigner.cs b/crypto/src/crypto/tls/TlsDsaSigner.cs
index bba114e90..a5ac55974 100644
--- a/crypto/src/crypto/tls/TlsDsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsDsaSigner.cs
@@ -7,45 +7,77 @@ using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    internal abstract class TlsDsaSigner
-        :	TlsSigner
+    public abstract class TlsDsaSigner
+        :	AbstractTlsSigner
     {
-        public virtual byte[] GenerateRawSignature(SecureRandom random,
-            AsymmetricKeyParameter privateKey, byte[] md5andsha1)
+        public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+            AsymmetricKeyParameter privateKey, byte[] hash)
         {
-            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();
+            ISigner signer = MakeSigner(algorithm, true, true,
+                new ParametersWithRandom(privateKey, this.mContext.SecureRandom));
+            if (algorithm == null)
+            {
+                // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+                signer.BlockUpdate(hash, 16, 20);
+            }
+            else
+            {
+                signer.BlockUpdate(hash, 0, hash.Length);
+            }
+            return signer.GenerateSignature();
         }
 
-        public bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1)
+        public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+            AsymmetricKeyParameter publicKey, byte[] hash)
         {
-            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);
+            ISigner signer = MakeSigner(algorithm, true, false, publicKey);
+            if (algorithm == null)
+            {
+                // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+                signer.BlockUpdate(hash, 16, 20);
+            }
+            else
+            {
+                signer.BlockUpdate(hash, 0, hash.Length);
+            }
+            return signer.VerifySignature(sigBytes);
         }
 
-        public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+        public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
         {
-            return MakeSigner(new Sha1Digest(), true, new ParametersWithRandom(privateKey, random));
+            return MakeSigner(algorithm, false, true, privateKey);
         }
 
-        public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+        public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
         {
-            return MakeSigner(new Sha1Digest(), false, publicKey);
+            return MakeSigner(algorithm, false, false, publicKey);
         }
 
-        public abstract bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
+        protected virtual ICipherParameters MakeInitParameters(bool forSigning, ICipherParameters cp)
+        {
+            return cp;
+        }
 
-        protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+        protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning,
+            ICipherParameters cp)
         {
-            ISigner s = new DsaDigestSigner(CreateDsaImpl(), d);
-            s.Init(forSigning, cp);
+            if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext))
+                throw new InvalidOperationException();
+
+            // TODO For TLS 1.2+, lift the SHA-1 restriction here
+            if (algorithm != null && (algorithm.Hash != HashAlgorithm.sha1 || algorithm.Signature != SignatureAlgorithm))
+                throw new InvalidOperationException();
+
+            byte hashAlgorithm = algorithm == null ? HashAlgorithm.sha1 : algorithm.Hash;
+            IDigest d = raw ? new NullDigest() : TlsUtilities.CreateHash(hashAlgorithm);
+
+            ISigner s = new DsaDigestSigner(CreateDsaImpl(hashAlgorithm), d);
+            s.Init(forSigning, MakeInitParameters(forSigning, cp));
             return s;
         }
 
-        protected abstract IDsa CreateDsaImpl();
+        protected abstract byte SignatureAlgorithm { get; }
+
+        protected abstract IDsa CreateDsaImpl(byte hashAlgorithm);
     }
 }
diff --git a/crypto/src/crypto/tls/TlsDssSigner.cs b/crypto/src/crypto/tls/TlsDssSigner.cs
index c6f1abcec..707ef3853 100644
--- a/crypto/src/crypto/tls/TlsDssSigner.cs
+++ b/crypto/src/crypto/tls/TlsDssSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	internal class TlsDssSigner
-		: TlsDsaSigner
-	{
-		public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
-		{
-			return publicKey is DsaPublicKeyParameters;
-		}
+    public class TlsDssSigner
+        :   TlsDsaSigner
+    {
+        public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+        {
+            return publicKey is DsaPublicKeyParameters;
+        }
 
-	    protected override IDsa CreateDsaImpl()
-	    {
-			return new DsaSigner();
-	    }
-	}
+        protected override IDsa CreateDsaImpl(byte hashAlgorithm)
+        {
+            return new DsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm)));
+        }
+
+        protected override byte SignatureAlgorithm
+        {
+            get { return Tls.SignatureAlgorithm.dsa; }
+        }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
index 65d07a10c..b02d5a4fd 100644
--- a/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDHKeyExchange.cs
@@ -18,7 +18,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsECDHKeyExchange
         : TlsKeyExchange
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
         protected int keyExchange;
         protected TlsSigner tlsSigner;
 
@@ -27,7 +27,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected TlsAgreementCredentials agreementCredentials;
         protected ECPrivateKeyParameters ecAgreeClientPrivateKey = null;
 
-        internal TlsECDHKeyExchange(TlsClientContext context, int keyExchange)
+        internal TlsECDHKeyExchange(TlsContext context, int keyExchange)
         {
             switch (keyExchange)
             {
diff --git a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
index a36bff75b..5f66dbf59 100644
--- a/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsECDheKeyExchange.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     */
     internal class TlsECDheKeyExchange : TlsECDHKeyExchange
     {
-        internal TlsECDheKeyExchange(TlsClientContext context, int keyExchange)
+        internal TlsECDheKeyExchange(TlsContext context, int keyExchange)
             : base(context, keyExchange)
         {
         }
diff --git a/crypto/src/crypto/tls/TlsECDsaSigner.cs b/crypto/src/crypto/tls/TlsECDsaSigner.cs
index 3c30fdc0c..fa9d0b714 100644
--- a/crypto/src/crypto/tls/TlsECDsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsECDsaSigner.cs
@@ -5,17 +5,22 @@ using Org.BouncyCastle.Crypto.Signers;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	internal class TlsECDsaSigner
-		: TlsDsaSigner
-	{
-		public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
-		{
-			return publicKey is ECPublicKeyParameters;
-		}
+    public class TlsECDsaSigner
+        :   TlsDsaSigner
+    {
+        public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+        {
+            return publicKey is ECPublicKeyParameters;
+        }
 
-		protected override IDsa CreateDsaImpl()
-		{
-			return new ECDsaSigner();
-		}
-	}
+        protected override IDsa CreateDsaImpl(byte hashAlgorithm)
+        {
+            return new ECDsaSigner(new HMacDsaKCalculator(TlsUtilities.CreateHash(hashAlgorithm)));
+        }
+
+        protected override byte SignatureAlgorithm
+        {
+            get { return Tls.SignatureAlgorithm.ecdsa; }
+        }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsEncryptionCredentials.cs b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
new file mode 100644
index 000000000..52f007006
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsEncryptionCredentials.cs
@@ -0,0 +1,12 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsEncryptionCredentials
+        :   TlsCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        byte[] DecryptPreMasterSecret(byte[] encryptedPreMasterSecret);
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsProtocolHandler.cs b/crypto/src/crypto/tls/TlsProtocolHandler.cs
index 72bf8c5cf..cf1296b14 100644
--- a/crypto/src/crypto/tls/TlsProtocolHandler.cs
+++ b/crypto/src/crypto/tls/TlsProtocolHandler.cs
@@ -269,9 +269,8 @@ namespace Org.BouncyCastle.Crypto.Tls
                             /*
                              * Calculate our own checksum.
                              */
-                            byte[] expectedServerVerifyData = TlsUtilities.PRF(
-                                securityParameters.masterSecret, "server finished",
-                                rs.GetCurrentHash(), 12);
+                            byte[] expectedServerVerifyData = TlsUtilities.PRF(tlsClientContext,
+                                securityParameters.masterSecret, ExporterLabel.server_finished, rs.GetCurrentHash(), 12);
 
                             /*
                              * Compare both checksums.
@@ -388,7 +387,7 @@ namespace Org.BouncyCastle.Crypto.Tls
                                         continue;
 
                                     // TODO Add session resumption support
-                                    ///*
+                                    //*
                                     // * 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[.]
@@ -546,9 +545,8 @@ namespace Org.BouncyCastle.Crypto.Tls
                              */
                             byte[] pms = this.keyExchange.GeneratePremasterSecret();
 
-                            securityParameters.masterSecret = TlsUtilities.PRF(pms, "master secret",
-                                TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom),
-                                48);
+                            securityParameters.masterSecret = TlsUtilities.PRF(tlsClientContext, pms, ExporterLabel.master_secret,
+                                TlsUtilities.Concat(securityParameters.clientRandom, securityParameters.serverRandom), 48);
 
                             // TODO Is there a way to ensure the data is really overwritten?
                             /*
@@ -565,8 +563,8 @@ namespace Org.BouncyCastle.Crypto.Tls
                             /*
                              * Send our finished message.
                              */
-                            byte[] clientVerifyData = TlsUtilities.PRF(securityParameters.masterSecret,
-                                "client finished", rs.GetCurrentHash(), 12);
+                            byte[] clientVerifyData = TlsUtilities.PRF(tlsClientContext, securityParameters.masterSecret,
+                                ExporterLabel.client_finished, rs.GetCurrentHash(), 12);
 
                             MemoryStream bos = new MemoryStream();
                             TlsUtilities.WriteUint8((byte)HandshakeType.finished, bos);
@@ -818,19 +816,22 @@ namespace Org.BouncyCastle.Crypto.Tls
             if (this.tlsClient != null)
                 throw new InvalidOperationException("Connect can only be called once");
 
-            /*
-             * Send Client hello
-             *
-             * First, generate some random data.
-             */
+            this.tlsClient = tlsClient;
+
             this.securityParameters = new SecurityParameters();
-            this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(), random,
-                ExporterLabel.client_random);
 
             this.tlsClientContext = new TlsClientContextImpl(random, securityParameters);
-            this.tlsClient = tlsClient;
+
+            this.securityParameters.clientRandom = CreateRandomBlock(tlsClient.ShouldUseGmtUnixTime(),
+                tlsClientContext.NonceRandomGenerator);
+
             this.tlsClient.Init(tlsClientContext);
 
+            /*
+             * Send Client hello
+             *
+             * First, send the client_random data.
+             */
             MemoryStream outStr = new MemoryStream();
             TlsUtilities.WriteVersion(outStr);
             outStr.Write(securityParameters.clientRandom, 0, 32);
@@ -1160,20 +1161,10 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        protected static byte[] CreateRandomBlock(bool useGMTUnixTime, SecureRandom random, string asciiLabel)
+        protected static byte[] CreateRandomBlock(bool useGMTUnixTime, IRandomGenerator randomGenerator)
         {
-            /*
-             * We use the TLS 1.0 PRF on the SecureRandom output, to guard against RNGs where the raw
-             * output could be used to recover the internal state.
-             */
-            byte[] secret = new byte[32];
-            random.NextBytes(secret);
-
-            byte[] seed = new byte[8];
-            // TODO Use high-resolution timer
-            TlsUtilities.WriteUint64(DateTimeUtilities.CurrentUnixMs(), seed, 0);
-
-            byte[] result = TlsUtilities.PRF(secret, asciiLabel, seed, 32);
+            byte[] result = new byte[32];
+            randomGenerator.NextBytes(result);
 
             if (useGMTUnixTime)
             {
diff --git a/crypto/src/crypto/tls/TlsPskKeyExchange.cs b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
index 9961fc9d1..4a5cb4ead 100644
--- a/crypto/src/crypto/tls/TlsPskKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsPskKeyExchange.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsPskKeyExchange
         : TlsKeyExchange
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
         protected int keyExchange;
         protected TlsPskIdentity pskIdentity;
 
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected RsaKeyParameters rsaServerPublicKey = null;
         protected byte[] premasterSecret;
 
-        internal TlsPskKeyExchange(TlsClientContext context, int keyExchange,
+        internal TlsPskKeyExchange(TlsContext context, int keyExchange,
             TlsPskIdentity pskIdentity)
         {
             switch (keyExchange)
@@ -139,7 +139,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         public virtual void GenerateClientKeyExchange(Stream output)
         {
-            if (psk_identity_hint == null || psk_identity_hint.Length == 0)
+            if (psk_identity_hint == null)
             {
                 pskIdentity.SkipIdentityHint();
             }
@@ -152,16 +152,21 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             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)
+            if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
             {
                 this.dhAgreeClientPrivateKey = TlsDHUtilities.GenerateEphemeralClientKeyExchange(
                     context.SecureRandom, this.dhAgreeServerPublicKey.Parameters, output);
             }
+            else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+            {
+                // TODO[RFC 5489]
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+            else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+            {
+                this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
+                    context, this.rsaServerPublicKey, output);
+            }
         }
 
         public virtual byte[] GeneratePremasterSecret()
diff --git a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
index aad482316..0a9fbc67d 100644
--- a/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsRsaKeyExchange.cs
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsRsaKeyExchange
         : TlsKeyExchange
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
 
         protected AsymmetricKeyParameter serverPublicKey = null;
 
@@ -25,7 +25,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected byte[] premasterSecret;
 
-        internal TlsRsaKeyExchange(TlsClientContext context)
+        internal TlsRsaKeyExchange(TlsContext context)
         {
             this.context = context;
         }
@@ -110,8 +110,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         
         public virtual void GenerateClientKeyExchange(Stream output)
         {
-            this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(
-                context.SecureRandom, this.rsaServerPublicKey, output);
+            this.premasterSecret = TlsRsaUtilities.GenerateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output);
         }
 
         public virtual byte[] GeneratePremasterSecret()
diff --git a/crypto/src/crypto/tls/TlsRsaSigner.cs b/crypto/src/crypto/tls/TlsRsaSigner.cs
index ce18ef5e1..6da1c5e9b 100644
--- a/crypto/src/crypto/tls/TlsRsaSigner.cs
+++ b/crypto/src/crypto/tls/TlsRsaSigner.cs
@@ -10,50 +10,92 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-    internal class TlsRsaSigner
-        : TlsSigner
+    public class TlsRsaSigner
+        :   AbstractTlsSigner
     {
-        public virtual byte[] GenerateRawSignature(SecureRandom random,
-            AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+        public override byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+            AsymmetricKeyParameter privateKey, byte[] hash)
         {
-            IAsymmetricBlockCipher engine = CreateRsaImpl();
-            engine.Init(true, new ParametersWithRandom(privateKey, random));
-            return engine.ProcessBlock(md5AndSha1, 0, md5AndSha1.Length);
+            ISigner signer = MakeSigner(algorithm, true, true,
+                new ParametersWithRandom(privateKey, this.mContext.SecureRandom));
+            signer.BlockUpdate(hash, 0, hash.Length);
+            return signer.GenerateSignature();
         }
 
-        public virtual bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey,
-            byte[] md5AndSha1)
+        public override bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+            AsymmetricKeyParameter publicKey, byte[] hash)
         {
-            IAsymmetricBlockCipher engine = CreateRsaImpl();
-            engine.Init(false, publicKey);
-            byte[] signed = engine.ProcessBlock(sigBytes, 0, sigBytes.Length);
-            return Arrays.ConstantTimeAreEqual(signed, md5AndSha1);
+            ISigner signer = MakeSigner(algorithm, true, false, publicKey);
+            signer.BlockUpdate(hash, 0, hash.Length);
+            return signer.VerifySignature(sigBytes);
         }
 
-        public virtual ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey)
+        public override ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
         {
-            return MakeSigner(new CombinedHash(), true, new ParametersWithRandom(privateKey, random));
+            return MakeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.mContext.SecureRandom));
         }
 
-        public virtual ISigner CreateVerifyer(AsymmetricKeyParameter publicKey)
+        public override ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
         {
-            return MakeSigner(new CombinedHash(), false, publicKey);
+            return MakeSigner(algorithm, false, false, publicKey);
         }
 
-        public virtual bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
+        public override bool IsValidPublicKey(AsymmetricKeyParameter publicKey)
         {
             return publicKey is RsaKeyParameters && !publicKey.IsPrivate;
         }
 
-        protected virtual ISigner MakeSigner(IDigest d, bool forSigning, ICipherParameters cp)
+        protected virtual ISigner MakeSigner(SignatureAndHashAlgorithm algorithm, bool raw, bool forSigning,
+            ICipherParameters cp)
         {
-            ISigner s = new GenericSigner(CreateRsaImpl(), d);
+            if ((algorithm != null) != TlsUtilities.IsTlsV12(mContext))
+                throw new InvalidOperationException();
+            if (algorithm != null && algorithm.Signature != SignatureAlgorithm.rsa)
+                throw new InvalidOperationException();
+
+            IDigest d;
+            if (raw)
+            {
+                d = new NullDigest();
+            }
+            else if (algorithm == null)
+            {
+                d = new CombinedHash();
+            }
+            else
+            {
+                d = TlsUtilities.CreateHash(algorithm.Hash);
+            }
+
+            ISigner s;
+            if (algorithm != null)
+            {
+                /*
+                 * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated
+                 * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1].
+                 */
+                s = new RsaDigestSigner(d, TlsUtilities.GetOidForHashAlgorithm(algorithm.Hash));
+            }
+            else
+            {
+                /*
+                 * RFC 5246 4.7. Note that earlier versions of TLS used a different RSA signature scheme
+                 * that did not include a DigestInfo encoding.
+                 */
+                s = new GenericSigner(CreateRsaImpl(), d);
+            }
             s.Init(forSigning, cp);
             return s;
         }
 
         protected virtual IAsymmetricBlockCipher CreateRsaImpl()
         {
+            /*
+             * RFC 5264 7.4.7.1. Implementation note: It is now known that remote timing-based attacks
+             * on TLS are possible, at least when the client and server are on the same LAN.
+             * Accordingly, implementations that use static RSA keys MUST use RSA blinding or some other
+             * anti-timing technique, as described in [TIMING].
+             */
             return new Pkcs1Encoding(new RsaBlindedEngine());
         }
     }
diff --git a/crypto/src/crypto/tls/TlsRsaUtilities.cs b/crypto/src/crypto/tls/TlsRsaUtilities.cs
index 4450ba452..de56ffc4d 100644
--- a/crypto/src/crypto/tls/TlsRsaUtilities.cs
+++ b/crypto/src/crypto/tls/TlsRsaUtilities.cs
@@ -5,38 +5,128 @@ using Org.BouncyCastle.Crypto.Encodings;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Crypto.Engines;
 using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
 
 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;
-		}
-	}
+    public abstract class TlsRsaUtilities
+    {
+        /// <exception cref="IOException"></exception>
+        public static byte[] GenerateEncryptedPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPublicKey,
+            Stream output)
+        {
+            /*
+             * Choose a PremasterSecret and send it encrypted to the server
+             */
+            byte[] premasterSecret = new byte[48];
+            context.SecureRandom.NextBytes(premasterSecret);
+            TlsUtilities.WriteVersion(context.ClientVersion, premasterSecret, 0);
+
+            Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine());
+            encoding.Init(true, new ParametersWithRandom(rsaServerPublicKey, context.SecureRandom));
+
+            try
+            {
+                byte[] encryptedPreMasterSecret = encoding.ProcessBlock(premasterSecret, 0, premasterSecret.Length);
+
+                if (TlsUtilities.IsSsl(context))
+                {
+                    // TODO Do any SSLv3 servers actually expect the length?
+                    output.Write(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length);
+                }
+                else
+                {
+                    TlsUtilities.WriteOpaque16(encryptedPreMasterSecret, output);
+                }
+            }
+            catch (InvalidCipherTextException)
+            {
+                /*
+                 * This should never happen, only during decryption.
+                 */
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            return premasterSecret;
+        }
+
+        public static byte[] SafeDecryptPreMasterSecret(TlsContext context, RsaKeyParameters rsaServerPrivateKey,
+            byte[] encryptedPreMasterSecret)
+        {
+            /*
+             * RFC 5246 7.4.7.1.
+             */
+            ProtocolVersion clientVersion = context.ClientVersion;
+
+            // TODO Provide as configuration option?
+            bool versionNumberCheckDisabled = false;
+
+            /*
+             * Generate 48 random bytes we can use as a Pre-Master-Secret, if the
+             * PKCS1 padding check should fail.
+             */
+            byte[] fallback = new byte[48];
+            context.SecureRandom.NextBytes(fallback);
+
+            byte[] M = Arrays.Clone(fallback);
+            try
+            {
+                Pkcs1Encoding encoding = new Pkcs1Encoding(new RsaBlindedEngine(), fallback);
+                encoding.Init(false,
+                    new ParametersWithRandom(rsaServerPrivateKey, context.SecureRandom));
+
+                M = encoding.ProcessBlock(encryptedPreMasterSecret, 0, encryptedPreMasterSecret.Length);
+            }
+            catch (Exception)
+            {
+                /*
+                 * This should never happen since the decryption should never throw an exception
+                 * and return a random value instead.
+                 *
+                 * In any case, a TLS server MUST NOT generate an alert if processing an
+                 * RSA-encrypted premaster secret message fails, or the version number is not as
+                 * expected. Instead, it MUST continue the handshake with a randomly generated
+                 * premaster secret.
+                 */
+            }
+
+            /*
+             * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
+             * check the version number [..].
+             */
+            if (versionNumberCheckDisabled && clientVersion.IsEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
+            {
+                /*
+                 * If the version number is TLS 1.0 or earlier, server
+                 * implementations SHOULD check the version number, but MAY have a
+                 * configuration option to disable the check.
+                 *
+                 * So there is nothing to do here.
+                 */
+            }
+            else
+            {
+                /*
+                 * OK, we need to compare the version number in the decrypted Pre-Master-Secret with the
+                 * clientVersion received during the handshake. If they don't match, we replace the
+                 * decrypted Pre-Master-Secret with a random one.
+                 */
+                int correct = (clientVersion.MajorVersion ^ (M[0] & 0xff))
+                    | (clientVersion.MinorVersion ^ (M[1] & 0xff));
+                correct |= correct >> 1;
+                correct |= correct >> 2;
+                correct |= correct >> 4;
+                int mask = ~((correct & 1) - 1);
+
+                /*
+                 * mask will be all bits set to 0xff if the version number differed.
+                 */
+                for (int i = 0; i < 48; i++)
+                {
+                    M[i] = (byte)((M[i] & (~mask)) | (fallback[i] & mask));
+                }
+            }
+            return M;
+        }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsServerContext.cs b/crypto/src/crypto/tls/TlsServerContext.cs
new file mode 100644
index 000000000..4021571aa
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServerContext.cs
@@ -0,0 +1,11 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    public interface TlsServerContext
+        : TlsContext
+    {
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsServerContextImpl.cs b/crypto/src/crypto/tls/TlsServerContextImpl.cs
new file mode 100644
index 000000000..d56566ffc
--- /dev/null
+++ b/crypto/src/crypto/tls/TlsServerContextImpl.cs
@@ -0,0 +1,20 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls
+{
+    internal class TlsServerContextImpl
+        : AbstractTlsContext, TlsServerContext
+    {
+        internal TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
+            : base(secureRandom, securityParameters)
+        {
+        }
+
+        public override bool IsServer
+        {
+            get { return true; }
+        }
+    }
+}
diff --git a/crypto/src/crypto/tls/TlsSigner.cs b/crypto/src/crypto/tls/TlsSigner.cs
index 79d468fee..ffdd4c9a1 100644
--- a/crypto/src/crypto/tls/TlsSigner.cs
+++ b/crypto/src/crypto/tls/TlsSigner.cs
@@ -1,18 +1,29 @@
 using System;
 
-using Org.BouncyCastle.Security;
-
 namespace Org.BouncyCastle.Crypto.Tls
 {
     public interface TlsSigner
     {
-        byte[] GenerateRawSignature(SecureRandom random, AsymmetricKeyParameter privateKey,
-            byte[] md5andsha1);
-        bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5andsha1);
+        void Init(TlsContext context);
+
+        byte[] GenerateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1);
+
+        byte[] GenerateRawSignature(SignatureAndHashAlgorithm algorithm,
+            AsymmetricKeyParameter privateKey, byte[] hash);
+
+        bool VerifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1);
+
+        bool VerifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+            AsymmetricKeyParameter publicKey, byte[] hash);
+
+        ISigner CreateSigner(AsymmetricKeyParameter privateKey);
+
+        ISigner CreateSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
 
-        ISigner CreateSigner(SecureRandom random, AsymmetricKeyParameter privateKey);
         ISigner CreateVerifyer(AsymmetricKeyParameter publicKey);
 
+        ISigner CreateVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
         bool IsValidPublicKey(AsymmetricKeyParameter publicKey);
     }
 }
diff --git a/crypto/src/crypto/tls/TlsSignerCredentials.cs b/crypto/src/crypto/tls/TlsSignerCredentials.cs
index 2adb06c26..92ed7cc19 100644
--- a/crypto/src/crypto/tls/TlsSignerCredentials.cs
+++ b/crypto/src/crypto/tls/TlsSignerCredentials.cs
@@ -3,9 +3,12 @@ using System.IO;
 
 namespace Org.BouncyCastle.Crypto.Tls
 {
-	public interface TlsSignerCredentials : TlsCredentials
-	{
-		/// <exception cref="IOException"></exception>
-		byte[] GenerateCertificateSignature(byte[] md5andsha1);
-	}
+    public interface TlsSignerCredentials
+        :   TlsCredentials
+    {
+        /// <exception cref="IOException"></exception>
+        byte[] GenerateCertificateSignature(byte[] hash);
+
+        SignatureAndHashAlgorithm SignatureAndHashAlgorithm { get; }
+    }
 }
diff --git a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
index 950be87ba..46e0e02b2 100644
--- a/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
+++ b/crypto/src/crypto/tls/TlsSrpKeyExchange.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Crypto.Tls
     internal class TlsSrpKeyExchange
         : TlsKeyExchange
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
         protected int keyExchange;
         protected TlsSigner tlsSigner;
         protected byte[] identity;
@@ -33,7 +33,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected BigInteger B = null;
         protected Srp6Client srpClient = new Srp6Client();
 
-        internal TlsSrpKeyExchange(TlsClientContext context, int keyExchange,
+        internal TlsSrpKeyExchange(TlsContext context, int keyExchange,
             byte[] identity, byte[] password)
         {
             switch (keyExchange)
diff --git a/crypto/src/crypto/tls/TlsStreamCipher.cs b/crypto/src/crypto/tls/TlsStreamCipher.cs
index 35f794d96..3e6f7e06d 100644
--- a/crypto/src/crypto/tls/TlsStreamCipher.cs
+++ b/crypto/src/crypto/tls/TlsStreamCipher.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 {
     public class TlsStreamCipher : TlsCipher
     {
-        protected TlsClientContext context;
+        protected TlsContext context;
 
         protected IStreamCipher encryptCipher;
         protected IStreamCipher decryptCipher;
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         protected TlsMac writeMac;
         protected TlsMac readMac;
 
-        public TlsStreamCipher(TlsClientContext context, IStreamCipher encryptCipher,
+        public TlsStreamCipher(TlsContext context, IStreamCipher encryptCipher,
             IStreamCipher decryptCipher, IDigest writeDigest, IDigest readDigest, int cipherKeySize)
         {
             this.context = context;
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Crypto.Tls
 
             SecurityParameters securityParameters = context.SecurityParameters;
 
-            byte[] keyBlock = TlsUtilities.PRF(securityParameters.masterSecret, "key expansion",
+            byte[] keyBlock = TlsUtilities.PRF(context, securityParameters.masterSecret, "key expansion",
                 TlsUtilities.Concat(securityParameters.serverRandom, securityParameters.clientRandom),
                 prfSize);
 
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index ffb2fc3e6..462ec4074 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -4,6 +4,8 @@ using System.IO;
 using System.Text;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Nist;
+using Org.BouncyCastle.Asn1.Pkcs;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto.Digests;
 using Org.BouncyCastle.Crypto.Macs;
@@ -660,37 +662,54 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
-        internal static byte[] PRF(byte[] secret, string asciiLabel, byte[] seed, int size)
+        public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size)
         {
-            byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+            ProtocolVersion version = context.ServerVersion;
 
-            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);
+            if (version.IsSsl)
+                throw new InvalidOperationException("No PRF available for SSLv3 session");
+
+            byte[] label = Strings.ToByteArray(asciiLabel);
+            byte[] labelSeed = Concat(label, seed);
 
-            byte[] ls = Concat(label, seed);
+            int prfAlgorithm = context.SecurityParameters.PrfAlgorithm;
 
-            byte[] buf = new byte[size];
-            byte[] prf = new byte[size];
-            HMacHash(new MD5Digest(), s1, ls, prf);
-            HMacHash(new Sha1Digest(), s2, ls, buf);
-            for (int i = 0; i < size; i++)
+            if (prfAlgorithm == PrfAlgorithm.tls_prf_legacy)
             {
-                buf[i] ^= prf[i];
+                return PRF_legacy(secret, label, labelSeed, size);
             }
+
+            IDigest prfDigest = CreatePrfHash(prfAlgorithm);
+            byte[] buf = new byte[size];
+            HMacHash(prfDigest, secret, labelSeed, buf);
             return buf;
         }
 
-        internal static byte[] PRF_1_2(IDigest digest, byte[] secret, string asciiLabel, byte[] seed, int size)
+        public static byte[] PRF_legacy(byte[] secret, string asciiLabel, byte[] seed, int size)
         {
-            byte[] label = Strings.ToAsciiByteArray(asciiLabel);
+            byte[] label = Strings.ToByteArray(asciiLabel);
             byte[] labelSeed = Concat(label, seed);
 
-            byte[] buf = new byte[size];
-            HMacHash(digest, secret, labelSeed, buf);
-            return buf;
+            return PRF_legacy(secret, label, labelSeed, size);
+        }
+
+        internal static byte[] PRF_legacy(byte[] secret, byte[] label, byte[] labelSeed, int size)
+        {
+            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[] b1 = new byte[size];
+            byte[] b2 = new byte[size];
+            HMacHash(CreateHash(HashAlgorithm.md5), s1, labelSeed, b1);
+            HMacHash(CreateHash(HashAlgorithm.sha1), s2, labelSeed, b2);
+            for (int i = 0; i < size; i++)
+            {
+                b1[i] ^= b2[i];
+            }
+            return b1;
         }
 
         internal static byte[] Concat(byte[] a, byte[] b)
@@ -782,6 +801,64 @@ namespace Org.BouncyCastle.Crypto.Tls
             }
         }
 
+        public static IDigest CreatePrfHash(int prfAlgorithm)
+        {
+            switch (prfAlgorithm)
+            {
+                case PrfAlgorithm.tls_prf_legacy:
+                    return new CombinedHash();
+                default:
+                    return CreateHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm));
+            }
+        }
+
+        public static IDigest ClonePrfHash(int prfAlgorithm, IDigest hash)
+        {
+            switch (prfAlgorithm)
+            {
+                case PrfAlgorithm.tls_prf_legacy:
+                    return new CombinedHash((CombinedHash)hash);
+                default:
+                    return CloneHash(GetHashAlgorithmForPrfAlgorithm(prfAlgorithm), hash);
+            }
+        }
+
+        public static byte GetHashAlgorithmForPrfAlgorithm(int prfAlgorithm)
+        {
+            switch (prfAlgorithm)
+            {
+                case PrfAlgorithm.tls_prf_legacy:
+                    throw new ArgumentException("legacy PRF not a valid algorithm", "prfAlgorithm");
+                case PrfAlgorithm.tls_prf_sha256:
+                    return HashAlgorithm.sha256;
+                case PrfAlgorithm.tls_prf_sha384:
+                    return HashAlgorithm.sha384;
+                default:
+                    throw new ArgumentException("unknown PrfAlgorithm", "prfAlgorithm");
+            }
+        }
+
+        public static DerObjectIdentifier GetOidForHashAlgorithm(byte hashAlgorithm)
+        {
+            switch (hashAlgorithm)
+            {
+                case HashAlgorithm.md5:
+                    return PkcsObjectIdentifiers.MD5;
+                case HashAlgorithm.sha1:
+                    return X509ObjectIdentifiers.IdSha1;
+                case HashAlgorithm.sha224:
+                    return NistObjectIdentifiers.IdSha224;
+                case HashAlgorithm.sha256:
+                    return NistObjectIdentifiers.IdSha256;
+                case HashAlgorithm.sha384:
+                    return NistObjectIdentifiers.IdSha384;
+                case HashAlgorithm.sha512:
+                    return NistObjectIdentifiers.IdSha512;
+                default:
+                    throw new ArgumentException("unknown HashAlgorithm", "hashAlgorithm");
+            }
+        }
+
         private static IList VectorOfOne(object obj)
         {
             IList v = Platform.CreateArrayList(1);