summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2015-12-16 22:50:41 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2015-12-16 22:50:41 +0700
commitae227e65ab48b8747c80e1b336a3e6896e9e346e (patch)
treebff90e085f89872399328db0391f9e8b4f7368f0
parentUpdate version for release (diff)
downloadBouncyCastle.NET-ed25519-ae227e65ab48b8747c80e1b336a3e6896e9e346e.tar.xz
Validate CertificateVerify signature algorithm (TLS 1.2+)
- check the algorithm is in the CertificateRequest list
- add (D)TLS test scenarios for various failure modes
-rw-r--r--crypto/crypto.csproj20
-rw-r--r--crypto/src/crypto/tls/DeferredHash.cs2
-rw-r--r--crypto/src/crypto/tls/DtlsServerProtocol.cs23
-rw-r--r--crypto/src/crypto/tls/HashAlgorithm.cs28
-rw-r--r--crypto/src/crypto/tls/TlsServerProtocol.cs17
-rw-r--r--crypto/src/crypto/tls/TlsUtilities.cs42
-rw-r--r--crypto/test/UnitTests.csproj4
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestCase.cs9
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs28
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs18
-rw-r--r--crypto/test/src/crypto/tls/test/DtlsTestSuite.cs122
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestCase.cs8
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs11
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs29
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestConfig.cs19
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs27
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs19
-rw-r--r--crypto/test/src/crypto/tls/test/TlsTestSuite.cs74
18 files changed, 426 insertions, 74 deletions
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index e6ab94ee2..e4bd2e036 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -11840,6 +11840,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestClientProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "test\src\crypto\tls\test\DtlsTestServerProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\DtlsTestSuite.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11950,6 +11960,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestClientProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsTestConfig.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
@@ -11960,6 +11975,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "test\src\crypto\tls\test\TlsTestServerProtocol.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "test\src\crypto\tls\test\TlsTestSuite.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/crypto/tls/DeferredHash.cs b/crypto/src/crypto/tls/DeferredHash.cs
index 1112d4a3c..f402f26d2 100644
--- a/crypto/src/crypto/tls/DeferredHash.cs
+++ b/crypto/src/crypto/tls/DeferredHash.cs
@@ -103,7 +103,7 @@ namespace Org.BouncyCastle.Crypto.Tls
         {
             IDigest d = (IDigest)mHashes[hashAlgorithm];
             if (d == null)
-                throw new InvalidOperationException("HashAlgorithm " + hashAlgorithm + " is not being tracked");
+                throw new InvalidOperationException("HashAlgorithm." + HashAlgorithm.GetText(hashAlgorithm) + " is not being tracked");
 
             d = TlsUtilities.CloneHash(hashAlgorithm, d);
             if (mBuf != null)
diff --git a/crypto/src/crypto/tls/DtlsServerProtocol.cs b/crypto/src/crypto/tls/DtlsServerProtocol.cs
index 9c7caf290..e2e9eddfc 100644
--- a/crypto/src/crypto/tls/DtlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/DtlsServerProtocol.cs
@@ -458,6 +458,9 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual void ProcessCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash)
         {
+            if (state.certificateRequest == null)
+                throw new InvalidOperationException();
+
             MemoryStream buf = new MemoryStream(body, false);
 
             TlsServerContextImpl context = state.serverContext;
@@ -466,13 +469,15 @@ namespace Org.BouncyCastle.Crypto.Tls
             TlsProtocol.AssertEmpty(buf);
 
             // Verify the CertificateVerify message contains a correct signature.
-            bool verified = false;
             try
             {
+                SignatureAndHashAlgorithm signatureAlgorithm = clientCertificateVerify.Algorithm;
+
                 byte[] hash;
                 if (TlsUtilities.IsTlsV12(context))
                 {
-                    hash = prepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash);
+                    TlsUtilities.VerifySupportedSignatureAlgorithm(state.certificateRequest.SupportedSignatureAlgorithms, signatureAlgorithm);
+                    hash = prepareFinishHash.GetFinalHash(signatureAlgorithm.Hash);
                 }
                 else
                 {
@@ -485,15 +490,17 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)state.clientCertificateType);
                 tlsSigner.Init(context);
-                verified = tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm,
-                    clientCertificateVerify.Signature, publicKey, hash);
+                if (!tlsSigner.VerifyRawSignature(signatureAlgorithm, clientCertificateVerify.Signature, publicKey, hash))
+                    throw new TlsFatalAlert(AlertDescription.decrypt_error);
             }
-            catch (Exception)
+            catch (TlsFatalAlert e)
             {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new TlsFatalAlert(AlertDescription.decrypt_error, e);
             }
-
-            if (!verified)
-                throw new TlsFatalAlert(AlertDescription.decrypt_error);
         }
 
         protected virtual void ProcessClientHello(ServerHandshakeState state, byte[] body)
diff --git a/crypto/src/crypto/tls/HashAlgorithm.cs b/crypto/src/crypto/tls/HashAlgorithm.cs
index ac6def26f..ff540d2e0 100644
--- a/crypto/src/crypto/tls/HashAlgorithm.cs
+++ b/crypto/src/crypto/tls/HashAlgorithm.cs
@@ -12,5 +12,33 @@ namespace Org.BouncyCastle.Crypto.Tls
         public const byte sha256 = 4;
         public const byte sha384 = 5;
         public const byte sha512 = 6;
+
+        public static string GetName(byte hashAlgorithm)
+        {
+            switch (hashAlgorithm)
+            {
+            case none:
+                return "none";
+            case md5:
+                return "md5";
+            case sha1:
+                return "sha1";
+            case sha224:
+                return "sha224";
+            case sha256:
+                return "sha256";
+            case sha384:
+                return "sha384";
+            case sha512:
+                return "sha512";
+            default:
+                return "UNKNOWN";
+            }
+        }
+
+        public static string GetText(byte hashAlgorithm)
+        {
+            return GetName(hashAlgorithm) + "(" + hashAlgorithm + ")";
+        }
     }
 }
diff --git a/crypto/src/crypto/tls/TlsServerProtocol.cs b/crypto/src/crypto/tls/TlsServerProtocol.cs
index 4ab628b13..5716c0cd1 100644
--- a/crypto/src/crypto/tls/TlsServerProtocol.cs
+++ b/crypto/src/crypto/tls/TlsServerProtocol.cs
@@ -460,6 +460,9 @@ namespace Org.BouncyCastle.Crypto.Tls
 
         protected virtual void ReceiveCertificateVerifyMessage(MemoryStream buf)
         {
+            if (mCertificateRequest == null)
+                throw new InvalidOperationException();
+
             DigitallySigned clientCertificateVerify = DigitallySigned.Parse(Context, buf);
 
             AssertEmpty(buf);
@@ -467,10 +470,13 @@ namespace Org.BouncyCastle.Crypto.Tls
             // Verify the CertificateVerify message contains a correct signature.
             try
             {
+                SignatureAndHashAlgorithm signatureAlgorithm = clientCertificateVerify.Algorithm;
+
                 byte[] hash;
                 if (TlsUtilities.IsTlsV12(Context))
                 {
-                    hash = mPrepareFinishHash.GetFinalHash(clientCertificateVerify.Algorithm.Hash);
+                    TlsUtilities.VerifySupportedSignatureAlgorithm(mCertificateRequest.SupportedSignatureAlgorithms, signatureAlgorithm);
+                    hash = mPrepareFinishHash.GetFinalHash(signatureAlgorithm.Hash);
                 }
                 else
                 {
@@ -483,11 +489,12 @@ namespace Org.BouncyCastle.Crypto.Tls
 
                 TlsSigner tlsSigner = TlsUtilities.CreateTlsSigner((byte)mClientCertificateType);
                 tlsSigner.Init(Context);
-                if (!tlsSigner.VerifyRawSignature(clientCertificateVerify.Algorithm,
-                    clientCertificateVerify.Signature, publicKey, hash))
-                {
+                if (!tlsSigner.VerifyRawSignature(signatureAlgorithm, clientCertificateVerify.Signature, publicKey, hash))
                     throw new TlsFatalAlert(AlertDescription.decrypt_error);
-                }
+            }
+            catch (TlsFatalAlert e)
+            {
+                throw e;
             }
             catch (Exception e)
             {
diff --git a/crypto/src/crypto/tls/TlsUtilities.cs b/crypto/src/crypto/tls/TlsUtilities.cs
index 26fb0d5e8..7d1d488d7 100644
--- a/crypto/src/crypto/tls/TlsUtilities.cs
+++ b/crypto/src/crypto/tls/TlsUtilities.cs
@@ -129,14 +129,24 @@ namespace Org.BouncyCastle.Crypto.Tls
             return context.ServerVersion.IsSsl;
         }
 
+        public static bool IsTlsV11(ProtocolVersion version)
+        {
+            return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(version.GetEquivalentTLSVersion());
+        }
+
         public static bool IsTlsV11(TlsContext context)
         {
-            return ProtocolVersion.TLSv11.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion());
+            return IsTlsV11(context.ServerVersion);
+        }
+
+        public static bool IsTlsV12(ProtocolVersion version)
+        {
+            return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(version.GetEquivalentTLSVersion());
         }
 
         public static bool IsTlsV12(TlsContext context)
         {
-            return ProtocolVersion.TLSv12.IsEqualOrEarlierVersionOf(context.ServerVersion.GetEquivalentTLSVersion());
+            return IsTlsV12(context.ServerVersion);
         }
 
         public static void WriteUint8(byte i, Stream output)
@@ -712,11 +722,10 @@ namespace Org.BouncyCastle.Crypto.Tls
         public static void EncodeSupportedSignatureAlgorithms(IList supportedSignatureAlgorithms, bool allowAnonymous,
             Stream output)
         {
-            if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.Count < 1
-                || supportedSignatureAlgorithms.Count >= (1 << 15))
-            {
+            if (supportedSignatureAlgorithms == null)
+                throw new ArgumentNullException("supportedSignatureAlgorithms");
+            if (supportedSignatureAlgorithms.Count < 1 || supportedSignatureAlgorithms.Count >= (1 << 15))
                 throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms");
-            }
 
             // supported_signature_algorithms
             int length = 2 * supportedSignatureAlgorithms.Count;
@@ -762,6 +771,27 @@ namespace Org.BouncyCastle.Crypto.Tls
             return supportedSignatureAlgorithms;
         }
 
+        public static void VerifySupportedSignatureAlgorithm(IList supportedSignatureAlgorithms, SignatureAndHashAlgorithm signatureAlgorithm)
+        {
+            if (supportedSignatureAlgorithms == null)
+                throw new ArgumentNullException("supportedSignatureAlgorithms");
+            if (supportedSignatureAlgorithms.Count < 1 || supportedSignatureAlgorithms.Count >= (1 << 15))
+                throw new ArgumentException("must have length from 1 to (2^15 - 1)", "supportedSignatureAlgorithms");
+            if (signatureAlgorithm == null)
+                throw new ArgumentNullException("signatureAlgorithm");
+
+            if (signatureAlgorithm.Signature != SignatureAlgorithm.anonymous)
+            {
+                foreach (SignatureAndHashAlgorithm entry in supportedSignatureAlgorithms)
+                {
+                    if (entry.Hash == signatureAlgorithm.Hash && entry.Signature == signatureAlgorithm.Signature)
+                        return;
+                }
+            }
+
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
         public static byte[] PRF(TlsContext context, byte[] secret, string asciiLabel, byte[] seed, int size)
         {
             ProtocolVersion version = context.ServerVersion;
diff --git a/crypto/test/UnitTests.csproj b/crypto/test/UnitTests.csproj
index 71896d203..24a60346e 100644
--- a/crypto/test/UnitTests.csproj
+++ b/crypto/test/UnitTests.csproj
@@ -277,6 +277,8 @@
     <Compile Include="src\crypto\tls\test\ByteQueueStreamTest.cs" />
     <Compile Include="src\crypto\tls\test\DtlsProtocolTest.cs" />
     <Compile Include="src\crypto\tls\test\DtlsTestCase.cs" />
+    <Compile Include="src\crypto\tls\test\DtlsTestClientProtocol.cs" />
+    <Compile Include="src\crypto\tls\test\DtlsTestServerProtocol.cs" />
     <Compile Include="src\crypto\tls\test\DtlsTestSuite.cs" />
     <Compile Include="src\crypto\tls\test\LoggingDatagramTransport.cs" />
     <Compile Include="src\crypto\tls\test\MockDatagramAssociation.cs" />
@@ -299,8 +301,10 @@
     <Compile Include="src\crypto\tls\test\TlsSrpProtocolTest.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestCase.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestClientImpl.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestClientProtocol.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestConfig.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestServerImpl.cs" />
+    <Compile Include="src\crypto\tls\test\TlsTestServerProtocol.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestSuite.cs" />
     <Compile Include="src\crypto\tls\test\TlsTestUtilities.cs" />
     <Compile Include="src\crypto\tls\test\UnreliableDatagramTransport.cs" />
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
index d4af04fac..5e43337f4 100644
--- a/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
+++ b/crypto/test/src/crypto/tls/test/DtlsTestCase.cs
@@ -28,8 +28,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
 
             SecureRandom secureRandom = new SecureRandom();
 
-            DtlsClientProtocol clientProtocol = new DtlsClientProtocol(secureRandom);
-            DtlsServerProtocol serverProtocol = new DtlsServerProtocol(secureRandom);
+            DtlsTestClientProtocol clientProtocol = new DtlsTestClientProtocol(secureRandom, config);
+            DtlsTestServerProtocol serverProtocol = new DtlsTestServerProtocol(secureRandom, config);
 
             MockDatagramAssociation network = new MockDatagramAssociation(1500);
 
@@ -101,14 +101,15 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         internal class Server
         {
             private readonly DtlsTestCase mOuter;
-            private readonly DtlsServerProtocol mServerProtocol;
+            private readonly DtlsTestServerProtocol mServerProtocol;
             private readonly DatagramTransport mServerTransport;
             private readonly TlsTestServerImpl mServerImpl;
 
             private volatile bool isShutdown = false;
             internal Exception mCaught = null;
 
-            internal Server(DtlsTestCase outer, DtlsServerProtocol serverProtocol, DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
+            internal Server(DtlsTestCase outer, DtlsTestServerProtocol serverProtocol,
+                DatagramTransport serverTransport, TlsTestServerImpl serverImpl)
             {
                 this.mOuter = outer;
                 this.mServerProtocol = serverProtocol;
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs b/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs
new file mode 100644
index 000000000..41ed93eb0
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestClientProtocol.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class DtlsTestClientProtocol
+        :   DtlsClientProtocol
+    {
+        protected readonly TlsTestConfig config;
+
+        public DtlsTestClientProtocol(SecureRandom secureRandom, TlsTestConfig config)
+            : base(secureRandom)
+        {
+            this.config = config;
+        }
+
+        protected override byte[] GenerateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify)
+        {
+            if (certificateVerify.Algorithm != null && config.clientAuthSigAlgClaimed != null)
+            {
+                certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+            }
+
+            return base.GenerateCertificateVerify(state, certificateVerify);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs b/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs
new file mode 100644
index 000000000..006473cef
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/DtlsTestServerProtocol.cs
@@ -0,0 +1,18 @@
+using System;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class DtlsTestServerProtocol
+        :   DtlsServerProtocol
+    {
+        protected readonly TlsTestConfig config;
+
+        public DtlsTestServerProtocol(SecureRandom secureRandom, TlsTestConfig config)
+            : base(secureRandom)
+        {
+            this.config = config;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
index eb9d42e5f..69ce36071 100644
--- a/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
+++ b/crypto/test/src/crypto/tls/test/DtlsTestSuite.cs
@@ -31,7 +31,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
                 c.clientFallback = true;
 
-                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+                AddTestCase(testSuite, c, "FallbackGood");
             }
 
             /*
@@ -40,20 +40,22 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
              * alerts being raised
              */
 
-            //{
-            //    TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
-            //    c.clientOfferVersion = ProtocolVersion.DTLSv10;
-            //    c.clientFallback = true;
-            //    c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
+#if false
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
+                c.clientOfferVersion = ProtocolVersion.DTLSv10;
+                c.clientFallback = true;
+                c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
 
-            //    testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
-            //}
+                AddTestCase(testSuite, c, "FallbackBad");
+            }
+#endif
 
             {
                 TlsTestConfig c = CreateDtlsTestConfig(ProtocolVersion.DTLSv12);
                 c.clientOfferVersion = ProtocolVersion.DTLSv10;
 
-                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+                AddTestCase(testSuite, c, "FallbackNone");
             }
         }
 
@@ -71,52 +73,110 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
              * alerts being raised
              */
 
-            //{
-            //    TlsTestConfig c = CreateDtlsTestConfig(version);
-            //    c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
-            //    c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+#if false
+            /*
+             * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+             * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+                c.ExpectClientFatalAlert(AlertDescription.internal_error);
 
-            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
-            //}
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+            }
 
-            //{
-            //    TlsTestConfig c = CreateDtlsTestConfig(version);
-            //    c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
-            //    c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+            /*
+             * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+             * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+             * when it verifies the selected algorithm against the CertificateRequest supported
+             * algorithms.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+                c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
 
-            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
-            //}
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+            }
 
-            //{
-            //    TlsTestConfig c = CreateDtlsTestConfig(version);
-            //    c.clientAuth = C.CLIENT_AUTH_NONE;
-            //    c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
-            //    c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+            /*
+             * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+             * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+             * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+             * we expect fatal alert to come from the server when it finds the claimed algorithm
+             * doesn't match the client certificate.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+                c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+                c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
 
-            //    testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
-            //}
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
+            }
 
             {
                 TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+                c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+                c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
+
+                AddTestCase(testSuite, c, prefix + "BadClientCertificate");
+            }
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_NONE;
+                c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+                c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
+
+                AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
+            }
+#endif
+
+            {
+                TlsTestConfig c = CreateDtlsTestConfig(version);
+
+                AddTestCase(testSuite, c, prefix + "GoodDefault");
             }
 
             {
                 TlsTestConfig c = CreateDtlsTestConfig(version);
                 c.serverCertReq = C.SERVER_CERT_REQ_NONE;
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+                AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
             }
 
             {
                 TlsTestConfig c = CreateDtlsTestConfig(version);
                 c.clientAuth = C.CLIENT_AUTH_NONE;
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+                AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
             }
         }
 
+        private static void AddTestCase(IList testSuite, TlsTestConfig config, String name)
+        {
+            testSuite.Add(new TestCaseData(config).SetName(name));
+        }
+
         private static TlsTestConfig CreateDtlsTestConfig(ProtocolVersion version)
         {
             TlsTestConfig c = new TlsTestConfig();
diff --git a/crypto/test/src/crypto/tls/test/TlsTestCase.cs b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
index 4b0c12710..7fb5db6ce 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestCase.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestCase.cs
@@ -35,8 +35,8 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             NetworkStream clientNet = new NetworkStream(clientPipe);
             NetworkStream serverNet = new NetworkStream(serverPipe);
 
-            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNet, secureRandom);
-            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNet, secureRandom);
+            TlsTestClientProtocol clientProtocol = new TlsTestClientProtocol(clientNet, secureRandom, config);
+            TlsTestServerProtocol serverProtocol = new TlsTestServerProtocol(serverNet, secureRandom, config);
 
             TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
             TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
@@ -104,13 +104,13 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         internal class Server
         {
             protected readonly TlsTestCase mOuter;
-            protected readonly TlsServerProtocol mServerProtocol;
+            protected readonly TlsTestServerProtocol mServerProtocol;
             protected readonly TlsTestServerImpl mServerImpl;
 
             internal bool mCanExit = false;
             internal Exception mCaught = null;
 
-            internal Server(TlsTestCase outer, TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
+            internal Server(TlsTestCase outer, TlsTestServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
             {
                 this.mOuter = outer;
                 this.mServerProtocol = serverProtocol;
diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
index 0cc1883ba..f313f75fe 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestClientImpl.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 using System.IO;
 
 using Org.BouncyCastle.Asn1;
@@ -201,9 +202,15 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                     return null;
                 }
 
+                IList supportedSigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+                if (supportedSigAlgs != null && mOuter.mConfig.clientAuthSigAlg != null)
+                {
+                    supportedSigAlgs = new ArrayList(1);
+                    supportedSigAlgs.Add(mOuter.mConfig.clientAuthSigAlg);
+                }
+
                 TlsSignerCredentials signerCredentials = TlsTestUtilities.LoadSignerCredentials(mContext,
-                    certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
-                    "x509-client.pem", "x509-client-key.pem");
+                    supportedSigAlgs, SignatureAlgorithm.rsa, "x509-client.pem", "x509-client-key.pem");
 
                 if (mOuter.mConfig.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
                 {
diff --git a/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs b/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs
new file mode 100644
index 000000000..97b7c91bc
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestClientProtocol.cs
@@ -0,0 +1,29 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestClientProtocol
+        :   TlsClientProtocol
+    {
+        protected readonly TlsTestConfig config;
+
+        public TlsTestClientProtocol(Stream stream, SecureRandom secureRandom, TlsTestConfig config)
+            : base(stream, secureRandom)
+        {
+            this.config = config;
+        }
+
+        protected override void SendCertificateVerifyMessage(DigitallySigned certificateVerify)
+        {
+            if (certificateVerify.Algorithm != null && config.clientAuthSigAlgClaimed != null)
+            {
+                certificateVerify = new DigitallySigned(config.clientAuthSigAlgClaimed, certificateVerify.Signature);
+            }
+
+            base.SendCertificateVerifyMessage(certificateVerify);
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
index 0d1e7badb..212d2b63c 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestConfig.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Collections;
 
 namespace Org.BouncyCastle.Crypto.Tls.Tests
 {
@@ -47,6 +48,18 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         public int clientAuth = CLIENT_AUTH_VALID;
 
         /**
+         * If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/hash algorithm to
+         * be used for the CertificateVerify signature (if one is sent).
+         */
+        public SignatureAndHashAlgorithm clientAuthSigAlg = null;
+
+        /**
+         * If not null, and TLS 1.2 or higher is negotiated, selects a fixed signature/hash algorithm to
+         * be _claimed_ in the CertificateVerify (if one is sent), independently of what was actually used.
+         */
+        public SignatureAndHashAlgorithm clientAuthSigAlgClaimed = null;
+
+        /**
          * Configures the minimum protocol version the client will accept. If null, uses the library's default.
          */
         public ProtocolVersion clientMinimumVersion = null;
@@ -67,6 +80,12 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
         public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
 
         /**
+         * If TLS 1.2 or higher is negotiated, configures the set of supported signature algorithms in the
+         * CertificateRequest (if one is sent). If null, uses a default set.
+         */
+        public IList serverCertReqSigAlgs = null;
+
+        /**
          * Configures the maximum protocol version the server will accept. If null, uses the library's default.
          */
         public ProtocolVersion serverMaximumVersion = null;
diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
index 152d5dbdc..6f9d35b5c 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestServerImpl.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections;
 using System.IO;
+using System.Threading;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
@@ -72,11 +73,11 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                     + ", " + AlertDescription.GetText(alertDescription));
                 if (message != null)
                 {
-                    output.WriteLine("> " + message);
+                    SafeWriteLine(output, "> " + message);
                 }
                 if (cause != null)
                 {
-                    output.WriteLine(cause);
+                    SafeWriteLine(output, cause);
                 }
             }
         }
@@ -92,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             if (TlsTestConfig.DEBUG)
             {
                 TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
-                output.WriteLine("TLS server received alert: " + AlertLevel.GetText(alertLevel)
+                SafeWriteLine(output, "TLS server received alert: " + AlertLevel.GetText(alertLevel)
                     + ", " + AlertDescription.GetText(alertDescription));
             }
         }
@@ -122,7 +123,11 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             IList serverSigAlgs = null;
             if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(mServerVersion))
             {
-                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+                serverSigAlgs = mConfig.serverCertReqSigAlgs;
+                if (serverSigAlgs == null)
+                {
+                    serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms();
+                }
             }
 
             IList certificateAuthorities = new ArrayList();
@@ -190,5 +195,19 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             return TlsTestUtilities.LoadSignerCredentials(mContext, mSupportedSignatureAlgorithms, SignatureAlgorithm.rsa,
                 "x509-server.pem", "x509-server-key.pem");
         }
+
+        private static void SafeWriteLine(TextWriter output, object line)
+        {
+            try
+            {
+                output.WriteLine(line);
+            }
+            catch (ThreadInterruptedException)
+            {
+                /*
+                 * For some reason the NUnit plugin in Visual Studio started throwing these during alert logging
+                 */
+            }
+        }
     }
 }
diff --git a/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs b/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs
new file mode 100644
index 000000000..845b7f0b9
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestServerProtocol.cs
@@ -0,0 +1,19 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Tests
+{
+    internal class TlsTestServerProtocol
+        :   TlsServerProtocol
+    {
+        protected readonly TlsTestConfig config;
+
+        public TlsTestServerProtocol(Stream stream, SecureRandom secureRandom, TlsTestConfig config)
+            : base(stream, secureRandom)
+        {
+            this.config = config;
+        }
+    }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
index dfd09d06e..0770187d5 100644
--- a/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
+++ b/crypto/test/src/crypto/tls/test/TlsTestSuite.cs
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
                 c.clientFallback = true;
 
-                testSuite.Add(new TestCaseData(c).SetName("FallbackGood"));
+                AddTestCase(testSuite, c, "FallbackGood");
             }
 
             {
@@ -41,14 +41,14 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 c.clientFallback = true;
                 c.ExpectServerFatalAlert(AlertDescription.inappropriate_fallback);
 
-                testSuite.Add(new TestCaseData(c).SetName("FallbackBad"));
+                AddTestCase(testSuite, c, "FallbackBad");
             }
 
             {
                 TlsTestConfig c = CreateTlsTestConfig(ProtocolVersion.TLSv12);
                 c.clientOfferVersion = ProtocolVersion.TLSv11;
 
-                testSuite.Add(new TestCaseData(c).SetName("FallbackNone"));
+                AddTestCase(testSuite, c, "FallbackNone");
             }
         }
 
@@ -63,7 +63,58 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
             {
                 TlsTestConfig c = CreateTlsTestConfig(version);
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodDefault"));
+                AddTestCase(testSuite, c, prefix + "GoodDefault");
+            }
+
+            /*
+             * Server only declares support for SHA1/RSA, client selects MD5/RSA. Since the client is
+             * NOT actually tracking MD5 over the handshake, we expect fatal alert from the client.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.md5, SignatureAlgorithm.rsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultRsaSignatureAlgorithms();
+                c.ExpectClientFatalAlert(AlertDescription.internal_error);
+
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifyHashAlg");
+            }
+
+            /*
+             * Server only declares support for SHA1/ECDSA, client selects SHA1/RSA. Since the client is
+             * actually tracking SHA1 over the handshake, we expect fatal alert to come from the server
+             * when it verifies the selected algorithm against the CertificateRequest supported
+             * algorithms.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+                c.ExpectServerFatalAlert(AlertDescription.illegal_parameter);
+
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlg");
+            }
+
+            /*
+             * Server only declares support for SHA1/ECDSA, client signs with SHA1/RSA, but sends
+             * SHA1/ECDSA in the CertificateVerify. Since the client is actually tracking SHA1 over the
+             * handshake, and the claimed algorithm is in the CertificateRequest supported algorithms,
+             * we expect fatal alert to come from the server when it finds the claimed algorithm
+             * doesn't match the client certificate.
+             */
+            if (TlsUtilities.IsTlsV12(version))
+            {
+                TlsTestConfig c = CreateTlsTestConfig(version);
+                c.clientAuth = C.CLIENT_AUTH_VALID;
+                c.clientAuthSigAlg = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa);
+                c.clientAuthSigAlgClaimed = new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.ecdsa);
+                c.serverCertReqSigAlgs = TlsUtilities.GetDefaultECDsaSignatureAlgorithms();
+                c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
+
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySigAlgMismatch");
             }
 
             {
@@ -71,7 +122,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
                 c.ExpectServerFatalAlert(AlertDescription.decrypt_error);
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadCertificateVerify"));
+                AddTestCase(testSuite, c, prefix + "BadCertificateVerifySignature");
             }
 
             {
@@ -79,7 +130,7 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
                 c.ExpectServerFatalAlert(AlertDescription.bad_certificate);
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadClientCertificate"));
+                AddTestCase(testSuite, c, prefix + "BadClientCertificate");
             }
 
             {
@@ -88,24 +139,29 @@ namespace Org.BouncyCastle.Crypto.Tls.Tests
                 c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
                 c.ExpectServerFatalAlert(AlertDescription.handshake_failure);
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "BadMandatoryCertReqDeclined"));
+                AddTestCase(testSuite, c, prefix + "BadMandatoryCertReqDeclined");
             }
 
             {
                 TlsTestConfig c = CreateTlsTestConfig(version);
                 c.serverCertReq = C.SERVER_CERT_REQ_NONE;
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodNoCertReq"));
+                AddTestCase(testSuite, c, prefix + "GoodNoCertReq");
             }
 
             {
                 TlsTestConfig c = CreateTlsTestConfig(version);
                 c.clientAuth = C.CLIENT_AUTH_NONE;
 
-                testSuite.Add(new TestCaseData(c).SetName(prefix + "GoodOptionalCertReqDeclined"));
+                AddTestCase(testSuite, c, prefix + "GoodOptionalCertReqDeclined");
             }
         }
 
+        private static void AddTestCase(IList testSuite, TlsTestConfig config, string name)
+        {
+            testSuite.Add(new TestCaseData(config).SetName(name));
+        }
+
         private static TlsTestConfig CreateTlsTestConfig(ProtocolVersion version)
         {
             TlsTestConfig c = new TlsTestConfig();