diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-09-24 18:09:39 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2022-09-24 18:09:39 +0700 |
commit | ed081e3fe9634391ac496bd79193a7d00dfa6f07 (patch) | |
tree | ecc1382c5f3f9a17c6b21ebf8026076f6a32f15f | |
parent | Cmp updates (diff) | |
download | BouncyCastle.NET-ed25519-ed081e3fe9634391ac496bd79193a7d00dfa6f07.tar.xz |
(D)TLS: RFC 7250 Raw Public Keys
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) + { + } + } + } + } + } +} |