summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src')
-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
15 files changed, 745 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);
+        }
+    }
+}