summary refs log tree commit diff
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
parentCmp updates (diff)
downloadBouncyCastle.NET-ed25519-ed081e3fe9634391ac496bd79193a7d00dfa6f07.tar.xz
(D)TLS: RFC 7250 Raw Public Keys
-rw-r--r--crypto/src/tls/AbstractTlsClient.cs37
-rw-r--r--crypto/src/tls/AbstractTlsServer.cs70
-rw-r--r--crypto/src/tls/Certificate.cs74
-rw-r--r--crypto/src/tls/DtlsClientProtocol.cs6
-rw-r--r--crypto/src/tls/DtlsServerProtocol.cs6
-rw-r--r--crypto/src/tls/SecurityParameters.cs9
-rw-r--r--crypto/src/tls/TlsClientProtocol.cs17
-rw-r--r--crypto/src/tls/TlsExtensionsUtilities.cs10
-rw-r--r--crypto/src/tls/TlsServerProtocol.cs12
-rw-r--r--crypto/src/tls/TlsUtilities.cs16
-rw-r--r--crypto/src/tls/crypto/TlsCrypto.cs7
-rw-r--r--crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs485
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs12
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsRawKeyCertificate.cs507
-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
20 files changed, 1476 insertions, 530 deletions
diff --git a/crypto/src/tls/AbstractTlsClient.cs b/crypto/src/tls/AbstractTlsClient.cs
index 8bfd828f1..3061f3642 100644
--- a/crypto/src/tls/AbstractTlsClient.cs
+++ b/crypto/src/tls/AbstractTlsClient.cs
@@ -174,6 +174,16 @@ namespace Org.BouncyCastle.Tls
             return null;
         }
 
+        protected virtual short[] GetAllowedClientCertificateTypes()
+        {
+            return null;
+        }
+
+        protected virtual short[] GetAllowedServerCertificateTypes()
+        {
+            return null;
+        }
+
         public virtual void Init(TlsClientContext context)
         {
             this.m_context = context;
@@ -334,6 +344,33 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
+            /*
+             * RFC 7250 4.1:
+             *
+             * If the client has no remaining certificate types to send in
+             * the client hello, other than the default X.509 type, it MUST omit the
+             * client_certificate_type extension in the client hello.
+             */
+            short[] clientCertTypes = GetAllowedClientCertificateTypes();
+            if (clientCertTypes != null && (clientCertTypes.Length > 1 || clientCertTypes[0] != CertificateType.X509))
+            {
+                TlsExtensionsUtilities.AddClientCertificateTypeExtensionClient(clientExtensions, clientCertTypes);
+            }
+
+            /*
+             * RFC 7250 4.1:
+             *
+             * If the client has no remaining certificate types to send in
+             * the client hello, other than the default X.509 certificate type, it
+             * MUST omit the entire server_certificate_type extension from the
+             * client hello.
+             */
+            short[] serverCertTypes = GetAllowedServerCertificateTypes();
+            if (serverCertTypes != null && (serverCertTypes.Length > 1 || serverCertTypes[0] != CertificateType.X509))
+            {
+                TlsExtensionsUtilities.AddServerCertificateTypeExtensionClient(clientExtensions, serverCertTypes);
+            }
+
             return clientExtensions;
         }
 
diff --git a/crypto/src/tls/AbstractTlsServer.cs b/crypto/src/tls/AbstractTlsServer.cs
index a41bc4710..3c62793b6 100644
--- a/crypto/src/tls/AbstractTlsServer.cs
+++ b/crypto/src/tls/AbstractTlsServer.cs
@@ -207,6 +207,16 @@ namespace Org.BouncyCastle.Tls
             return true;
         }
 
+        protected virtual bool PreferLocalClientCertificateTypes()
+        {
+            return false;
+        }
+
+        protected virtual short[] GetAllowedClientCertificateTypes()
+        {
+            return null;
+        }
+
         public virtual void Init(TlsServerContext context)
         {
             this.m_context = context;
@@ -491,6 +501,66 @@ namespace Org.BouncyCastle.Tls
                 TlsExtensionsUtilities.AddMaxFragmentLengthExtension(m_serverExtensions, m_maxFragmentLengthOffered);
             }
 
+            // RFC 7250 4.2 for server_certificate_type
+            short[] serverCertTypes = TlsExtensionsUtilities.GetServerCertificateTypeExtensionClient(
+                m_clientExtensions);
+            if (serverCertTypes != null)
+            {
+                TlsCredentials credentials = GetCredentials();
+
+                if (credentials == null || !Arrays.Contains(serverCertTypes, credentials.Certificate.CertificateType))
+                {
+                    // outcome 2: we support the extension but have no common types
+                    throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+                }
+
+                // outcome 3: we support the extension and have a common type
+                TlsExtensionsUtilities.AddServerCertificateTypeExtensionServer(m_serverExtensions,
+                    credentials.Certificate.CertificateType);
+            }
+
+            // RFC 7250 4.2 for client_certificate_type
+            short[] remoteClientCertTypes = TlsExtensionsUtilities.GetClientCertificateTypeExtensionClient(
+                m_clientExtensions);
+            if (remoteClientCertTypes != null)
+            {
+                short[] localClientCertTypes = GetAllowedClientCertificateTypes();
+                if (localClientCertTypes != null)
+                {
+                    short[] preferredTypes;
+                    short[] nonPreferredTypes;
+                    if (PreferLocalClientCertificateTypes())
+                    {
+                        preferredTypes = localClientCertTypes;
+                        nonPreferredTypes = remoteClientCertTypes;
+                    }
+                    else
+                    {
+                        preferredTypes = remoteClientCertTypes;
+                        nonPreferredTypes = localClientCertTypes;
+                    }
+
+                    short selectedType = -1;
+                    for (int i = 0; i < preferredTypes.Length; i++)
+                    {
+                        if (Arrays.Contains(nonPreferredTypes, preferredTypes[i]))
+                        {
+                            selectedType = preferredTypes[i];
+                            break;
+                        }
+                    }
+
+                    if (selectedType == -1)
+                    {
+                        // outcome 2: we support the extension but have no common types
+                        throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
+                    }
+
+                    // outcome 3: we support the extension and have a common type
+                    TlsExtensionsUtilities.AddClientCertificateTypeExtensionServer(m_serverExtensions, selectedType);
+                } // else outcome 1: we don't support the extension
+            }
+
             return m_serverExtensions;
         }
 
diff --git a/crypto/src/tls/Certificate.cs b/crypto/src/tls/Certificate.cs
index c7f08b2aa..30b14368b 100644
--- a/crypto/src/tls/Certificate.cs
+++ b/crypto/src/tls/Certificate.cs
@@ -1,7 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
-
+using Org.BouncyCastle.Pqc.Crypto.Lms;
 using Org.BouncyCastle.Tls.Crypto;
 
 namespace Org.BouncyCastle.Tls
@@ -25,18 +25,8 @@ namespace Org.BouncyCastle.Tls
 
         public sealed class ParseOptions
         {
-            private int m_maxChainLength = int.MaxValue;
-
-            public int MaxChainLength
-            {
-                get { return m_maxChainLength; }
-            }
-
-            public ParseOptions SetMaxChainLength(int maxChainLength)
-            {
-                this.m_maxChainLength = maxChainLength;
-                return this;
-            }
+            public short CertificateType { get; set; } = Tls.CertificateType.X509;
+            public int MaxChainLength { get; set; } = int.MaxValue;
         }
 
         private static CertificateEntry[] Convert(TlsCertificate[] certificateList)
@@ -55,22 +45,29 @@ namespace Org.BouncyCastle.Tls
 
         private readonly byte[] m_certificateRequestContext;
         private readonly CertificateEntry[] m_certificateEntryList;
+        private readonly short m_certificateType;
 
         public Certificate(TlsCertificate[] certificateList)
             : this(null, Convert(certificateList))
         {
         }
 
-        // TODO[tls13] Prefer to manage the certificateRequestContext internally only? 
         public Certificate(byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
+            : this(Tls.CertificateType.X509, certificateRequestContext, certificateEntryList)
+        {
+        }
+
+        // TODO[tls13] Prefer to manage the certificateRequestContext internally only?
+        public Certificate(short certificateType, byte[] certificateRequestContext, CertificateEntry[] certificateEntryList)
         {
             if (null != certificateRequestContext && !TlsUtilities.IsValidUint8(certificateRequestContext.Length))
                 throw new ArgumentException("cannot be longer than 255", "certificateRequestContext");
             if (TlsUtilities.IsNullOrContainsNull(certificateEntryList))
                 throw new ArgumentException("cannot be null or contain any nulls", "certificateEntryList");
 
-            this.m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
-            this.m_certificateEntryList = certificateEntryList;
+            m_certificateRequestContext = TlsUtilities.Clone(certificateRequestContext);
+            m_certificateEntryList = certificateEntryList;
+            m_certificateType = certificateType;
         }
 
         public byte[] GetCertificateRequestContext()
@@ -99,22 +96,13 @@ namespace Org.BouncyCastle.Tls
             return CloneCertificateEntryList();
         }
 
-        public short CertificateType
-        {
-            get { return Tls.CertificateType.X509; }
-        }
+        public short CertificateType => m_certificateType;
 
-        public int Length
-        {
-            get { return m_certificateEntryList.Length; }
-        }
+        public int Length => m_certificateEntryList.Length;
 
         /// <returns><c>true</c> if this certificate chain contains no certificates, or <c>false</c> otherwise.
         /// </returns>
-        public bool IsEmpty
-        {
-            get { return m_certificateEntryList.Length == 0; }
-        }
+        public bool IsEmpty => m_certificateEntryList.Length == 0;
 
         /// <summary>Encode this <see cref="Certificate"/> to a <see cref="Stream"/>, and optionally calculate the
         /// "end point hash" (per RFC 5929's tls-server-end-point binding).</summary>
@@ -168,8 +156,13 @@ namespace Org.BouncyCastle.Tls
                 }
             }
 
-            TlsUtilities.CheckUint24(totalLength);
-            TlsUtilities.WriteUint24((int)totalLength, messageOutput);
+            // RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is
+            // but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list
+            if (isTlsV13 || m_certificateType != Tls.CertificateType.RawPublicKey)
+            {
+                TlsUtilities.CheckUint24(totalLength);
+                TlsUtilities.WriteUint24((int)totalLength, messageOutput);
+            }
 
             for (int i = 0; i < count; ++i)
             {
@@ -195,6 +188,7 @@ namespace Org.BouncyCastle.Tls
         {
             SecurityParameters securityParameters = context.SecurityParameters;
             bool isTlsV13 = TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion);
+            short certType = options.CertificateType;
 
             byte[] certificateRequestContext = null;
             if (isTlsV13)
@@ -207,7 +201,7 @@ namespace Org.BouncyCastle.Tls
             {
                 return !isTlsV13 ? EmptyChain
                     :  certificateRequestContext.Length < 1 ? EmptyChainTls13
-                    :  new Certificate(certificateRequestContext, EmptyCertEntries);
+                    :  new Certificate(certType, certificateRequestContext, EmptyCertEntries);
             }
 
             byte[] certListData = TlsUtilities.ReadFully(totalLength, messageInput);
@@ -225,8 +219,20 @@ namespace Org.BouncyCastle.Tls
                         "Certificate chain longer than maximum (" + maxChainLength + ")");
                 }
 
-                byte[] derEncoding = TlsUtilities.ReadOpaque24(buf, 1);
-                TlsCertificate cert = crypto.CreateCertificate(derEncoding);
+                // RFC 7250 indicates the raw key is not wrapped in a cert list like X509 is
+                // but RFC 8446 wraps it in a CertificateEntry, which is inside certificate_list
+                byte[] derEncoding;
+                if (isTlsV13 || certType != Tls.CertificateType.RawPublicKey)
+                {
+                    derEncoding = TlsUtilities.ReadOpaque24(buf, 1);
+                }
+                else
+                {
+                    derEncoding = certListData;
+                    buf.Seek(totalLength, SeekOrigin.Current);
+                }
+
+                TlsCertificate cert = crypto.CreateCertificate(certType, derEncoding);
 
                 if (certificate_list.Count < 1 && endPointHashOutput != null)
                 {
@@ -250,7 +256,7 @@ namespace Org.BouncyCastle.Tls
                 certificateList[i] = (CertificateEntry)certificate_list[i];
             }
 
-            return new Certificate(certificateRequestContext, certificateList);
+            return new Certificate(certType, certificateRequestContext, certificateList);
         }
 
         private static void CalculateEndPointHash(TlsContext context, TlsCertificate cert, byte[] encoding,
diff --git a/crypto/src/tls/DtlsClientProtocol.cs b/crypto/src/tls/DtlsClientProtocol.cs
index 3e3aab662..b8c09617a 100644
--- a/crypto/src/tls/DtlsClientProtocol.cs
+++ b/crypto/src/tls/DtlsClientProtocol.cs
@@ -579,6 +579,10 @@ namespace Org.BouncyCastle.Tls
             TlsProtocol.AssertEmpty(buf);
 
             state.certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, state.keyExchange);
+
+            state.clientContext.SecurityParameters.m_clientCertificateType =
+                TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(state.serverExtensions,
+                    CertificateType.X509);
         }
 
         /// <exception cref="IOException"/>
@@ -633,7 +637,7 @@ namespace Org.BouncyCastle.Tls
         protected virtual void ProcessServerCertificate(ClientHandshakeState state, byte[] body)
         {
             state.authentication = TlsUtilities.ReceiveServerCertificate(state.clientContext, state.client,
-                new MemoryStream(body, false));
+                new MemoryStream(body, false), state.serverExtensions);
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/tls/DtlsServerProtocol.cs b/crypto/src/tls/DtlsServerProtocol.cs
index e4ce4d6be..b42f97b64 100644
--- a/crypto/src/tls/DtlsServerProtocol.cs
+++ b/crypto/src/tls/DtlsServerProtocol.cs
@@ -637,7 +637,11 @@ namespace Org.BouncyCastle.Tls
             MemoryStream buf = new MemoryStream(body, false);
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
-                .SetMaxChainLength(state.server.GetMaxCertificateChainLength());
+            {
+                CertificateType = TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(
+                    state.clientExtensions, CertificateType.X509),
+                MaxChainLength = state.server.GetMaxCertificateChainLength(),
+            };
 
             Certificate clientCertificate = Certificate.Parse(options, state.serverContext, buf, null);
 
diff --git a/crypto/src/tls/SecurityParameters.cs b/crypto/src/tls/SecurityParameters.cs
index 7891549b6..7775ca7c7 100644
--- a/crypto/src/tls/SecurityParameters.cs
+++ b/crypto/src/tls/SecurityParameters.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
 
 using Org.BouncyCastle.Tls.Crypto;
 
@@ -52,6 +51,7 @@ namespace Org.BouncyCastle.Tls
         internal Certificate m_peerCertificate = null;
         internal ProtocolVersion m_negotiatedVersion = null;
         internal int m_statusRequestVersion = 0;
+        internal short m_clientCertificateType = -1;
 
         // TODO[tls-ops] Investigate whether we can handle verify data using TlsSecret
         internal byte[] m_localVerifyData = null;
@@ -100,6 +100,11 @@ namespace Org.BouncyCastle.Tls
             get { return m_cipherSuite; }
         }
 
+        public short ClientCertificateType
+        {
+            get { return m_clientCertificateType; }
+        }
+
         public short[] ClientCertTypes
         {
             get { return m_clientCertTypes; }
diff --git a/crypto/src/tls/TlsClientProtocol.cs b/crypto/src/tls/TlsClientProtocol.cs
index fc3894710..b7295bcc5 100644
--- a/crypto/src/tls/TlsClientProtocol.cs
+++ b/crypto/src/tls/TlsClientProtocol.cs
@@ -380,8 +380,8 @@ namespace Org.BouncyCastle.Tls
                      * NOTE: Certificate processing (including authentication) is delayed to allow for a
                      * possible CertificateStatus message.
                      */
-                    this.m_authentication = TlsUtilities.ReceiveServerCertificate(m_tlsClientContext, m_tlsClient,
-                        buf);
+                    m_authentication = TlsUtilities.ReceiveServerCertificate(m_tlsClientContext, m_tlsClient, buf,
+                        m_serverExtensions);
                     break;
                 }
                 default:
@@ -1364,6 +1364,10 @@ namespace Org.BouncyCastle.Tls
 
             this.m_certificateRequest = certificateRequest;
 
+            m_tlsClientContext.SecurityParameters.m_clientCertificateType =
+                TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(m_serverExtensions,
+                    CertificateType.X509);
+
             TlsUtilities.EstablishServerSigAlgs(m_tlsClientContext.SecurityParameters, certificateRequest);
         }
 
@@ -1467,7 +1471,8 @@ namespace Org.BouncyCastle.Tls
             if (m_selectedPsk13)
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
 
-            this.m_authentication = TlsUtilities.Receive13ServerCertificate(m_tlsClientContext, m_tlsClient, buf);
+            m_authentication = TlsUtilities.Receive13ServerCertificate(m_tlsClientContext, m_tlsClient, buf,
+                m_serverExtensions);
 
             // NOTE: In TLS 1.3 we don't have to wait for a possible CertificateStatus message.
             HandleServerCertificate();
@@ -1509,7 +1514,11 @@ namespace Org.BouncyCastle.Tls
 
             AssertEmpty(buf);
 
-            this.m_certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, m_keyExchange);
+            m_certificateRequest = TlsUtilities.ValidateCertificateRequest(certificateRequest, m_keyExchange);
+
+            m_tlsClientContext.SecurityParameters.m_clientCertificateType =
+                TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(m_serverExtensions,
+                    CertificateType.X509);
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/tls/TlsExtensionsUtilities.cs b/crypto/src/tls/TlsExtensionsUtilities.cs
index 5a97e1efc..46d42417c 100644
--- a/crypto/src/tls/TlsExtensionsUtilities.cs
+++ b/crypto/src/tls/TlsExtensionsUtilities.cs
@@ -302,10 +302,11 @@ namespace Org.BouncyCastle.Tls
         }
 
         /// <exception cref="IOException"/>
-        public static short GetClientCertificateTypeExtensionServer(IDictionary<int, byte[]> extensions)
+        public static short GetClientCertificateTypeExtensionServer(IDictionary<int, byte[]> extensions,
+            short defaultValue)
         {
             byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.client_certificate_type);
-            return extensionData == null ? (short)-1 : ReadCertificateTypeExtensionServer(extensionData);
+            return extensionData == null ? defaultValue : ReadCertificateTypeExtensionServer(extensionData);
         }
 
         /// <exception cref="IOException"/>
@@ -415,10 +416,11 @@ namespace Org.BouncyCastle.Tls
         }
 
         /// <exception cref="IOException"/>
-        public static short GetServerCertificateTypeExtensionServer(IDictionary<int, byte[]> extensions)
+        public static short GetServerCertificateTypeExtensionServer(IDictionary<int, byte[]> extensions,
+            short defaultValue)
         {
             byte[] extensionData = TlsUtilities.GetExtensionData(extensions, ExtensionType.server_certificate_type);
-            return extensionData == null ? (short)-1 : ReadCertificateTypeExtensionServer(extensionData);
+            return extensionData == null ? defaultValue : ReadCertificateTypeExtensionServer(extensionData);
         }
 
         /// <exception cref="IOException"/>
diff --git a/crypto/src/tls/TlsServerProtocol.cs b/crypto/src/tls/TlsServerProtocol.cs
index 3acbe90df..bf4b9119a 100644
--- a/crypto/src/tls/TlsServerProtocol.cs
+++ b/crypto/src/tls/TlsServerProtocol.cs
@@ -1290,7 +1290,11 @@ namespace Org.BouncyCastle.Tls
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
-                .SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
+            {
+                CertificateType = TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(m_serverExtensions,
+                    CertificateType.X509),
+                MaxChainLength = m_tlsServer.GetMaxCertificateChainLength(),
+            };
 
             Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
 
@@ -1326,7 +1330,11 @@ namespace Org.BouncyCastle.Tls
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
-                .SetMaxChainLength(m_tlsServer.GetMaxCertificateChainLength());
+            {
+                CertificateType = TlsExtensionsUtilities.GetClientCertificateTypeExtensionServer(m_serverExtensions,
+                    CertificateType.X509),
+                MaxChainLength = m_tlsServer.GetMaxCertificateChainLength(),
+            };
 
             Certificate clientCertificate = Certificate.Parse(options, m_tlsServerContext, buf, null);
 
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
index 97895e8f2..a417336be 100644
--- a/crypto/src/tls/TlsUtilities.cs
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -4756,7 +4756,7 @@ namespace Org.BouncyCastle.Tls
         }
 
         internal static TlsAuthentication ReceiveServerCertificate(TlsClientContext clientContext, TlsClient client,
-            MemoryStream buf)
+            MemoryStream buf, IDictionary<int, byte[]> serverExtensions)
         {
             SecurityParameters securityParameters = clientContext.SecurityParameters;
             if (KeyExchangeAlgorithm.IsAnonymous(securityParameters.KeyExchangeAlgorithm)
@@ -4768,7 +4768,11 @@ namespace Org.BouncyCastle.Tls
             MemoryStream endPointHash = new MemoryStream();
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
-                .SetMaxChainLength(client.GetMaxCertificateChainLength());
+            {
+                CertificateType = TlsExtensionsUtilities.GetServerCertificateTypeExtensionServer(serverExtensions,
+                    CertificateType.X509),
+                MaxChainLength = client.GetMaxCertificateChainLength(),
+            };
 
             Certificate serverCertificate = Certificate.Parse(options, clientContext, buf, endPointHash);
 
@@ -4788,14 +4792,18 @@ namespace Org.BouncyCastle.Tls
         }
 
         internal static TlsAuthentication Receive13ServerCertificate(TlsClientContext clientContext, TlsClient client,
-            MemoryStream buf)
+            MemoryStream buf, IDictionary<int, byte[]> serverExtensions)
         {
             SecurityParameters securityParameters = clientContext.SecurityParameters;
             if (null != securityParameters.PeerCertificate)
                 throw new TlsFatalAlert(AlertDescription.unexpected_message);
 
             Certificate.ParseOptions options = new Certificate.ParseOptions()
-                .SetMaxChainLength(client.GetMaxCertificateChainLength());
+            {
+                CertificateType = TlsExtensionsUtilities.GetServerCertificateTypeExtensionServer(serverExtensions,
+                    CertificateType.X509),
+                MaxChainLength = client.GetMaxCertificateChainLength(),
+            };
 
             Certificate serverCertificate = Certificate.Parse(options, clientContext, buf, null);
 
diff --git a/crypto/src/tls/crypto/TlsCrypto.cs b/crypto/src/tls/crypto/TlsCrypto.cs
index c9d00cbb0..d9c2c0da1 100644
--- a/crypto/src/tls/crypto/TlsCrypto.cs
+++ b/crypto/src/tls/crypto/TlsCrypto.cs
@@ -112,6 +112,13 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <exception cref="IOException">if there is an issue on decoding or constructing the certificate.</exception>
         TlsCertificate CreateCertificate(byte[] encoding);
 
+        /// <summary>Create a TlsCertificate from an ASN.1 binary encoding of a certificate.</summary>
+        /// <param name="type">Certificate type as per IANA TLS Certificate Types registry.</param>
+        /// <param name="encoding">DER/BER encoding of the certificate of interest.</param>
+        /// <returns>a TlsCertificate.</returns>
+        /// <exception cref="IOException">if there is an issue on decoding or constructing the certificate.</exception>
+        TlsCertificate CreateCertificate(short type, byte[] encoding);
+
         /// <summary>Create a cipher for the specified encryption and MAC algorithms.</summary>
         /// <remarks>
         /// See enumeration classes <see cref="EncryptionAlgorithm"/>, <see cref="MacAlgorithm"/> for appropriate
diff --git a/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
index b2e1e7fe0..607f12778 100644
--- a/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
+++ b/crypto/src/tls/crypto/impl/AbstractTlsCrypto.cs
@@ -48,7 +48,12 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
 
         public abstract SecureRandom SecureRandom { get; }
 
-        public abstract TlsCertificate CreateCertificate(byte[] encoding);
+        public virtual TlsCertificate CreateCertificate(byte[] encoding)
+        {
+            return CreateCertificate(CertificateType.X509, encoding);
+        }
+
+        public abstract TlsCertificate CreateCertificate(short type, byte[] encoding);
 
         public abstract TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm, int macAlgorithm);
 
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs
index 7e946ce23..f64d8332d 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCertificate.cs
@@ -3,19 +3,14 @@ using System.IO;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Engines;
-using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Crypto.Signers;
 using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 {
     /// <summary>Implementation class for a single X.509 certificate based on the BC light-weight API.</summary>
     public class BcTlsCertificate
-        : TlsCertificate
+        : BcTlsRawKeyCertificate
     {
         /// <exception cref="IOException"/>
         public static BcTlsCertificate Convert(BcTlsCrypto crypto, TlsCertificate certificate)
@@ -40,15 +35,8 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             }
         }
 
-        protected readonly BcTlsCrypto m_crypto;
         protected readonly X509CertificateStructure m_certificate;
 
-        protected DHPublicKeyParameters m_pubKeyDH = null;
-        protected ECPublicKeyParameters m_pubKeyEC = null;
-        protected Ed25519PublicKeyParameters m_pubKeyEd25519 = null;
-        protected Ed448PublicKeyParameters m_pubKeyEd448 = null;
-        protected RsaKeyParameters m_pubKeyRsa = null;
-
         /// <exception cref="IOException"/>
         public BcTlsCertificate(BcTlsCrypto crypto, byte[] encoding)
             : this(crypto, ParseCertificate(encoding))
@@ -56,204 +44,21 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
         }
 
         public BcTlsCertificate(BcTlsCrypto crypto, X509CertificateStructure certificate)
+            : base(crypto, certificate.SubjectPublicKeyInfo)
         {
-            this.m_crypto = crypto;
-            this.m_certificate = certificate;
+            m_certificate = certificate;
         }
 
-        /// <exception cref="IOException"/>
-        public virtual TlsEncryptor CreateEncryptor(int tlsCertificateRole)
-        {
-            ValidateKeyUsage(KeyUsage.KeyEncipherment);
-
-            switch (tlsCertificateRole)
-            {
-            case TlsCertificateRole.RsaEncryption:
-            {
-                this.m_pubKeyRsa = GetPubKeyRsa();
-                return new BcTlsRsaEncryptor(m_crypto, m_pubKeyRsa);
-            }
-            // TODO[gmssl]
-            //case TlsCertificateRole.Sm2Encryption:
-            //{
-            //    this.m_pubKeyEC = GetPubKeyEC();
-            //    return new BcTlsSM2Encryptor(m_crypto, m_pubKeyEC);
-            //}
-            }
-
-            throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
+        public virtual X509CertificateStructure X509CertificateStructure => m_certificate;
 
         /// <exception cref="IOException"/>
-        public virtual TlsVerifier CreateVerifier(short signatureAlgorithm)
-        {
-            switch (signatureAlgorithm)
-            {
-            case SignatureAlgorithm.ed25519:
-            case SignatureAlgorithm.ed448:
-            {
-                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
-                Tls13Verifier tls13Verifier = CreateVerifier(signatureScheme);
-                return new LegacyTls13Verifier(signatureScheme, tls13Verifier);
-            }
-            }
-
-            ValidateKeyUsage(KeyUsage.DigitalSignature);
-
-            switch (signatureAlgorithm)
-            {
-            case SignatureAlgorithm.dsa:
-                return new BcTlsDsaVerifier(m_crypto, GetPubKeyDss());
-
-            case SignatureAlgorithm.ecdsa:
-                return new BcTlsECDsaVerifier(m_crypto, GetPubKeyEC());
-
-            case SignatureAlgorithm.rsa:
-            {
-                ValidateRsa_Pkcs1();
-                return new BcTlsRsaVerifier(m_crypto, GetPubKeyRsa());
-            }
-
-            case SignatureAlgorithm.rsa_pss_pss_sha256:
-            case SignatureAlgorithm.rsa_pss_pss_sha384:
-            case SignatureAlgorithm.rsa_pss_pss_sha512:
-            {
-                ValidateRsa_Pss_Pss(signatureAlgorithm);
-                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
-                return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
-            }
-
-            case SignatureAlgorithm.rsa_pss_rsae_sha256:
-            case SignatureAlgorithm.rsa_pss_rsae_sha384:
-            case SignatureAlgorithm.rsa_pss_rsae_sha512:
-            {
-                ValidateRsa_Pss_Rsae();
-                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
-                return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
-            }
-
-            default:
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual Tls13Verifier CreateVerifier(int signatureScheme)
-        {
-            ValidateKeyUsage(KeyUsage.DigitalSignature);
-
-            switch (signatureScheme)
-            {
-            case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256:
-            case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384:
-            case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512:
-            case SignatureScheme.ecdsa_secp256r1_sha256:
-            case SignatureScheme.ecdsa_secp384r1_sha384:
-            case SignatureScheme.ecdsa_secp521r1_sha512:
-            case SignatureScheme.ecdsa_sha1:
-            {
-                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
-                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
-
-                ISigner verifier = new DsaDigestSigner(new ECDsaSigner(), digest);
-                verifier.Init(false, GetPubKeyEC());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            case SignatureScheme.ed25519:
-            {
-                Ed25519Signer verifier = new Ed25519Signer();
-                verifier.Init(false, GetPubKeyEd25519());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            case SignatureScheme.ed448:
-            {
-                Ed448Signer verifier = new Ed448Signer(TlsUtilities.EmptyBytes);
-                verifier.Init(false, GetPubKeyEd448());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            case SignatureScheme.rsa_pkcs1_sha1:
-            case SignatureScheme.rsa_pkcs1_sha256:
-            case SignatureScheme.rsa_pkcs1_sha384:
-            case SignatureScheme.rsa_pkcs1_sha512:
-            {
-                ValidateRsa_Pkcs1();
-
-                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
-                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
-
-                RsaDigestSigner verifier = new RsaDigestSigner(digest,
-                    TlsCryptoUtilities.GetOidForHash(cryptoHashAlgorithm));
-                verifier.Init(false, GetPubKeyRsa());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            case SignatureScheme.rsa_pss_pss_sha256:
-            case SignatureScheme.rsa_pss_pss_sha384:
-            case SignatureScheme.rsa_pss_pss_sha512:
-            {
-                ValidateRsa_Pss_Pss(SignatureScheme.GetSignatureAlgorithm(signatureScheme));
-
-                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
-                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
-
-                PssSigner verifier = new PssSigner(new RsaEngine(), digest, digest.GetDigestSize());
-                verifier.Init(false, GetPubKeyRsa());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            case SignatureScheme.rsa_pss_rsae_sha256:
-            case SignatureScheme.rsa_pss_rsae_sha384:
-            case SignatureScheme.rsa_pss_rsae_sha512:
-            {
-                ValidateRsa_Pss_Rsae();
-
-                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
-                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
-
-                PssSigner verifier = new PssSigner(new RsaEngine(), digest, digest.GetDigestSize());
-                verifier.Init(false, GetPubKeyRsa());
-
-                return new BcTls13Verifier(verifier);
-            }
-
-            // TODO[RFC 8998]
-            //case SignatureScheme.sm2sig_sm3:
-            //{
-            //    ParametersWithID parametersWithID = new ParametersWithID(GetPubKeyEC(),
-            //        Strings.ToByteArray("TLSv1.3+GM+Cipher+Suite"));
-    
-            //    SM2Signer verifier = new SM2Signer();
-            //    verifier.Init(false, parametersWithID);
-
-            //    return new BcTls13Verifier(verifier);
-            //}
-
-            default:
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-            }
-        }
-
-        public virtual X509CertificateStructure X509CertificateStructure
-        {
-            get { return m_certificate; }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual byte[] GetEncoded()
+        public override byte[] GetEncoded()
         {
             return m_certificate.GetEncoded(Asn1Encodable.Der);
         }
 
         /// <exception cref="IOException"/>
-        public virtual byte[] GetExtension(DerObjectIdentifier extensionOid)
+        public override byte[] GetExtension(DerObjectIdentifier extensionOid)
         {
             X509Extensions extensions = m_certificate.TbsCertificate.Extensions;
             if (extensions != null)
@@ -267,191 +72,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             return null;
         }
 
-        public virtual BigInteger SerialNumber
-        {
-            get { return m_certificate.SerialNumber.Value; }
-        }
-
-        public virtual string SigAlgOid
-        {
-            get { return m_certificate.SignatureAlgorithm.Algorithm.Id; }
-        }
-
-        public virtual Asn1Encodable GetSigAlgParams()
-        {
-            return m_certificate.SignatureAlgorithm.Parameters;
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual short GetLegacySignatureAlgorithm()
-        {
-            AsymmetricKeyParameter publicKey = GetPublicKey();
-            if (publicKey.IsPrivate)
-                throw new TlsFatalAlert(AlertDescription.internal_error);
-
-            if (!SupportsKeyUsage(KeyUsage.DigitalSignature))
-                return -1;
-
-            /*
-             * RFC 5246 7.4.6. Client Certificate
-             */
+        public override BigInteger SerialNumber => m_certificate.SerialNumber.Value;
 
-            /*
-             * RSA public key; the certificate MUST allow the key to be used for signing with the
-             * signature scheme and hash algorithm that will be employed in the certificate verify
-             * message.
-             */
-            if (publicKey is RsaKeyParameters)
-                return SignatureAlgorithm.rsa;
+        public override string SigAlgOid => m_certificate.SignatureAlgorithm.Algorithm.Id;
 
-            /*
-                * DSA public key; the certificate MUST allow the key to be used for signing with the
-                * hash algorithm that will be employed in the certificate verify message.
-                */
-            if (publicKey is DsaPublicKeyParameters)
-                return SignatureAlgorithm.dsa;
+        public override Asn1Encodable GetSigAlgParams() => m_certificate.SignatureAlgorithm.Parameters;
 
-            /*
-             * ECDSA-capable public key; the certificate MUST allow the key to be used for signing
-             * with the hash algorithm that will be employed in the certificate verify message; the
-             * public key MUST use a curve and point format supported by the server.
-             */
-            if (publicKey is ECPublicKeyParameters)
-            {
-                // TODO Check the curve and point format
-                return SignatureAlgorithm.ecdsa;
-            }
-
-            return -1;
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual DHPublicKeyParameters GetPubKeyDH()
-        {
-            try
-            {
-                return (DHPublicKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual DsaPublicKeyParameters GetPubKeyDss()
-        {
-            try
-            {
-                return (DsaPublicKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual ECPublicKeyParameters GetPubKeyEC()
-        {
-            try
-            {
-                return (ECPublicKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual Ed25519PublicKeyParameters GetPubKeyEd25519()
-        {
-            try
-            {
-                return (Ed25519PublicKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual Ed448PublicKeyParameters GetPubKeyEd448()
-        {
-            try
-            {
-                return (Ed448PublicKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual RsaKeyParameters GetPubKeyRsa()
-        {
-            try
-            {
-                return (RsaKeyParameters)GetPublicKey();
-            }
-            catch (InvalidCastException e)
-            {
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm)
-        {
-            return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.DigitalSignature);
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual bool SupportsSignatureAlgorithmCA(short signatureAlgorithm)
-        {
-            return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.KeyCertSign);
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual TlsCertificate CheckUsageInRole(int tlsCertificateRole)
-        {
-            switch (tlsCertificateRole)
-            {
-            case TlsCertificateRole.DH:
-            {
-                ValidateKeyUsage(KeyUsage.KeyAgreement);
-                this.m_pubKeyDH = GetPubKeyDH();
-                return this;
-            }
-            case TlsCertificateRole.ECDH:
-            {
-                ValidateKeyUsage(KeyUsage.KeyAgreement);
-                this.m_pubKeyEC = GetPubKeyEC();
-                return this;
-            }
-            }
-
-            throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
-
-        /// <exception cref="IOException"/>
-        protected virtual AsymmetricKeyParameter GetPublicKey()
-        {
-            SubjectPublicKeyInfo keyInfo = m_certificate.SubjectPublicKeyInfo;
-            try
-            {
-                return PublicKeyFactory.CreateKey(keyInfo);
-            }
-            catch (Exception e)
-            {
-                throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
-            }
-        }
-
-        protected virtual bool SupportsKeyUsage(int keyUsageBits)
+        protected override bool SupportsKeyUsage(int keyUsageBits)
         {
             X509Extensions exts = m_certificate.TbsCertificate.Extensions;
             if (exts != null)
@@ -466,97 +93,5 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             }
             return true;
         }
-
-        protected virtual bool SupportsRsa_Pkcs1()
-        {
-            AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
-            return RsaUtilities.SupportsPkcs1(pubKeyAlgID);
-        }
-
-        protected virtual bool SupportsRsa_Pss_Pss(short signatureAlgorithm)
-        {
-            AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
-            return RsaUtilities.SupportsPss_Pss(signatureAlgorithm, pubKeyAlgID);
-        }
-
-        protected virtual bool SupportsRsa_Pss_Rsae()
-        {
-            AlgorithmIdentifier pubKeyAlgID = m_certificate.SubjectPublicKeyInfo.AlgorithmID;
-            return RsaUtilities.SupportsPss_Rsae(pubKeyAlgID);
-        }
-
-        /// <exception cref="IOException"/>
-        protected virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm, int keyUsage)
-        {
-            if (!SupportsKeyUsage(keyUsage))
-                return false;
-
-            AsymmetricKeyParameter publicKey = GetPublicKey();
-
-            switch (signatureAlgorithm)
-            {
-            case SignatureAlgorithm.rsa:
-                return SupportsRsa_Pkcs1()
-                    && publicKey is RsaKeyParameters;
-
-            case SignatureAlgorithm.dsa:
-                return publicKey is DsaPublicKeyParameters;
-
-            case SignatureAlgorithm.ecdsa:
-            case SignatureAlgorithm.ecdsa_brainpoolP256r1tls13_sha256:
-            case SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384:
-            case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
-                return publicKey is ECPublicKeyParameters;
-
-            case SignatureAlgorithm.ed25519:
-                return publicKey is Ed25519PublicKeyParameters;
-
-            case SignatureAlgorithm.ed448:
-                return publicKey is Ed448PublicKeyParameters;
-
-            case SignatureAlgorithm.rsa_pss_rsae_sha256:
-            case SignatureAlgorithm.rsa_pss_rsae_sha384:
-            case SignatureAlgorithm.rsa_pss_rsae_sha512:
-                return SupportsRsa_Pss_Rsae()
-                    && publicKey is RsaKeyParameters;
-
-            case SignatureAlgorithm.rsa_pss_pss_sha256:
-            case SignatureAlgorithm.rsa_pss_pss_sha384:
-            case SignatureAlgorithm.rsa_pss_pss_sha512:
-                return SupportsRsa_Pss_Pss(signatureAlgorithm)
-                    && publicKey is RsaKeyParameters;
-
-            default:
-                return false;
-            }
-        }
-
-        /// <exception cref="IOException"/>
-        public virtual void ValidateKeyUsage(int keyUsageBits)
-        {
-            if (!SupportsKeyUsage(keyUsageBits))
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
-
-        /// <exception cref="IOException"/>
-        protected virtual void ValidateRsa_Pkcs1()
-        {
-            if (!SupportsRsa_Pkcs1())
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
-
-        /// <exception cref="IOException"/>
-        protected virtual void ValidateRsa_Pss_Pss(short signatureAlgorithm)
-        {
-            if (!SupportsRsa_Pss_Pss(signatureAlgorithm))
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
-
-        /// <exception cref="IOException"/>
-        protected virtual void ValidateRsa_Pss_Rsae()
-        {
-            if (!SupportsRsa_Pss_Rsae())
-                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
-        }
     }
 }
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
index e84361e49..3f63f9e83 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsCrypto.cs
@@ -42,9 +42,17 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             get { return m_entropySource; }
         }
 
-        public override TlsCertificate CreateCertificate(byte[] encoding)
+        public override TlsCertificate CreateCertificate(short type, byte[] encoding)
         {
-            return new BcTlsCertificate(this, encoding);
+            switch (type)
+            {
+            case CertificateType.X509:
+                return new BcTlsCertificate(this, encoding);
+            case CertificateType.RawPublicKey:
+                return new BcTlsRawKeyCertificate(this, encoding);
+            default:
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
         }
 
         public override TlsCipher CreateCipher(TlsCryptoParameters cryptoParams, int encryptionAlgorithm,
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsRawKeyCertificate.cs b/crypto/src/tls/crypto/impl/bc/BcTlsRawKeyCertificate.cs
new file mode 100644
index 000000000..4d208b35a
--- /dev/null
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsRawKeyCertificate.cs
@@ -0,0 +1,507 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Engines;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Signers;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
+{
+    /// <summary>Implementation class for a single X.509 certificate based on the BC light-weight API.</summary>
+    public class BcTlsRawKeyCertificate
+        : TlsCertificate
+    {
+        protected readonly BcTlsCrypto m_crypto;
+        protected readonly SubjectPublicKeyInfo m_keyInfo;
+
+        protected DHPublicKeyParameters m_pubKeyDH = null;
+        protected ECPublicKeyParameters m_pubKeyEC = null;
+        protected Ed25519PublicKeyParameters m_pubKeyEd25519 = null;
+        protected Ed448PublicKeyParameters m_pubKeyEd448 = null;
+        protected RsaKeyParameters m_pubKeyRsa = null;
+
+        /// <exception cref="IOException"/>
+        public BcTlsRawKeyCertificate(BcTlsCrypto crypto, byte[] encoding)
+            : this(crypto, SubjectPublicKeyInfo.GetInstance(encoding))
+        {
+        }
+
+        public BcTlsRawKeyCertificate(BcTlsCrypto crypto, SubjectPublicKeyInfo keyInfo)
+        {
+            m_crypto = crypto;
+            m_keyInfo = keyInfo;
+        }
+
+        public virtual SubjectPublicKeyInfo SubjectPublicKeyInfo => m_keyInfo;
+
+        /// <exception cref="IOException"/>
+        public virtual TlsEncryptor CreateEncryptor(int tlsCertificateRole)
+        {
+            ValidateKeyUsage(KeyUsage.KeyEncipherment);
+
+            switch (tlsCertificateRole)
+            {
+            case TlsCertificateRole.RsaEncryption:
+            {
+                this.m_pubKeyRsa = GetPubKeyRsa();
+                return new BcTlsRsaEncryptor(m_crypto, m_pubKeyRsa);
+            }
+            // TODO[gmssl]
+            //case TlsCertificateRole.Sm2Encryption:
+            //{
+            //    this.m_pubKeyEC = GetPubKeyEC();
+            //    return new BcTlsSM2Encryptor(m_crypto, m_pubKeyEC);
+            //}
+            }
+
+            throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual TlsVerifier CreateVerifier(short signatureAlgorithm)
+        {
+            switch (signatureAlgorithm)
+            {
+            case SignatureAlgorithm.ed25519:
+            case SignatureAlgorithm.ed448:
+            {
+                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
+                Tls13Verifier tls13Verifier = CreateVerifier(signatureScheme);
+                return new LegacyTls13Verifier(signatureScheme, tls13Verifier);
+            }
+            }
+
+            ValidateKeyUsage(KeyUsage.DigitalSignature);
+
+            switch (signatureAlgorithm)
+            {
+            case SignatureAlgorithm.dsa:
+                return new BcTlsDsaVerifier(m_crypto, GetPubKeyDss());
+
+            case SignatureAlgorithm.ecdsa:
+                return new BcTlsECDsaVerifier(m_crypto, GetPubKeyEC());
+
+            case SignatureAlgorithm.rsa:
+            {
+                ValidateRsa_Pkcs1();
+                return new BcTlsRsaVerifier(m_crypto, GetPubKeyRsa());
+            }
+
+            case SignatureAlgorithm.rsa_pss_pss_sha256:
+            case SignatureAlgorithm.rsa_pss_pss_sha384:
+            case SignatureAlgorithm.rsa_pss_pss_sha512:
+            {
+                ValidateRsa_Pss_Pss(signatureAlgorithm);
+                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
+                return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
+            }
+
+            case SignatureAlgorithm.rsa_pss_rsae_sha256:
+            case SignatureAlgorithm.rsa_pss_rsae_sha384:
+            case SignatureAlgorithm.rsa_pss_rsae_sha512:
+            {
+                ValidateRsa_Pss_Rsae();
+                int signatureScheme = SignatureScheme.From(HashAlgorithm.Intrinsic, signatureAlgorithm);
+                return new BcTlsRsaPssVerifier(m_crypto, GetPubKeyRsa(), signatureScheme);
+            }
+
+            default:
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual Tls13Verifier CreateVerifier(int signatureScheme)
+        {
+            ValidateKeyUsage(KeyUsage.DigitalSignature);
+
+            switch (signatureScheme)
+            {
+            case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256:
+            case SignatureScheme.ecdsa_brainpoolP384r1tls13_sha384:
+            case SignatureScheme.ecdsa_brainpoolP512r1tls13_sha512:
+            case SignatureScheme.ecdsa_secp256r1_sha256:
+            case SignatureScheme.ecdsa_secp384r1_sha384:
+            case SignatureScheme.ecdsa_secp521r1_sha512:
+            case SignatureScheme.ecdsa_sha1:
+            {
+                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+                ISigner verifier = new DsaDigestSigner(new ECDsaSigner(), digest);
+                verifier.Init(false, GetPubKeyEC());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            case SignatureScheme.ed25519:
+            {
+                Ed25519Signer verifier = new Ed25519Signer();
+                verifier.Init(false, GetPubKeyEd25519());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            case SignatureScheme.ed448:
+            {
+                Ed448Signer verifier = new Ed448Signer(TlsUtilities.EmptyBytes);
+                verifier.Init(false, GetPubKeyEd448());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            case SignatureScheme.rsa_pkcs1_sha1:
+            case SignatureScheme.rsa_pkcs1_sha256:
+            case SignatureScheme.rsa_pkcs1_sha384:
+            case SignatureScheme.rsa_pkcs1_sha512:
+            {
+                ValidateRsa_Pkcs1();
+
+                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+                RsaDigestSigner verifier = new RsaDigestSigner(digest,
+                    TlsCryptoUtilities.GetOidForHash(cryptoHashAlgorithm));
+                verifier.Init(false, GetPubKeyRsa());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            case SignatureScheme.rsa_pss_pss_sha256:
+            case SignatureScheme.rsa_pss_pss_sha384:
+            case SignatureScheme.rsa_pss_pss_sha512:
+            {
+                ValidateRsa_Pss_Pss(SignatureScheme.GetSignatureAlgorithm(signatureScheme));
+
+                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+                PssSigner verifier = new PssSigner(new RsaEngine(), digest, digest.GetDigestSize());
+                verifier.Init(false, GetPubKeyRsa());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            case SignatureScheme.rsa_pss_rsae_sha256:
+            case SignatureScheme.rsa_pss_rsae_sha384:
+            case SignatureScheme.rsa_pss_rsae_sha512:
+            {
+                ValidateRsa_Pss_Rsae();
+
+                int cryptoHashAlgorithm = SignatureScheme.GetCryptoHashAlgorithm(signatureScheme);
+                IDigest digest = m_crypto.CreateDigest(cryptoHashAlgorithm);
+
+                PssSigner verifier = new PssSigner(new RsaEngine(), digest, digest.GetDigestSize());
+                verifier.Init(false, GetPubKeyRsa());
+
+                return new BcTls13Verifier(verifier);
+            }
+
+            // TODO[RFC 8998]
+            //case SignatureScheme.sm2sig_sm3:
+            //{
+            //    ParametersWithID parametersWithID = new ParametersWithID(GetPubKeyEC(),
+            //        Strings.ToByteArray("TLSv1.3+GM+Cipher+Suite"));
+    
+            //    SM2Signer verifier = new SM2Signer();
+            //    verifier.Init(false, parametersWithID);
+
+            //    return new BcTls13Verifier(verifier);
+            //}
+
+            default:
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual byte[] GetEncoded()
+        {
+            return m_keyInfo.GetEncoded(Asn1Encodable.Der);
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual byte[] GetExtension(DerObjectIdentifier extensionOid)
+        {
+            return null;
+        }
+
+        public virtual BigInteger SerialNumber => null;
+
+        public virtual string SigAlgOid => null;
+
+        public virtual Asn1Encodable GetSigAlgParams() => null;
+
+        /// <exception cref="IOException"/>
+        public virtual short GetLegacySignatureAlgorithm()
+        {
+            AsymmetricKeyParameter publicKey = GetPublicKey();
+            if (publicKey.IsPrivate)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            if (!SupportsKeyUsage(KeyUsage.DigitalSignature))
+                return -1;
+
+            /*
+             * RFC 5246 7.4.6. Client Certificate
+             */
+
+            /*
+             * RSA public key; the certificate MUST allow the key to be used for signing with the
+             * signature scheme and hash algorithm that will be employed in the certificate verify
+             * message.
+             */
+            if (publicKey is RsaKeyParameters)
+                return SignatureAlgorithm.rsa;
+
+            /*
+                * DSA public key; the certificate MUST allow the key to be used for signing with the
+                * hash algorithm that will be employed in the certificate verify message.
+                */
+            if (publicKey is DsaPublicKeyParameters)
+                return SignatureAlgorithm.dsa;
+
+            /*
+             * ECDSA-capable public key; the certificate MUST allow the key to be used for signing
+             * with the hash algorithm that will be employed in the certificate verify message; the
+             * public key MUST use a curve and point format supported by the server.
+             */
+            if (publicKey is ECPublicKeyParameters)
+            {
+                // TODO Check the curve and point format
+                return SignatureAlgorithm.ecdsa;
+            }
+
+            return -1;
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual DHPublicKeyParameters GetPubKeyDH()
+        {
+            try
+            {
+                return (DHPublicKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual DsaPublicKeyParameters GetPubKeyDss()
+        {
+            try
+            {
+                return (DsaPublicKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual ECPublicKeyParameters GetPubKeyEC()
+        {
+            try
+            {
+                return (ECPublicKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual Ed25519PublicKeyParameters GetPubKeyEd25519()
+        {
+            try
+            {
+                return (Ed25519PublicKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual Ed448PublicKeyParameters GetPubKeyEd448()
+        {
+            try
+            {
+                return (Ed448PublicKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual RsaKeyParameters GetPubKeyRsa()
+        {
+            try
+            {
+                return (RsaKeyParameters)GetPublicKey();
+            }
+            catch (InvalidCastException e)
+            {
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown, e);
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm)
+        {
+            return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.DigitalSignature);
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual bool SupportsSignatureAlgorithmCA(short signatureAlgorithm)
+        {
+            return SupportsSignatureAlgorithm(signatureAlgorithm, KeyUsage.KeyCertSign);
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual TlsCertificate CheckUsageInRole(int tlsCertificateRole)
+        {
+            switch (tlsCertificateRole)
+            {
+            case TlsCertificateRole.DH:
+            {
+                ValidateKeyUsage(KeyUsage.KeyAgreement);
+                this.m_pubKeyDH = GetPubKeyDH();
+                return this;
+            }
+            case TlsCertificateRole.ECDH:
+            {
+                ValidateKeyUsage(KeyUsage.KeyAgreement);
+                this.m_pubKeyEC = GetPubKeyEC();
+                return this;
+            }
+            }
+
+            throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+
+        /// <exception cref="IOException"/>
+        protected virtual AsymmetricKeyParameter GetPublicKey()
+        {
+            try
+            {
+                return PublicKeyFactory.CreateKey(m_keyInfo);
+            }
+            catch (Exception e)
+            {
+                throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e);
+            }
+        }
+
+        protected virtual bool SupportsKeyUsage(int keyUsageBits)
+        {
+            return true;
+        }
+
+        protected virtual bool SupportsRsa_Pkcs1()
+        {
+            AlgorithmIdentifier pubKeyAlgID = m_keyInfo.AlgorithmID;
+            return RsaUtilities.SupportsPkcs1(pubKeyAlgID);
+        }
+
+        protected virtual bool SupportsRsa_Pss_Pss(short signatureAlgorithm)
+        {
+            AlgorithmIdentifier pubKeyAlgID = m_keyInfo.AlgorithmID;
+            return RsaUtilities.SupportsPss_Pss(signatureAlgorithm, pubKeyAlgID);
+        }
+
+        protected virtual bool SupportsRsa_Pss_Rsae()
+        {
+            AlgorithmIdentifier pubKeyAlgID = m_keyInfo.AlgorithmID;
+            return RsaUtilities.SupportsPss_Rsae(pubKeyAlgID);
+        }
+
+        /// <exception cref="IOException"/>
+        protected virtual bool SupportsSignatureAlgorithm(short signatureAlgorithm, int keyUsage)
+        {
+            if (!SupportsKeyUsage(keyUsage))
+                return false;
+
+            AsymmetricKeyParameter publicKey = GetPublicKey();
+
+            switch (signatureAlgorithm)
+            {
+            case SignatureAlgorithm.rsa:
+                return SupportsRsa_Pkcs1()
+                    && publicKey is RsaKeyParameters;
+
+            case SignatureAlgorithm.dsa:
+                return publicKey is DsaPublicKeyParameters;
+
+            case SignatureAlgorithm.ecdsa:
+            case SignatureAlgorithm.ecdsa_brainpoolP256r1tls13_sha256:
+            case SignatureAlgorithm.ecdsa_brainpoolP384r1tls13_sha384:
+            case SignatureAlgorithm.ecdsa_brainpoolP512r1tls13_sha512:
+                return publicKey is ECPublicKeyParameters;
+
+            case SignatureAlgorithm.ed25519:
+                return publicKey is Ed25519PublicKeyParameters;
+
+            case SignatureAlgorithm.ed448:
+                return publicKey is Ed448PublicKeyParameters;
+
+            case SignatureAlgorithm.rsa_pss_rsae_sha256:
+            case SignatureAlgorithm.rsa_pss_rsae_sha384:
+            case SignatureAlgorithm.rsa_pss_rsae_sha512:
+                return SupportsRsa_Pss_Rsae()
+                    && publicKey is RsaKeyParameters;
+
+            case SignatureAlgorithm.rsa_pss_pss_sha256:
+            case SignatureAlgorithm.rsa_pss_pss_sha384:
+            case SignatureAlgorithm.rsa_pss_pss_sha512:
+                return SupportsRsa_Pss_Pss(signatureAlgorithm)
+                    && publicKey is RsaKeyParameters;
+
+            default:
+                return false;
+            }
+        }
+
+        /// <exception cref="IOException"/>
+        public virtual void ValidateKeyUsage(int keyUsageBits)
+        {
+            if (!SupportsKeyUsage(keyUsageBits))
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+
+        /// <exception cref="IOException"/>
+        protected virtual void ValidateRsa_Pkcs1()
+        {
+            if (!SupportsRsa_Pkcs1())
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+
+        /// <exception cref="IOException"/>
+        protected virtual void ValidateRsa_Pss_Pss(short signatureAlgorithm)
+        {
+            if (!SupportsRsa_Pss_Pss(signatureAlgorithm))
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+
+        /// <exception cref="IOException"/>
+        protected virtual void ValidateRsa_Pss_Rsae()
+        {
+            if (!SupportsRsa_Pss_Rsae())
+                throw new TlsFatalAlert(AlertDescription.certificate_unknown);
+        }
+    }
+}
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)
+                    {
+                    }
+                }
+            }
+        }
+    }
+}