Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/quite/humla-spongycastle.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2014-04-15 11:48:48 +0400
committerPeter Dettman <peter.dettman@bouncycastle.org>2014-04-15 11:48:48 +0400
commit2f6c03f41a3f4b338083f77ab6896811f10f91a7 (patch)
tree72050e92f1847300f2e2271264f53d02f6357c2b /core/src
parent40100ccac6fd0aa5069abe65fa5c159cdd38c7b4 (diff)
A new suite of TLS tests, intially covering client authentication
use-cases for TLS 1.1
Diffstat (limited to 'core/src')
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java60
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java54
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java147
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java253
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java91
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java219
-rw-r--r--core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java65
7 files changed, 889 insertions, 0 deletions
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java
new file mode 100644
index 00000000..e5f47706
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkInputStream.java
@@ -0,0 +1,60 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Tracks and enforces close() calls, without closing the underlying InputStream
+ */
+class NetworkInputStream extends FilterInputStream
+{
+ boolean closed = false;
+
+ public NetworkInputStream(InputStream input)
+ {
+ super(input);
+ }
+
+ synchronized boolean isClosed()
+ {
+ return closed;
+ }
+
+ public int available() throws IOException
+ {
+ checkNotClosed();
+ return in.available();
+ }
+
+ public synchronized void close() throws IOException
+ {
+ closed = true;
+ }
+
+ public int read() throws IOException
+ {
+ checkNotClosed();
+ return in.read();
+ }
+
+ public int read(byte[] b) throws IOException
+ {
+ checkNotClosed();
+ return in.read(b);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ checkNotClosed();
+ return in.read(b, off, len);
+ }
+
+ protected synchronized void checkNotClosed() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("NetworkInputStream closed");
+ }
+ }
+} \ No newline at end of file
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java
new file mode 100644
index 00000000..a11694b7
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/NetworkOutputStream.java
@@ -0,0 +1,54 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Tracks and enforces close() calls, without closing the underlying OutputStream
+ */
+class NetworkOutputStream extends FilterOutputStream
+{
+ boolean closed = false;
+
+ public NetworkOutputStream(OutputStream output)
+ {
+ super(output);
+ }
+
+ synchronized boolean isClosed()
+ {
+ return closed;
+ }
+
+ public synchronized void close() throws IOException
+ {
+ closed = true;
+ }
+
+ public void write(int b) throws IOException
+ {
+ checkNotClosed();
+ out.write(b);
+ }
+
+ public void write(byte[] b) throws IOException
+ {
+ checkNotClosed();
+ out.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException
+ {
+ checkNotClosed();
+ out.write(b, off, len);
+ }
+
+ protected synchronized void checkNotClosed() throws IOException
+ {
+ if (closed)
+ {
+ throw new IOException("NetworkOutputStream closed");
+ }
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java
new file mode 100644
index 00000000..801ce285
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestCase.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+
+import org.bouncycastle.crypto.tls.TlsClientProtocol;
+import org.bouncycastle.crypto.tls.TlsServerProtocol;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class TlsTestCase extends TestCase
+{
+ protected final TlsTestConfig config;
+
+ public TlsTestCase(TlsTestConfig config, String name)
+ {
+ this.config = config;
+
+ setName(name);
+ }
+
+ protected void runTest() throws Throwable
+ {
+ TlsTestClientImpl clientImpl = new TlsTestClientImpl(config);
+ TlsTestServerImpl serverImpl = new TlsTestServerImpl(config);
+
+ SecureRandom secureRandom = new SecureRandom();
+
+ PipedInputStream clientRead = new PipedInputStream();
+ PipedInputStream serverRead = new PipedInputStream();
+ PipedOutputStream clientWrite = new PipedOutputStream(serverRead);
+ PipedOutputStream serverWrite = new PipedOutputStream(clientRead);
+
+ NetworkInputStream clientNetIn = new NetworkInputStream(clientRead);
+ NetworkInputStream serverNetIn = new NetworkInputStream(serverRead);
+ NetworkOutputStream clientNetOut = new NetworkOutputStream(clientWrite);
+ NetworkOutputStream serverNetOut = new NetworkOutputStream(serverWrite);
+
+ TlsClientProtocol clientProtocol = new TlsClientProtocol(clientNetIn, clientNetOut, secureRandom);
+ TlsServerProtocol serverProtocol = new TlsServerProtocol(serverNetIn, serverNetOut, secureRandom);
+
+ ServerThread serverThread = new ServerThread(serverProtocol, serverImpl);
+ serverThread.start();
+
+ Exception caught = null;
+ try
+ {
+ clientProtocol.connect(clientImpl);
+
+ // 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];
+ secureRandom.nextBytes(data);
+
+ OutputStream output = clientProtocol.getOutputStream();
+ output.write(data);
+
+ byte[] echo = new byte[data.length];
+ int count = Streams.readFully(clientProtocol.getInputStream(), echo);
+
+ assertEquals(count, data.length);
+ assertTrue(Arrays.areEqual(data, echo));
+
+ output.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ }
+
+ serverThread.allowExit();
+ serverThread.join();
+
+ assertTrue("Client InputStream not closed", clientNetIn.isClosed());
+ assertTrue("Client OutputStream not closed", clientNetOut.isClosed());
+ assertTrue("Server InputStream not closed", serverNetIn.isClosed());
+ assertTrue("Server OutputStream not closed", serverNetOut.isClosed());
+
+ assertEquals("Client fatal alert connection end", config.expectFatalAlertConnectionEnd, clientImpl.firstFatalAlertConnectionEnd);
+ assertEquals("Server fatal alert connection end", config.expectFatalAlertConnectionEnd, serverImpl.firstFatalAlertConnectionEnd);
+
+ assertEquals("Client fatal alert description", config.expectFatalAlertDescription, clientImpl.firstFatalAlertDescription);
+ assertEquals("Server fatal alert description", config.expectFatalAlertDescription, serverImpl.firstFatalAlertDescription);
+
+ if (config.expectFatalAlertConnectionEnd == -1)
+ {
+ assertNull("Unexpected client exception", caught);
+ assertNull("Unexpected server exception", serverThread.caught);
+ }
+ }
+
+ class ServerThread extends Thread
+ {
+ protected final TlsServerProtocol serverProtocol;
+ protected final TlsTestServerImpl serverImpl;
+
+ boolean canExit = false;
+ Exception caught = null;
+
+ ServerThread(TlsServerProtocol serverProtocol, TlsTestServerImpl serverImpl)
+ {
+ this.serverProtocol = serverProtocol;
+ this.serverImpl = serverImpl;
+ }
+
+ synchronized void allowExit()
+ {
+ canExit = true;
+ this.notifyAll();
+ }
+
+ public void run()
+ {
+ try
+ {
+ serverProtocol.accept(serverImpl);
+ Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream());
+ serverProtocol.close();
+ }
+ catch (Exception e)
+ {
+ caught = e;
+ }
+
+ waitExit();
+ }
+
+ protected synchronized void waitExit()
+ {
+ while (!canExit)
+ {
+ try
+ {
+ this.wait();
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java
new file mode 100644
index 00000000..4f5be8e3
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestClientImpl.java
@@ -0,0 +1,253 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.DefaultTlsClient;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsAuthentication;
+import org.bouncycastle.crypto.tls.TlsCredentials;
+import org.bouncycastle.crypto.tls.TlsFatalAlert;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.util.Arrays;
+
+class TlsTestClientImpl
+ extends DefaultTlsClient
+{
+ protected final TlsTestConfig config;
+
+ protected int firstFatalAlertConnectionEnd = -1;
+ protected short firstFatalAlertDescription = -1;
+
+ TlsTestClientImpl(TlsTestConfig config)
+ {
+ this.config = config;
+ }
+
+ int getFirstFatalAlertConnectionEnd()
+ {
+ return firstFatalAlertConnectionEnd;
+ }
+
+ short getFirstFatalAlertDescription()
+ {
+ return firstFatalAlertDescription;
+ }
+
+ public ProtocolVersion getClientVersion()
+ {
+ if (config.clientOfferVersion != null)
+ {
+ return config.clientOfferVersion;
+ }
+
+ return super.getClientVersion();
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS client received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+ }
+
+ public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException
+ {
+ super.notifyServerVersion(serverVersion);
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS client negotiated " + serverVersion);
+ }
+ }
+
+ public TlsAuthentication getAuthentication()
+ throws IOException
+ {
+ return new TlsAuthentication()
+ {
+ public void notifyServerCertificate(org.bouncycastle.crypto.tls.Certificate serverCertificate)
+ throws IOException
+ {
+ boolean isEmpty = serverCertificate == null || serverCertificate.isEmpty();
+
+ Certificate[] chain = serverCertificate.getCertificateList();
+
+ if (isEmpty || !chain[0].equals(TlsTestUtils.loadCertificateResource("x509-server.pem")))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS client received server certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ }
+
+ public TlsCredentials getClientCredentials(CertificateRequest certificateRequest)
+ throws IOException
+ {
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ {
+ throw new IllegalStateException();
+ }
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE)
+ {
+ return null;
+ }
+
+ short[] certificateTypes = certificateRequest.getCertificateTypes();
+ if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign))
+ {
+ return null;
+ }
+
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = certificateRequest.getSupportedSignatureAlgorithms();
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ final TlsSignerCredentials signerCredentials = TlsTestUtils.loadSignerCredentials(context, new String[]{
+ "x509-client.pem", "x509-ca.pem" }, "x509-client-key.pem", signatureAndHashAlgorithm);
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_VALID)
+ {
+ return signerCredentials;
+ }
+
+ return new TlsSignerCredentials()
+ {
+ public byte[] generateCertificateSignature(byte[] hash) throws IOException
+ {
+ byte[] sig = signerCredentials.generateCertificateSignature(hash);
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_VERIFY)
+ {
+ sig = corruptBit(sig);
+ }
+
+ return sig;
+ }
+
+ public org.bouncycastle.crypto.tls.Certificate getCertificate()
+ {
+ org.bouncycastle.crypto.tls.Certificate cert = signerCredentials.getCertificate();
+
+ if (config.clientAuth == TlsTestConfig.CLIENT_AUTH_INVALID_CERT)
+ {
+ cert = corruptCertificate(cert);
+ }
+
+ return cert;
+ }
+
+ public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+ {
+ return signerCredentials.getSignatureAndHashAlgorithm();
+ }
+ };
+ }
+ };
+ }
+
+ protected org.bouncycastle.crypto.tls.Certificate corruptCertificate(org.bouncycastle.crypto.tls.Certificate cert)
+ {
+ Certificate[] certList = cert.getCertificateList();
+ certList[0] = corruptCertificateSignature(certList[0]);
+ return new org.bouncycastle.crypto.tls.Certificate(certList);
+ }
+
+ protected Certificate corruptCertificateSignature(Certificate cert)
+ {
+ ASN1EncodableVector v = new ASN1EncodableVector();
+ v.add(cert.getTBSCertificate());
+ v.add(cert.getSignatureAlgorithm());
+ v.add(corruptBitString(cert.getSignature()));
+
+ return Certificate.getInstance(new DERSequence(v));
+ }
+
+ protected DERBitString corruptBitString(DERBitString bs)
+ {
+ return new DERBitString(corruptBit(bs.getBytes()));
+ }
+
+ protected byte[] corruptBit(byte[] bs)
+ {
+ bs = Arrays.clone(bs);
+
+ // Flip a random bit
+ int bit = context.getSecureRandom().nextInt(bs.length << 3);
+ bs[bit >>> 3] ^= (1 << (bit & 7));
+
+ return bs;
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java
new file mode 100644
index 00000000..3784bc23
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestConfig.java
@@ -0,0 +1,91 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+
+public class TlsTestConfig
+{
+ public static final boolean DEBUG = false;
+
+ /**
+ * Client does not authenticate, ignores any certificate request
+ */
+ public static final int CLIENT_AUTH_NONE = 0;
+
+ /**
+ * Client will authenticate if it receives a certificate request
+ */
+ public static final int CLIENT_AUTH_VALID = 1;
+
+ /**
+ * Client will authenticate if it receives a certificate request, with an invalid certificate
+ */
+ public static final int CLIENT_AUTH_INVALID_CERT = 2;
+
+ /**
+ * Client will authenticate if it receives a certificate request, with an invalid CertificateVerify signature
+ */
+ public static final int CLIENT_AUTH_INVALID_VERIFY = 3;
+
+ /**
+ * Server will not request a client certificate
+ */
+ public static final int SERVER_CERT_REQ_NONE = 0;
+
+ /**
+ * Server will request a client certificate but receiving one is optional
+ */
+ public static final int SERVER_CERT_REQ_OPTIONAL = 1;
+
+ /**
+ * Server will request a client certificate and receiving one is mandatory
+ */
+ public static final int SERVER_CERT_REQ_MANDATORY = 2;
+
+ /**
+ * Configures the client authentication behaviour of the test client. Use CLIENT_AUTH_* constants.
+ */
+ public int clientAuth = CLIENT_AUTH_VALID;
+
+ /**
+ * Configures the protocol version the client will offer. If null, uses the library's default.
+ */
+ public ProtocolVersion clientOfferVersion = null;
+
+ /**
+ * Configures whether the test server will send a certificate request.
+ */
+ public int serverCertReq = SERVER_CERT_REQ_OPTIONAL;
+
+ /**
+ * Configures the maximum protocol version the server will accept. If null, uses the library's default.
+ */
+ public ProtocolVersion serverMaximumVersion = null;
+
+ /**
+ * Configures the minimum protocol version the server will accept. If null, uses the library's default.
+ */
+ public ProtocolVersion serverMinimumVersion = null;
+
+ /**
+ * Configures the connection end that a fatal alert is expected to be raised. Use ConnectionEnd.* constants.
+ */
+ public int expectFatalAlertConnectionEnd = -1;
+
+ /**
+ * Configures the type of fatal alert expected to be raised. Use AlertDescription.* constants.
+ */
+ public short expectFatalAlertDescription = -1;
+
+ public void expectClientFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.client;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+
+ public void expectServerFatalAlert(short alertDescription)
+ {
+ this.expectFatalAlertConnectionEnd = ConnectionEnd.server;
+ this.expectFatalAlertDescription = alertDescription;
+ }
+}
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java
new file mode 100644
index 00000000..e9d9aafc
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestServerImpl.java
@@ -0,0 +1,219 @@
+package org.bouncycastle.crypto.tls.test;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.crypto.tls.AlertDescription;
+import org.bouncycastle.crypto.tls.AlertLevel;
+import org.bouncycastle.crypto.tls.CertificateRequest;
+import org.bouncycastle.crypto.tls.ClientCertificateType;
+import org.bouncycastle.crypto.tls.ConnectionEnd;
+import org.bouncycastle.crypto.tls.DefaultTlsServer;
+import org.bouncycastle.crypto.tls.HashAlgorithm;
+import org.bouncycastle.crypto.tls.ProtocolVersion;
+import org.bouncycastle.crypto.tls.SignatureAlgorithm;
+import org.bouncycastle.crypto.tls.SignatureAndHashAlgorithm;
+import org.bouncycastle.crypto.tls.TlsEncryptionCredentials;
+import org.bouncycastle.crypto.tls.TlsFatalAlert;
+import org.bouncycastle.crypto.tls.TlsSignerCredentials;
+import org.bouncycastle.crypto.tls.TlsUtils;
+
+class TlsTestServerImpl
+ extends DefaultTlsServer
+{
+ protected final TlsTestConfig config;
+
+ protected int firstFatalAlertConnectionEnd = -1;
+ protected short firstFatalAlertDescription = -1;
+
+ TlsTestServerImpl(TlsTestConfig config)
+ {
+ this.config = config;
+ }
+
+ int getFirstFatalAlertConnectionEnd()
+ {
+ return firstFatalAlertConnectionEnd;
+ }
+
+ short getFirstFatalAlertDescription()
+ {
+ return firstFatalAlertDescription;
+ }
+
+ protected ProtocolVersion getMaximumVersion()
+ {
+ if (config.serverMaximumVersion != null)
+ {
+ return config.serverMaximumVersion;
+ }
+
+ return super.getMaximumVersion();
+ }
+
+ protected ProtocolVersion getMinimumVersion()
+ {
+ if (config.serverMinimumVersion != null)
+ {
+ return config.serverMinimumVersion;
+ }
+
+ return super.getMinimumVersion();
+ }
+
+ public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.server;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server raised alert (AlertLevel." + alertLevel + ", AlertDescription." + alertDescription
+ + ")");
+ if (message != null)
+ {
+ out.println("> " + message);
+ }
+ if (cause != null)
+ {
+ cause.printStackTrace(out);
+ }
+ }
+ }
+
+ public void notifyAlertReceived(short alertLevel, short alertDescription)
+ {
+ if (alertLevel == AlertLevel.fatal && firstFatalAlertConnectionEnd == -1)
+ {
+ firstFatalAlertConnectionEnd = ConnectionEnd.client;
+ firstFatalAlertDescription = alertDescription;
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out;
+ out.println("TLS server received alert (AlertLevel." + alertLevel + ", AlertDescription."
+ + alertDescription + ")");
+ }
+ }
+
+ public ProtocolVersion getServerVersion() throws IOException
+ {
+ ProtocolVersion serverVersion = super.getServerVersion();
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS server negotiated " + serverVersion);
+ }
+
+ return serverVersion;
+ }
+
+ public CertificateRequest getCertificateRequest() throws IOException
+ {
+ if (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_NONE)
+ {
+ return null;
+ }
+
+ Vector serverSigAlgs = null;
+
+ if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(serverVersion))
+ {
+ short[] hashAlgorithms = new short[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+ HashAlgorithm.sha224, HashAlgorithm.sha1 };
+ short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa };
+
+ serverSigAlgs = new Vector();
+ for (int i = 0; i < hashAlgorithms.length; ++i)
+ {
+ for (int j = 0; j < signatureAlgorithms.length; ++j)
+ {
+ serverSigAlgs.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i],
+ signatureAlgorithms[j]));
+ }
+ }
+ }
+
+ return new CertificateRequest(new short[]{ ClientCertificateType.rsa_sign }, serverSigAlgs, null);
+ }
+
+ public void notifyClientCertificate(org.bouncycastle.crypto.tls.Certificate clientCertificate)
+ throws IOException
+ {
+ boolean isEmpty = (clientCertificate == null || clientCertificate.isEmpty());
+
+ if (isEmpty != (config.clientAuth == TlsTestConfig.CLIENT_AUTH_NONE))
+ {
+ throw new IllegalStateException();
+ }
+ if (isEmpty && (config.serverCertReq == TlsTestConfig.SERVER_CERT_REQ_MANDATORY))
+ {
+ throw new TlsFatalAlert(AlertDescription.handshake_failure);
+ }
+
+ Certificate[] chain = clientCertificate.getCertificateList();
+
+ if (!isEmpty && !chain[0].equals(TlsTestUtils.loadCertificateResource("x509-client.pem")))
+ {
+ throw new TlsFatalAlert(AlertDescription.bad_certificate);
+ }
+
+ if (TlsTestConfig.DEBUG)
+ {
+ System.out.println("TLS server received client certificate chain of length " + chain.length);
+ for (int i = 0; i != chain.length; i++)
+ {
+ Certificate entry = chain[i];
+ // TODO Create fingerprint based on certificate signature algorithm digest
+ System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " ("
+ + entry.getSubject() + ")");
+ }
+ }
+ }
+
+ protected TlsEncryptionCredentials getRSAEncryptionCredentials()
+ throws IOException
+ {
+ return TlsTestUtils.loadEncryptionCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem");
+ }
+
+ protected TlsSignerCredentials getRSASignerCredentials()
+ throws IOException
+ {
+ /*
+ * TODO Note that this code fails to provide default value for the client supported
+ * algorithms if it wasn't sent.
+ */
+ SignatureAndHashAlgorithm signatureAndHashAlgorithm = null;
+ Vector sigAlgs = supportedSignatureAlgorithms;
+ if (sigAlgs != null)
+ {
+ for (int i = 0; i < sigAlgs.size(); ++i)
+ {
+ SignatureAndHashAlgorithm sigAlg = (SignatureAndHashAlgorithm)
+ sigAlgs.elementAt(i);
+ if (sigAlg.getSignature() == SignatureAlgorithm.rsa)
+ {
+ signatureAndHashAlgorithm = sigAlg;
+ break;
+ }
+ }
+
+ if (signatureAndHashAlgorithm == null)
+ {
+ return null;
+ }
+ }
+
+ return TlsTestUtils.loadSignerCredentials(context, new String[]{"x509-server.pem", "x509-ca.pem"},
+ "x509-server-key.pem", signatureAndHashAlgorithm);
+ }
+} \ No newline at end of file
diff --git a/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java
new file mode 100644
index 00000000..6762a702
--- /dev/null
+++ b/core/src/test/java/org/bouncycastle/crypto/tls/test/TlsTestSuite.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.crypto.tls.test;
+
+import org.bouncycastle.crypto.tls.AlertDescription;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class TlsTestSuite extends TestSuite
+{
+ // Make the access to constants less verbose
+ static abstract class C extends TlsTestConfig {}
+
+ public static Test suite()
+ {
+ TlsTestSuite testSuite = new TlsTestSuite();
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientAuth = C.CLIENT_AUTH_INVALID_VERIFY;
+ c.expectServerFatalAlert(AlertDescription.decrypt_error);
+
+ testSuite.addTest(new TlsTestCase(c, "BadCertificateVerify"));
+ }
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientAuth = C.CLIENT_AUTH_INVALID_CERT;
+ c.expectServerFatalAlert(AlertDescription.bad_certificate);
+
+ testSuite.addTest(new TlsTestCase(c, "BadClientCertificate"));
+ }
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_MANDATORY;
+ c.expectServerFatalAlert(AlertDescription.handshake_failure);
+
+ testSuite.addTest(new TlsTestCase(c, "BadMandatoryCertReqDeclined"));
+ }
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+
+ testSuite.addTest(new TlsTestCase(c, "GoodDefault"));
+ }
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+
+ testSuite.addTest(new TlsTestCase(c, "GoodOptionalCertReqDeclined"));
+ }
+
+ {
+ TlsTestConfig c = new TlsTestConfig();
+ c.clientAuth = C.CLIENT_AUTH_NONE;
+ c.serverCertReq = C.SERVER_CERT_REQ_NONE;
+
+ testSuite.addTest(new TlsTestCase(c, "GoodServerOnlyAuthentication"));
+ }
+
+ return testSuite;
+ }
+}