Skip to content

Commit

Permalink
Added trust manager wrapper with dynamic callback for accepting serve…
Browse files Browse the repository at this point in the history
…r certificates on the fly.
  • Loading branch information
bharbulot committed Jul 20, 2011
1 parent bd83c90 commit e1a0fd2
Show file tree
Hide file tree
Showing 2 changed files with 386 additions and 0 deletions.
@@ -0,0 +1,204 @@
/*-----------------------------------------------------------------------
This file is part of the jSSLutils library.
Copyright (c) 2011, Bruno Harbulot.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Author........: Bruno Harbulot
-----------------------------------------------------------------------*/

package org.jsslutils.sslcontext.trustmanagers;

import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.UUID;

import javax.net.ssl.X509TrustManager;

import org.jsslutils.sslcontext.X509TrustManagerWrapper;

/**
* @author Bruno Harbulot.
*/
public class ServerCallbackWrappingTrustManager implements X509TrustManager {
private final X509TrustManager trustManager;
private final KeyStore localTrustStore;
private final CheckServerTrustedCallback callback;

/**
* Creates a new instance from an existing X509TrustManager.
*
* @param trustManager
* X509TrustManager to wrap.
* @param callback
* {@link CheckServerTrustedCallback} from the user-interface.
* @param localTrustStore
* {@link KeyStore} (loaded)to use as a trust store.
* @param saveLocalTrustStore
* Set to true to save the keystore, otherwise, it will only be
* kept in memory.
*/
public ServerCallbackWrappingTrustManager(X509TrustManager trustManager,
CheckServerTrustedCallback callback, KeyStore localTrustStore) {
this.trustManager = trustManager;
this.localTrustStore = localTrustStore;
this.callback = callback;
}

/**
* Creates a new instance from an existing X509TrustManager.
*
* @param trustManager
* X509TrustManager to wrap.
* @param callback
* {@link CheckServerTrustedCallback} from the user-interface.
* @param localTrustStore
* {@link KeyStore} to use as a trust store.
* @param saveLocalTrustStore
* Set to true to save the keystore, otherwise, it will only be
* kept in memory.
* @throws KeyStoreException
* @throws IOException
* @throws CertificateException
* @throws NoSuchAlgorithmException
*/
public ServerCallbackWrappingTrustManager(X509TrustManager trustManager,
CheckServerTrustedCallback callback) throws KeyStoreException,
NoSuchAlgorithmException, CertificateException, IOException {
this(trustManager, callback, KeyStore.getInstance(KeyStore
.getDefaultType()));
this.localTrustStore.load(null);
}

/**
* Checks that the client is trusted; in this case, it delegates this check
* to the trust manager it wraps
*/
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
this.trustManager.checkClientTrusted(chain, authType);
}

/**
* Checks that the server is trusted; in this case, it accepts anything.
*/
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
try {
this.trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException e) {
try {
boolean certTrusted = false;
Enumeration<String> aliases = this.localTrustStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Certificate cert = this.localTrustStore
.getCertificate(alias);
if (chain[0].equals(cert)) {
certTrusted = true;
break;
}
}
if (certTrusted
|| this.callback.checkServerTrusted(chain, authType)) {
this.localTrustStore.setCertificateEntry(UUID.randomUUID()
.toString(), chain[0]);
} else {
throw e;
}
} catch (KeyStoreException kse) {
throw new CertificateException(kse);
}
}
}

/**
* Returns the accepted issuers; in this case, it's an empty array.
*/
public X509Certificate[] getAcceptedIssuers() {
return this.trustManager.getAcceptedIssuers();
}

/**
* Wrapper factory class that wraps existing X509TrustManagers into
* X509TrustManagers that trust any clients.
*
* @author Bruno Harbulot.
*/
public static class Wrapper implements X509TrustManagerWrapper {
private final CheckServerTrustedCallback callback;
private final KeyStore localTrustStore;

public Wrapper(CheckServerTrustedCallback callback,
KeyStore localTrustStore) {
super();
this.callback = callback;
this.localTrustStore = localTrustStore;
}

/**
* Builds an X509TrustManager from another X509TrustManager.
*
* @param trustManager
* original X509TrustManager.
* @return wrapped X509TrustManager.
*/
public X509TrustManager wrapTrustManager(X509TrustManager trustManager) {
if (localTrustStore != null) {
return new ServerCallbackWrappingTrustManager(
(X509TrustManager) trustManager, callback,
localTrustStore);
} else {
try {
return new ServerCallbackWrappingTrustManager(
(X509TrustManager) trustManager, callback);
} catch (KeyStoreException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (CertificateException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

public static interface CheckServerTrustedCallback {
public boolean checkServerTrusted(X509Certificate[] chain,
String authType);
}
}
@@ -0,0 +1,182 @@
/*-----------------------------------------------------------------------
This file is part of the jSSLutils library.
Copyright (c) 2008-2009, The University of Manchester, United Kingdom.
Copyright (c) 2011, Bruno Harbulot.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Author........: Bruno Harbulot
-----------------------------------------------------------------------*/

package org.jsslutils.sslcontext.test;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.security.cert.X509Certificate;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;

import org.jsslutils.sslcontext.PKIXSSLContextFactory;
import org.jsslutils.sslcontext.SSLContextFactory.SSLContextFactoryException;
import org.jsslutils.sslcontext.trustmanagers.ServerCallbackWrappingTrustManager;
import org.jsslutils.sslcontext.trustmanagers.ServerCallbackWrappingTrustManager.CheckServerTrustedCallback;
import org.junit.Test;

/**
* Tests the SSLContext configured for X.509 without CRLs. It should accept both
* the "good" and the "bad" certificate.
*
* @author Bruno Harbulot.
*
*/
public class CallbackTest extends MiniSslClientServer {
protected PKIXSSLContextFactory clientSSLContextFactory;
protected PKIXSSLContextFactory serverSSLContextFactory;

public static CheckServerTrustedCallback ACCEPTING_CALLBACK = new CheckServerTrustedCallback() {
public boolean checkServerTrusted(X509Certificate[] chain,
String authType) {
System.out
.println("Asking whether to trust an unkown certificate: ACCEPTING.");
return true;
}
};

public static CheckServerTrustedCallback REFUSING_CALLBACK = new CheckServerTrustedCallback() {
public boolean checkServerTrusted(X509Certificate[] chain,
String authType) {
System.out
.println("Asking whether to trust an unkown certificate: REFUSING.");
return false;
}
};

@Test
public void testInMemoryKeyStoreRefused() throws Exception {
assertTrue(prepareSSLContextFactories());
this.clientSSLContextFactory
.setTrustManagerWrapper(new ServerCallbackWrappingTrustManager.Wrapper(
REFUSING_CALLBACK, null));
assertTrue(!runTest());
assertTrue(!runTest());
}

@Test
public void testInMemoryKeyStoreAccepted() throws Exception {
assertTrue(prepareSSLContextFactories());
this.clientSSLContextFactory
.setTrustManagerWrapper(new ServerCallbackWrappingTrustManager.Wrapper(
ACCEPTING_CALLBACK, null));
assertTrue(runTest());
assertTrue(runTest());
}

@Test
public void testBadClient() throws Exception {

}

public boolean prepareSSLContextFactories() throws Exception {
this.clientSSLContextFactory = new PKIXSSLContextFactory();
this.serverSSLContextFactory = new PKIXSSLContextFactory(
getServerCertKeyStore(), MiniSslClientServer.KEYSTORE_PASSWORD,
getCaKeyStore());
return true;
}

public boolean runTest() throws Exception {
return runTest(clientSSLContextFactory.buildSSLContext(),
serverSSLContextFactory.buildSSLContext());
}

/**
* This runs the main test: it runs a client and a server.
*
* @param sslClientContext
* SSLContext to be used by the client.
* @param sslServerContext
* SSLContext to be used by the server.
* @return true if the server accepted the SSL certificate.
* @throws SSLContextFactoryException
* @throws IOException
*/
public boolean runTest(SSLContext sslClientContext,
SSLContext sslServerContext) throws IOException,
InterruptedException {

final SSLServerSocket serverSocket = prepareServerSocket(sslServerContext);

assertNotNull("Server socket not null", serverSocket);
assertTrue("Server socket is bound", serverSocket.isBound());

Thread serverThread = runServer(serverSocket);

Exception clientException = null;

try {
clientException = makeClientRequest(sslClientContext);
} finally {
synchronized (serverSocket) {
if (!serverSocket.isClosed())
serverSocket.close();
}
}
synchronized (serverSocket) {
assertTrue(serverSocket.isClosed());
}

try {
serverThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

Throwable serverRequestException = null;
Future<?> serverRequestFuture = serverRequestsFutures.poll();
try {
serverRequestFuture.get();
} catch (ExecutionException e) {
serverRequestException = e.getCause();
}

System.out.println();
System.out.println("Server request exception: "
+ serverRequestException);
System.out.println("Client exception: " + clientException);
System.out.println("Listening server exception: "
+ this.listeningServerException);

return clientException == null;
}
}

0 comments on commit e1a0fd2

Please sign in to comment.