diff --git a/crypto/test/src/crypto/tls/test/MockTlsClient.cs b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
new file mode 100644
index 000000000..c22e98367
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/MockTlsClient.cs
@@ -0,0 +1,158 @@
+using System;
+using System.Collections;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ internal class MockTlsClient
+ : DefaultTlsClient
+ {
+ internal TlsSession mSession;
+
+ internal MockTlsClient(TlsSession session)
+ {
+ this.mSession = session;
+ }
+
+ public override TlsSession GetSessionToResume()
+ {
+ return this.mSession;
+ }
+
+ public override void NotifyAlertRaised(byte alertLevel, byte alertDescription, string message, Exception cause)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ output.WriteLine("> " + message);
+ }
+ if (cause != null)
+ {
+ output.WriteLine(cause.StackTrace);
+ }
+ }
+
+ public override void NotifyAlertReceived(byte alertLevel, byte alertDescription)
+ {
+ TextWriter output = (alertLevel == AlertLevel.fatal) ? Console.Error : Console.Out;
+ output.WriteLine("TLS client received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+
+ public override int[] GetCipherSuites()
+ {
+ return Arrays.Concatenate(base.GetCipherSuites(),
+ new int[]
+ {
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1,
+ CipherSuite.TLS_RSA_WITH_SALSA20_SHA1,
+ });
+ }
+
+ public override IDictionary GetClientExtensions()
+ {
+ IDictionary clientExtensions = TlsExtensionsUtilities.EnsureExtensionsInitialised(base.GetClientExtensions());
+ TlsExtensionsUtilities.AddEncryptThenMacExtension(clientExtensions);
+ TlsExtensionsUtilities.AddMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9);
+ TlsExtensionsUtilities.AddTruncatedHMacExtension(clientExtensions);
+ return clientExtensions;
+ }
+
+ public override void NotifyServerVersion(ProtocolVersion serverVersion)
+ {
+ base.NotifyServerVersion(serverVersion);
+
+ Console.WriteLine("TLS client negotiated " + serverVersion);
+ }
+
+ public override TlsAuthentication GetAuthentication()
+ {
+ return new MyTlsAuthentication(mContext);
+ }
+
+ public override void NotifyHandshakeComplete()
+ {
+ base.NotifyHandshakeComplete();
+
+ TlsSession newSession = mContext.ResumableSession;
+ if (newSession != null)
+ {
+ byte[] newSessionID = newSession.SessionID;
+ string hex = Hex.ToHexString(newSessionID);
+
+ if (this.mSession != null && Arrays.AreEqual(this.mSession.SessionID, newSessionID))
+ {
+ Console.WriteLine("Resumed session: " + hex);
+ }
+ else
+ {
+ Console.WriteLine("Established session: " + hex);
+ }
+
+ this.mSession = newSession;
+ }
+ }
+
+ internal class MyTlsAuthentication
+ : TlsAuthentication
+ {
+ private readonly TlsContext mContext;
+
+ internal MyTlsAuthentication(TlsContext context)
+ {
+ this.mContext = context;
+ }
+
+ public virtual void NotifyServerCertificate(Certificate serverCertificate)
+ {
+ X509CertificateStructure[] chain = serverCertificate.GetCertificateList();
+ Console.WriteLine("TLS client received server certificate chain of length " + chain.Length);
+ for (int i = 0; i != chain.Length; i++)
+ {
+ X509CertificateStructure entry = chain[i];
+ // TODO Create Fingerprint based on certificate signature algorithm digest
+ Console.WriteLine(" Fingerprint:SHA-256 " + TlsTestUtilities.Fingerprint(entry) + " ("
+ + entry.Subject + ")");
+ }
+ }
+
+ public virtual TlsCredentials GetClientCredentials(CertificateRequest certificateRequest)
+ {
+ byte[] certificateTypes = certificateRequest.CertificateTypes;
+ if (certificateTypes == null || !Arrays.Contains(certificateTypes, ClientCertificateType.rsa_sign))
+ return null;
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ IList sigAlgs = certificateRequest.SupportedSignatureAlgorithms;
+ if (sigAlgs != null)
+ {
+ foreach (SignatureAndHashAlgorithm sigAlg in sigAlgs)
+ {
+ if (sigAlg.Signature == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtilities.LoadSignerCredentials(mContext, new string[] { "x509-client.pem", "x509-ca.pem" },
+ "x509-client-key.pem", signatureAndHashAlgorithm);
+ }
+ };
+ }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsClientTest.cs b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
new file mode 100644
index 000000000..e68f1934c
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsClientTest.cs
@@ -0,0 +1,66 @@
+using System;
+using System.IO;
+using System.Net.Sockets;
+using System.Text;
+
+using Org.BouncyCastle.Security;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ /**
+ * A simple test designed to conduct a TLS handshake with an external TLS server.
+ * <p/>
+ * Please refer to GnuTLSSetup.html or OpenSSLSetup.html (under 'docs'), and x509-*.pem files in
+ * this package (under 'src/test/resources') for help configuring an external TLS server.
+ */
+ public class TlsClientTest
+ {
+ private static readonly SecureRandom secureRandom = new SecureRandom();
+
+ public static void Main(string[] args)
+ {
+ string hostname = "localhost";
+ int port = 5556;
+
+ long time1 = DateTime.UtcNow.Ticks;
+
+ MockTlsClient client = new MockTlsClient(null);
+ TlsClientProtocol protocol = OpenTlsConnection(hostname, port, client);
+ protocol.Close();
+
+ long time2 = DateTime.UtcNow.Ticks;
+ Console.WriteLine("Elapsed 1: " + (time2 - time1)/TimeSpan.TicksPerMillisecond + "ms");
+
+ client = new MockTlsClient(client.GetSessionToResume());
+ protocol = OpenTlsConnection(hostname, port, client);
+
+ long time3 = DateTime.UtcNow.Ticks;
+ Console.WriteLine("Elapsed 2: " + (time3 - time2)/TimeSpan.TicksPerMillisecond + "ms");
+
+ byte[] req = Encoding.UTF8.GetBytes("GET / HTTP/1.1\r\n\r\n");
+
+ Stream tlsStream = protocol.Stream;
+ tlsStream.Write(req, 0, req.Length);
+ tlsStream.Flush();
+
+ StreamReader reader = new StreamReader(tlsStream);
+
+ String line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ Console.WriteLine(">>> " + line);
+ }
+
+ protocol.Close();
+ }
+
+ internal static TlsClientProtocol OpenTlsConnection(string hostname, int port, TlsClient client)
+ {
+ TcpClient tcp = new TcpClient(hostname, port);
+
+ TlsClientProtocol protocol = new TlsClientProtocol(tcp.GetStream(), secureRandom);
+ protocol.Connect(client);
+ return protocol;
+ }
+ }
+}
diff --git a/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
new file mode 100644
index 000000000..85a47d5e9
--- /dev/null
+++ b/crypto/test/src/crypto/tls/test/TlsTestUtilities.cs
@@ -0,0 +1,137 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto.Digests;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO.Pem;
+using Org.BouncyCastle.Utilities.Test;
+
+namespace Org.BouncyCastle.Crypto.Tls.Test
+{
+ public abstract class TlsTestUtilities
+ {
+ internal static readonly byte[] RsaCertData = Base64
+ .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA2MDIwNVoXDTEzMDIyNT"
+ + "A2MDM0NVowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCBSAwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAHU55Ncz"
+ + "eglREcTg54YLUlGWu2WOYWhit/iM1eeq8Kivro7q98eW52jTuMI3CI5ulqd0hYzshQKQaZ5GDzErMyM=");
+
+ internal static readonly byte[] DudRsaCertData = Base64
+ .Decode("MIICUzCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEBhMCQVUxKDAmBgNVBAoMH1RoZSBMZWdpb2"
+ + "4gb2YgdGhlIEJvdW5jeSBDYXN0bGUxEjAQBgNVBAcMCU1lbGJvdXJuZTERMA8GA1UECAwIVmljdG9yaWExLzAtBgkq"
+ + "hkiG9w0BCQEWIGZlZWRiYWNrLWNyeXB0b0Bib3VuY3ljYXN0bGUub3JnMB4XDTEzMDIyNTA1NDcyOFoXDTEzMDIyNT"
+ + "A1NDkwOFowgY8xCzAJBgNVBAYTAkFVMSgwJgYDVQQKDB9UaGUgTGVnaW9uIG9mIHRoZSBCb3VuY3kgQ2FzdGxlMRIw"
+ + "EAYDVQQHDAlNZWxib3VybmUxETAPBgNVBAgMCFZpY3RvcmlhMS8wLQYJKoZIhvcNAQkBFiBmZWVkYmFjay1jcnlwdG"
+ + "9AYm91bmN5Y2FzdGxlLm9yZzBaMA0GCSqGSIb3DQEBAQUAA0kAMEYCQQC0p+RhcFdPFqlwgrIr5YtqKmKXmEGb4Shy"
+ + "pL26Ymz66ZAPdqv7EhOdzl3lZWT6srZUMWWgQMYGiHQg4z2R7X7XAgERo0QwQjAOBgNVHQ8BAf8EBAMCAAEwEgYDVR"
+ + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS"
+ + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0=");
+
+ internal static string Fingerprint(X509CertificateStructure c)
+ {
+ byte[] der = c.GetEncoded();
+ byte[] sha1 = Sha256DigestOf(der);
+ byte[] hexBytes = Hex.Encode(sha1);
+ string hex = Platform.ToUpperInvariant(Encoding.ASCII.GetString(hexBytes));
+
+ StringBuilder fp = new StringBuilder();
+ int i = 0;
+ fp.Append(hex.Substring(i, 2));
+ while ((i += 2) < hex.Length)
+ {
+ fp.Append(':');
+ fp.Append(hex.Substring(i, 2));
+ }
+ return fp.ToString();
+ }
+
+ internal static byte[] Sha256DigestOf(byte[] input)
+ {
+ return DigestUtilities.CalculateDigest("SHA256", input);
+ }
+
+ internal static TlsAgreementCredentials LoadAgreementCredentials(TlsContext context,
+ string[] certResources, string keyResource)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsAgreementCredentials(certificate, privateKey);
+ }
+
+ internal static TlsEncryptionCredentials LoadEncryptionCredentials(TlsContext context,
+ string[] certResources, string keyResource)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsEncryptionCredentials(context, certificate, privateKey);
+ }
+
+ internal static TlsSignerCredentials LoadSignerCredentials(TlsContext context, string[] certResources,
+ string keyResource, SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+ {
+ Certificate certificate = LoadCertificateChain(certResources);
+ AsymmetricKeyParameter privateKey = LoadPrivateKeyResource(keyResource);
+
+ return new DefaultTlsSignerCredentials(context, certificate, privateKey, signatureAndHashAlgorithm);
+ }
+
+ internal static Certificate LoadCertificateChain(string[] resources)
+ {
+ X509CertificateStructure[] chain = new X509CertificateStructure[resources.Length];
+ for (int i = 0; i < resources.Length; ++i)
+ {
+ chain[i] = LoadCertificateResource(resources[i]);
+ }
+ return new Certificate(chain);
+ }
+
+ internal static X509CertificateStructure LoadCertificateResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("CERTIFICATE"))
+ {
+ return X509CertificateStructure.GetInstance(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid certificate", "resource");
+ }
+
+ internal static AsymmetricKeyParameter LoadPrivateKeyResource(string resource)
+ {
+ PemObject pem = LoadPemResource(resource);
+ if (pem.Type.EndsWith("RSA PRIVATE KEY"))
+ {
+ RsaPrivateKeyStructure rsa = RsaPrivateKeyStructure.GetInstance(pem.Content);
+ return new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent,
+ rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
+ rsa.Exponent2, rsa.Coefficient);
+ }
+ if (pem.Type.EndsWith("PRIVATE KEY"))
+ {
+ return PrivateKeyFactory.CreateKey(pem.Content);
+ }
+ throw new ArgumentException("doesn't specify a valid private key", "resource");
+ }
+
+ internal static PemObject LoadPemResource(string resource)
+ {
+ Stream s = SimpleTest.GetTestDataAsStream("tls." + resource);
+ PemReader p = new PemReader(new StreamReader(s));
+ PemObject o = p.ReadPemObject();
+ p.Reader.Close();
+ return o;
+ }
+ }
+}
|