diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-04-15 11:48:48 +0400 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2014-04-15 11:48:48 +0400 |
commit | 2f6c03f41a3f4b338083f77ab6896811f10f91a7 (patch) | |
tree | 72050e92f1847300f2e2271264f53d02f6357c2b /core/src | |
parent | 40100ccac6fd0aa5069abe65fa5c159cdd38c7b4 (diff) |
A new suite of TLS tests, intially covering client authentication
use-cases for TLS 1.1
Diffstat (limited to 'core/src')
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; + } +} |