summary refs log tree commit diff
path: root/crypto/test/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-09-24 18:09:39 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-09-24 18:09:39 +0700
commited081e3fe9634391ac496bd79193a7d00dfa6f07 (patch)
treeecc1382c5f3f9a17c6b21ebf8026076f6a32f15f /crypto/test/src
parentCmp updates (diff)
downloadBouncyCastle.NET-ed25519-ed081e3fe9634391ac496bd79193a7d00dfa6f07.tar.xz
(D)TLS: RFC 7250 Raw Public Keys
Diffstat (limited to 'crypto/test/src')
-rw-r--r--crypto/test/src/tls/test/MockRawKeysTlsClient.cs126
-rw-r--r--crypto/test/src/tls/test/MockRawKeysTlsServer.cs134
-rw-r--r--crypto/test/src/tls/test/TlsClientRawKeysTest.cs99
-rw-r--r--crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs282
-rw-r--r--crypto/test/src/tls/test/TlsServerRawKeysTest.cs90
5 files changed, 731 insertions, 0 deletions
diff --git a/crypto/test/src/tls/test/MockRawKeysTlsClient.cs b/crypto/test/src/tls/test/MockRawKeysTlsClient.cs
new file mode 100644
index 000000000..594c4c94e
--- /dev/null
+++ b/crypto/test/src/tls/test/MockRawKeysTlsClient.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    internal class MockRawKeysTlsClient
+        : DefaultTlsClient
+    {
+        private short m_serverCertType;
+        private short m_clientCertType;
+        private short[] m_offerServerCertTypes;
+        private short[] m_offerClientCertTypes;
+        private ProtocolVersion m_tlsVersion;
+        private Ed25519PrivateKeyParameters m_privateKey;
+
+        internal MockRawKeysTlsClient(short serverCertType, short clientCertType, short[] offerServerCertTypes,
+            short[] offerClientCertTypes, Ed25519PrivateKeyParameters privateKey, ProtocolVersion tlsVersion)
+            : base(new BcTlsCrypto(new SecureRandom()))
+        {
+            m_serverCertType = serverCertType;
+            m_clientCertType = clientCertType;
+            m_offerServerCertTypes = offerServerCertTypes;
+            m_offerClientCertTypes = offerClientCertTypes;
+            m_privateKey = privateKey;
+            m_tlsVersion = tlsVersion;
+        }
+
+        protected override ProtocolVersion[] GetSupportedVersions()
+        {
+            return new ProtocolVersion[]{ m_tlsVersion };
+        }
+
+        protected override int[] GetSupportedCipherSuites()
+        {
+            return TlsUtilities.IsTlsV13(m_tlsVersion)
+                ?   new int[]{ CipherSuite.TLS_AES_128_GCM_SHA256 }
+                :   new int[]{ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 };
+        }
+
+        protected override short[] GetAllowedClientCertificateTypes() => m_offerClientCertTypes;
+
+        protected override short[] GetAllowedServerCertificateTypes() => m_offerServerCertTypes;
+
+        protected override CertificateStatusRequest GetCertificateStatusRequest()
+        {
+            return m_serverCertType == CertificateType.RawPublicKey ? null : base.GetCertificateStatusRequest();
+        }
+
+        protected override IList<CertificateStatusRequestItemV2> GetMultiCertStatusRequest()
+        {
+            return m_serverCertType == CertificateType.RawPublicKey ? null : base.GetMultiCertStatusRequest();
+        }
+
+        public override TlsAuthentication GetAuthentication()
+        {
+            return new MyTlsAuthentication(this);
+        }
+
+        internal class MyTlsAuthentication
+            : TlsAuthentication
+        {
+            private readonly MockRawKeysTlsClient m_outer;
+            private TlsCredentialedSigner m_credentials;
+
+            internal MyTlsAuthentication(MockRawKeysTlsClient outer)
+            {
+                m_outer = outer;
+            }
+
+            public void NotifyServerCertificate(TlsServerCertificate serverCertificate)
+            {
+                Assert.AreEqual(m_outer.m_serverCertType, serverCertificate.Certificate.CertificateType,
+                    "wrong certificate type from server");
+            }
+
+            public TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+            {
+                var clientCertType = m_outer.m_clientCertType;
+                var context = m_outer.m_context;
+                var crypto = (BcTlsCrypto)m_outer.Crypto;
+                var privateKey = m_outer.m_privateKey;
+
+                if (clientCertType < 0)
+                {
+                    Assert.Fail("should not have received a certificate request");
+                }
+
+                Assert.AreEqual(clientCertType, context.SecurityParameters.ClientCertificateType,
+                    "wrong certificate type in request");
+
+                if (m_credentials == null)
+                {
+                    switch (clientCertType)
+                    {
+                    case CertificateType.X509:
+                        m_credentials = TlsTestUtilities.LoadSignerCredentials(context,
+                            certificateRequest.SupportedSignatureAlgorithms, SignatureAlgorithm.ed25519,
+                            "x509-client-ed25519.pem", "x509-client-key-ed25519.pem");
+                        break;
+                    case CertificateType.RawPublicKey:
+                        TlsCertificate rawKeyCert = new BcTlsRawKeyCertificate(crypto,
+                            SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(privateKey.GeneratePublicKey()));
+                        Certificate cert = new Certificate(CertificateType.RawPublicKey,
+                            TlsUtilities.IsTlsV13(context) ? TlsUtilities.EmptyBytes : null,
+                            new CertificateEntry[]{ new CertificateEntry(rawKeyCert, null) });
+                        m_credentials = new BcDefaultTlsCredentialedSigner(new TlsCryptoParameters(context),
+                            crypto, privateKey, cert, SignatureAndHashAlgorithm.ed25519);
+                        break;
+                    default:
+                        throw new ArgumentException("Only supports X509 and raw keys");
+                    }
+                }
+
+                return m_credentials;
+            }
+        };
+    }
+}
diff --git a/crypto/test/src/tls/test/MockRawKeysTlsServer.cs b/crypto/test/src/tls/test/MockRawKeysTlsServer.cs
new file mode 100644
index 000000000..e136c6571
--- /dev/null
+++ b/crypto/test/src/tls/test/MockRawKeysTlsServer.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Collections.Generic;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Tls.Crypto;
+using Org.BouncyCastle.Tls.Crypto.Impl.BC;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    internal class MockRawKeysTlsServer
+        : DefaultTlsServer
+    {
+        private short m_serverCertType;
+        private short m_clientCertType;
+        private short[] m_allowedClientCertTypes;
+        private Ed25519PrivateKeyParameters m_privateKey;
+        private ProtocolVersion m_tlsVersion;
+        private TlsCredentialedSigner m_credentials;
+
+        internal IDictionary<int, byte[]> m_receivedClientExtensions;
+
+        internal MockRawKeysTlsServer(short serverCertType, short clientCertType, short[] allowedClientCertTypes,
+            Ed25519PrivateKeyParameters privateKey, ProtocolVersion tlsVersion)
+            : base(new BcTlsCrypto(new SecureRandom()))
+        {
+            m_serverCertType = serverCertType;
+            m_clientCertType = clientCertType;
+            m_allowedClientCertTypes = allowedClientCertTypes;
+            m_privateKey = privateKey;
+            m_tlsVersion = tlsVersion;
+        }
+
+        public override TlsCredentials GetCredentials()
+        {
+            /*
+             * TODO[tls13] Should really be finding the first client-supported signature scheme that the
+             * server also supports and has credentials for.
+             */
+            if (TlsUtilities.IsTlsV13(m_context))
+                return GetECDsaSignerCredentials();
+
+            return base.GetCredentials();
+        }
+
+        protected override ProtocolVersion[] GetSupportedVersions()
+        {
+            return new ProtocolVersion[]{ m_tlsVersion };
+        }
+
+        protected override int[] GetSupportedCipherSuites()
+        {
+            return TlsUtilities.IsTlsV13(m_tlsVersion)
+                ?   new int[]{ CipherSuite.TLS_AES_128_GCM_SHA256 }
+                :   new int[]{ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 };
+        }
+
+        public override void ProcessClientExtensions(IDictionary<int, byte[]> clientExtensions)
+        {
+            m_receivedClientExtensions = clientExtensions;
+
+            base.ProcessClientExtensions(clientExtensions);
+        }
+
+        protected override TlsCredentialedSigner GetECDsaSignerCredentials()
+        {
+            if (m_credentials == null)
+            {
+                var crypto = (BcTlsCrypto)Crypto;
+
+                switch (m_serverCertType)
+                {
+                case CertificateType.X509:
+                    m_credentials = TlsTestUtilities.LoadSignerCredentials(m_context,
+                        m_context.SecurityParameters.ClientSigAlgs, SignatureAlgorithm.ed25519,
+                        "x509-client-ed25519.pem", "x509-client-key-ed25519.pem");
+                    break;
+                case CertificateType.RawPublicKey:
+                    TlsCertificate rawKeyCert = new BcTlsRawKeyCertificate(crypto,
+                        SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(m_privateKey.GeneratePublicKey()));
+                    Certificate cert = new Certificate(CertificateType.RawPublicKey,
+                        TlsUtilities.IsTlsV13(m_context) ? TlsUtilities.EmptyBytes : null,
+                        new CertificateEntry[]{ new CertificateEntry(rawKeyCert, null) });
+                    m_credentials = new BcDefaultTlsCredentialedSigner(new TlsCryptoParameters(m_context),
+                        crypto, m_privateKey, cert, SignatureAndHashAlgorithm.ed25519);
+                    break;
+                default:
+                    throw new ArgumentException("Only supports X509 and raw keys");
+                }
+            }
+
+            return m_credentials;
+        }
+
+        protected override short[] GetAllowedClientCertificateTypes() => m_allowedClientCertTypes;
+
+        protected override bool AllowCertificateStatus()
+        {
+            return m_serverCertType == CertificateType.RawPublicKey ? false : base.AllowCertificateStatus();
+        }
+
+        protected override bool AllowMultiCertStatus()
+        {
+            return m_serverCertType == CertificateType.RawPublicKey ? false : base.AllowMultiCertStatus();
+        }
+
+        public override CertificateRequest GetCertificateRequest()
+        {
+            if (m_clientCertType < 0)
+                return null;
+
+            short[] certificateTypes = new short[]{ ClientCertificateType.ecdsa_sign };
+
+            IList<SignatureAndHashAlgorithm> serverSigAlgs = null;
+            if (TlsUtilities.IsSignatureAlgorithmsExtensionAllowed(m_context.ServerVersion))
+            {
+                serverSigAlgs = TlsUtilities.GetDefaultSupportedSignatureAlgorithms(m_context);
+            }
+
+            return TlsUtilities.IsTlsV13(m_tlsVersion)
+                ?   new CertificateRequest(TlsUtilities.EmptyBytes, serverSigAlgs, null, null)
+                :   new CertificateRequest(certificateTypes, serverSigAlgs, null);
+        }
+
+        public override void NotifyClientCertificate(Certificate clientCertificate)
+        {
+            Assert.AreEqual(m_clientCertType, clientCertificate.CertificateType,
+                "client certificate is the wrong type");
+        }
+    }
+}
diff --git a/crypto/test/src/tls/test/TlsClientRawKeysTest.cs b/crypto/test/src/tls/test/TlsClientRawKeysTest.cs
new file mode 100644
index 000000000..510213fc7
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsClientRawKeysTest.cs
@@ -0,0 +1,99 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    /// <summary>A simple test designed to conduct a TLS handshake with an external TLS server.</summary>
+    /// <remarks>
+    /// <code>
+    /// openssl genpkey -out ed25519.priv -algorithm ed25519
+    /// openssl pkey -in ed25519.priv -pubout -out ed25519.pub
+    /// 
+    /// gnutls-serv --http --debug 10 --priority NORMAL:+CTYPE-CLI-RAWPK:+CTYPE-SRV-RAWPK --rawpkkeyfile ed25519.priv --rawpkfile ed25519.pub
+    /// </code>
+    /// </remarks>
+    [TestFixture]
+    public class TlsClientRawKeysTest
+    {
+        [Test, Explicit]
+        public void TestConnection()
+        {
+            string host = "localhost";
+            int port = 5556;
+
+            RunTest(host, port, ProtocolVersion.TLSv12);
+            RunTest(host, port, ProtocolVersion.TLSv13);
+        }
+
+        private static void RunTest(string host, int port, ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey },
+                new short[]{ CertificateType.RawPublicKey }, new Ed25519PrivateKeyParameters(new SecureRandom()),
+                tlsVersion);
+            TlsClientProtocol protocol = OpenTlsClientConnection(host, port, client);
+
+            Http11Get(host, port, protocol.Stream);
+
+            protocol.Close();
+        }
+
+        private static void Http11Get(string host, int port, Stream s)
+        {
+            WriteUtf8Line(s, "GET / HTTP/1.1");
+            //WriteUtf8Line(s, "Host: " + host + ":" + port);
+            WriteUtf8Line(s, "");
+            s.Flush();
+
+            Console.WriteLine("---");
+
+            string[] ends = new string[] { "</HTML>", "HTTP/1.1 3", "HTTP/1.1 4" };
+
+            StreamReader reader = new StreamReader(s);
+
+            bool finished = false;
+            string line;
+            while (!finished && (line = reader.ReadLine()) != null)
+            {
+                Console.WriteLine("<<< " + line);
+
+                string upperLine = TlsTestUtilities.ToUpperInvariant(line);
+
+                // TEST CODE ONLY. This is not a robust way of parsing the result!
+                foreach (string end in ends)
+                {
+                    if (upperLine.IndexOf(end) >= 0)
+                    {
+                        finished = true;
+                        break;
+                    }
+                }
+            }
+
+            Console.Out.Flush();
+        }
+
+        private static TlsClientProtocol OpenTlsClientConnection(string hostname, int port, TlsClient client)
+        {
+            TcpClient tcp = new TcpClient(hostname, port);
+
+            TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream());
+            protocol.Connect(client);
+            return protocol;
+        }
+
+        private static void WriteUtf8Line(Stream output, string line)
+        {
+            byte[] buf = Encoding.UTF8.GetBytes(line + "\r\n");
+            output.Write(buf, 0, buf.Length);
+            Console.WriteLine(">>> " + line);
+        }
+    }
+}
diff --git a/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs b/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs
new file mode 100644
index 000000000..976df4009
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsRawKeysProtocolTest.cs
@@ -0,0 +1,282 @@
+using System;
+using System.IO;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    [TestFixture]
+    public class TlsRawKeysProtocolTest
+    {
+        private readonly SecureRandom Random = new SecureRandom();
+
+        [Test]
+        public void TestClientSendsExtensionButServerDoesNotSupportIt()
+        {
+            TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestClientSendsExtensionButServerDoesNotSupportIt_13()
+        {
+            TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion.TLSv13);
+        }
+
+        private void TestClientSendsExtensionButServerDoesNotSupportIt(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, -1,
+                new short[]{ CertificateType.RawPublicKey, CertificateType.X509 }, null, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, -1, null, GenerateKeyPair(),
+                tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509()
+        {
+            TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509_13()
+        {
+            TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion.TLSv13);
+        }
+
+        private void TestExtensionsAreOmittedIfSpecifiedButOnlyContainX509(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, CertificateType.X509,
+                new short[]{ CertificateType.X509 }, new short[]{ CertificateType.X509 }, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.X509,
+                new short[]{ CertificateType.X509 }, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+
+            Assert.IsFalse(server.m_receivedClientExtensions.ContainsKey(ExtensionType.client_certificate_type),
+                "client cert type extension should not be sent");
+            Assert.IsFalse(server.m_receivedClientExtensions.ContainsKey(ExtensionType.server_certificate_type),
+                "server cert type extension should not be sent");
+        }
+
+        [Test]
+        public void TestBothSidesUseRawKey()
+        {
+            TestBothSidesUseRawKey(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestBothSidesUseRawKey_13()
+        {
+            TestBothSidesUseRawKey(ProtocolVersion.TLSv13);
+        }
+
+        private void TestBothSidesUseRawKey(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey },
+                new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey,
+                CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientIsAnonymous()
+        {
+            TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientIsAnonymous_13()
+        {
+            TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion.TLSv13);
+        }
+
+        private void TestServerUsesRawKeyAndClientIsAnonymous(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey, -1,
+                new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey, -1, null,
+                GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientUsesX509()
+        {
+            TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestServerUsesRawKeyAndClientUsesX509_13()
+        {
+            TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion.TLSv13);
+        }
+
+        private void TestServerUsesRawKeyAndClientUsesX509(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                CertificateType.X509, new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(),
+                tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey,
+                CertificateType.X509, null, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestServerUsesX509AndClientUsesRawKey()
+        {
+            TestServerUsesX509AndClientUsesRawKey(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestServerUsesX509AndClientUsesRawKey_13()
+        {
+            TestServerUsesX509AndClientUsesRawKey(ProtocolVersion.TLSv13);
+        }
+
+        private void TestServerUsesX509AndClientUsesRawKey(ProtocolVersion tlsVersion)
+        {
+            MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509, CertificateType.RawPublicKey,
+                null, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.RawPublicKey,
+                new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(), tlsVersion);
+            PumpData(client, server);
+        }
+
+        [Test]
+        public void TestClientSendsClientCertExtensionButServerHasNoCommonTypes()
+        {
+            TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestClientSendsClientCertExtensionButServerHasNoCommonTypes_13()
+        {
+            TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion.TLSv13);
+        }
+
+        private void TestClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion tlsVersion)
+        {
+            try
+            {
+                MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.X509,
+                    CertificateType.RawPublicKey, null, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                    tlsVersion);
+                MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509, CertificateType.X509,
+                    new short[]{ CertificateType.X509 }, GenerateKeyPair(), tlsVersion);
+                PumpData(client, server);
+                Assert.Fail("Should have caused unsupported_certificate alert");
+            }
+            catch (TlsFatalAlertReceived alert)
+            {
+                Assert.AreEqual(AlertDescription.unsupported_certificate, alert.AlertDescription,
+                    "Should have caused unsupported_certificate alert");
+            }
+        }
+
+        [Test]
+        public void TestClientSendsServerCertExtensionButServerHasNoCommonTypes()
+        {
+            TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion.TLSv12);
+        }
+
+        [Test]
+        public void TestClientSendsServerCertExtensionButServerHasNoCommonTypes_13()
+        {
+            TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion.TLSv13);
+        }
+
+        private void TestClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion tlsVersion)
+        {
+            try
+            {
+                MockRawKeysTlsClient client = new MockRawKeysTlsClient(CertificateType.RawPublicKey,
+                    CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, null, GenerateKeyPair(),
+                    tlsVersion);
+                MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.X509,
+                    CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey }, GenerateKeyPair(),
+                    tlsVersion);
+                PumpData(client, server);
+                Assert.Fail("Should have caused unsupported_certificate alert");
+            }
+            catch (TlsFatalAlertReceived alert)
+            {
+                Assert.AreEqual(AlertDescription.unsupported_certificate, alert.AlertDescription,
+                    "Should have caused unsupported_certificate alert");
+            }
+        }
+
+        private Ed25519PrivateKeyParameters GenerateKeyPair()
+        {
+            return new Ed25519PrivateKeyParameters(Random);
+        }
+
+        private void PumpData(TlsClient client, TlsServer server)
+        {
+            PipedStream clientPipe = new PipedStream();
+            PipedStream serverPipe = new PipedStream(clientPipe);
+
+            TlsClientProtocol clientProtocol = new TlsClientProtocol(clientPipe);
+            TlsServerProtocol serverProtocol = new TlsServerProtocol(serverPipe);
+
+            Server serverRun = new Server(serverProtocol, server);
+            Thread serverThread = new Thread(new ThreadStart(serverRun.Run));
+            serverThread.Start();
+
+            clientProtocol.Connect(client);
+
+            // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity
+            int length = 1000;
+
+            byte[] data = new byte[length];
+            Random.NextBytes(data);
+
+            Stream output = clientProtocol.Stream;
+            output.Write(data, 0, data.Length);
+
+            byte[] echo = new byte[data.Length];
+            int count = Streams.ReadFully(clientProtocol.Stream, echo);
+
+            Assert.AreEqual(count, data.Length);
+            Assert.IsTrue(Arrays.AreEqual(data, echo));
+
+            output.Close();
+
+            serverThread.Join();
+        }
+
+        internal class Server
+        {
+            private readonly TlsServerProtocol m_serverProtocol;
+            private readonly TlsServer m_server;
+
+            internal Server(TlsServerProtocol serverProtocol, TlsServer server)
+            {
+                m_serverProtocol = serverProtocol;
+                m_server = server;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    m_serverProtocol.Accept(m_server);
+                    Streams.PipeAll(m_serverProtocol.Stream, m_serverProtocol.Stream);
+                    m_serverProtocol.Close();
+                }
+                catch (Exception)
+                {
+                }
+            }
+        }
+    }
+}
diff --git a/crypto/test/src/tls/test/TlsServerRawKeysTest.cs b/crypto/test/src/tls/test/TlsServerRawKeysTest.cs
new file mode 100644
index 000000000..22e35505c
--- /dev/null
+++ b/crypto/test/src/tls/test/TlsServerRawKeysTest.cs
@@ -0,0 +1,90 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+using NUnit.Framework;
+
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Tls.Tests
+{
+    /// <summary>A simple test designed to conduct a TLS handshake with an external TLS client.</summary>
+    /// <remarks>
+    /// <code>
+    /// gnutls-cli --rawpkkeyfile ed25519.priv --rawpkfile ed25519.pub --priority NORMAL:+CTYPE-CLI-RAWPK:+CTYPE-SRV-RAWPK --insecure --debug 10 --port 5556 localhost
+    /// </code>
+    /// </remarks>
+    [TestFixture]
+    public class TlsServerRawKeysTest
+    {
+        [Test, Explicit]
+        public void TestConnection()
+        {
+            int port = 5556;
+            ProtocolVersion[] tlsVersions = ProtocolVersion.TLSv13.DownTo(ProtocolVersion.TLSv12);
+
+            TcpListener ss = new TcpListener(IPAddress.Any, port);
+            ss.Start();
+            Stream stdout = Console.OpenStandardOutput();
+            try
+            {
+                foreach (var tlsVersion in tlsVersions)
+                {
+                    TcpClient s = ss.AcceptTcpClient();
+                    Console.WriteLine("--------------------------------------------------------------------------------");
+                    Console.WriteLine("Accepted " + s);
+                    Server serverRun = new Server(s, stdout, tlsVersion);
+                    Thread t = new Thread(new ThreadStart(serverRun.Run));
+                    t.Start();
+                }
+            }
+            finally
+            {
+                ss.Stop();
+            }
+        }
+
+        internal class Server
+        {
+            private readonly TcpClient s;
+            private readonly Stream stdout;
+            private readonly ProtocolVersion tlsVersion;
+
+            internal Server(TcpClient s, Stream stdout, ProtocolVersion tlsVersion)
+            {
+                this.s = s;
+                this.stdout = stdout;
+                this.tlsVersion = tlsVersion;
+            }
+
+            public void Run()
+            {
+                try
+                {
+                    MockRawKeysTlsServer server = new MockRawKeysTlsServer(CertificateType.RawPublicKey,
+                        CertificateType.RawPublicKey, new short[]{ CertificateType.RawPublicKey },
+                        new Ed25519PrivateKeyParameters(new SecureRandom()), tlsVersion);
+                    TlsServerProtocol serverProtocol = new TlsServerProtocol(s.GetStream());
+                    serverProtocol.Accept(server);
+                    Stream log = new TeeOutputStream(serverProtocol.Stream, stdout);
+                    Streams.PipeAll(serverProtocol.Stream, log);
+                    serverProtocol.Close();
+                }
+                finally
+                {
+                    try
+                    {
+                        s.Close();
+                    }
+                    catch (IOException)
+                    {
+                    }
+                }
+            }
+        }
+    }
+}