[jetty-user] Client-Server with SSL hangs if TrustManager throws exception
Siim Annuk <[email protected]> Thu, 22 Nov 2012 15:08:17 +0200
| Newsgroups | gmane.comp.java.jetty.support |
|---|---|
| Message-ID | <[email protected]> |
--------------010109070003000909070005
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Hello!
When using SSL (with custom Trust/KeyManagers) in Jetty in a
client-server environment, the program hangs and times out if the
TrustManager throws an exception during handshake.
For example, if the TrustManager.checkServerTrusted method simply throws
an CertificateException, the program does not terminate with an
exception and just waits until the client times out. The exception that
is thrown is only logged and a "General SSLEngine problem" appears.
So, the question is, how should SSL error handling be done in such case?
I have attached a simple client-server program that triggers the behaviour.
Best regards,
Siim Annuk
--------------010109070003000909070005
Content-Type: text/x-java;
name="CertificateExceptionTest.java"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename="CertificateExceptionTest.java"
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Objects;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.eclipse.jetty.client.Address;
import org.eclipse.jetty.client.ContentExchange;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory;
public final class CertificateExceptionTest {
private static final int PORT_NUMBER = 8443;
private static HttpClient client;
private static Server echoServer;
private static X509Certificate clientCert;
private static X509Certificate serverCert;
private static X509Certificate caCert;
private static PrivateKey clientKey;
private static PrivateKey serverKey;
public static void main(String[] args) throws Exception {
System.setProperty("javax.net.debug", "ssl");
try {
loadKeystores();
createServer();
createClient();
echoServer.start();
client.start();
sendMessage("Hello, world!");
client.stop();
echoServer.stop();
echoServer.join();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
private static void loadKeystores() throws Exception {
clientCert = (X509Certificate) ...
serverCert = (X509Certificate) ...
caCert = (X509Certificate) ...
clientKey = (PrivateKey) ...
serverKey = (PrivateKey) ...
}
private static void sendMessage(String message) throws Exception {
final byte[] data = message.getBytes(StandardCharsets.UTF_8);
ContentExchange exchange = new ContentExchange() {
@Override
protected void onResponseComplete() throws IOException {
System.out.println(
"Received from server: " + getResponseContent());
}
};
exchange.setMethod("POST");
exchange.setAddress(new Address("127.0.0.1", PORT_NUMBER));
exchange.setScheme("https");
exchange.setRequestURI("/");
exchange.setRequestContentSource(new ByteArrayInputStream(data));
client.send(exchange);
exchange.waitForDone();
}
private static void createClient() throws Exception {
SslContextFactory cf = new SslContextFactory(false);
cf.setNeedClientAuth(true);
KeyManager[] keyManagers = new KeyManager[] {
new MyClientKeyManager() };
TrustManager[] trustManagers = new TrustManager[] {
new MyClientTrustManager() };
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(keyManagers, trustManagers, new SecureRandom());
SSLEngine engine = ctx.createSSLEngine();
engine.setNeedClientAuth(cf.getNeedClientAuth());
engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
engine.setEnabledProtocols(engine.getSupportedProtocols());
cf.setSslContext(ctx);
client = new HttpClient(cf);
client.setConnectorType(HttpClient.CONNECTOR_SELECT_CHANNEL);
client.setMaxConnectionsPerAddress(1);
}
private static class MyClientKeyManager extends X509ExtendedKeyManager {
@Override
public String chooseClientAlias(String[] arg0, Principal[] arg1,
Socket arg2) {
return null;
}
@Override
public String chooseServerAlias(String arg0, Principal[] arg1,
Socket arg2) {
return null;
}
@Override
public X509Certificate[] getCertificateChain(String arg0) {
return new X509Certificate[] { clientCert };
}
@Override
public String[] getClientAliases(String arg0, Principal[] arg1) {
return null;
}
@Override
public PrivateKey getPrivateKey(String arg0) {
return clientKey;
}
@Override
public String[] getServerAliases(String arg0, Principal[] arg1) {
return null;
}
@Override
public String chooseEngineClientAlias(String[] keyType,
Principal[] issuers, SSLEngine engine) {
return "MyClientKeyManager";
}
@Override
public String chooseEngineServerAlias(String keyType,
Principal[] issuers, SSLEngine engine) {
return null;
}
}
private static class MyClientTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// XXX: Causes to hang
throw new CertificateException("Not trusted!");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
private static void createServer() throws Exception {
echoServer = new Server(8080);
echoServer.setHandler(new AbstractHandler() {
@Override
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
StringWriter sw = new StringWriter();
IOUtils.copy(request.getInputStream(), sw);
System.out.println("Received from client: " + sw.toString());
response.getOutputStream().print(sw.toString());
baseRequest.setHandled(true);
}
});
SslContextFactory cf = new SslContextFactory(false);
cf.setNeedClientAuth(true);
KeyManager[] keyManagers = new KeyManager[] {
new MyServerKeyManager() };
TrustManager[] trustManagers = new TrustManager[] {
new MyServerTrustManager() };
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(keyManagers, trustManagers, new SecureRandom());
SSLEngine engine = ctx.createSSLEngine();
engine.setNeedClientAuth(cf.getNeedClientAuth());
engine.setEnabledCipherSuites(engine.getSupportedCipherSuites());
engine.setEnabledProtocols(engine.getSupportedProtocols());
cf.setSslContext(ctx);
SslSelectChannelConnector connector = new SslSelectChannelConnector(cf);
connector.setName("ClientConnector");
connector.setHost("localhost");
connector.setPort(PORT_NUMBER);
echoServer.addConnector(connector);
}
private static class MyServerKeyManager extends X509ExtendedKeyManager {
@Override
public String chooseClientAlias(String[] arg0, Principal[] arg1,
Socket arg2) {
return null;
}
@Override
public String chooseServerAlias(String arg0, Principal[] arg1,
Socket arg2) {
return null;
}
@Override
public X509Certificate[] getCertificateChain(String arg0) {
return new X509Certificate[] { serverCert };
}
@Override
public String[] getClientAliases(String arg0, Principal[] arg1) {
return null;
}
@Override
public PrivateKey getPrivateKey(String arg0) {
return serverKey;
}
@Override
public String[] getServerAliases(String arg0, Principal[] arg1) {
return null;
}
@Override
public String chooseEngineClientAlias(String[] keyType,
Principal[] issuers, SSLEngine engine) {
return null;
}
@Override
public String chooseEngineServerAlias(String keyType,
Principal[] issuers, SSLEngine engine) {
return "MyServerKeyManager";
}
}
private static class MyServerTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1)
throws CertificateException {
// XXX: Deliberately cause a handshake failure
//throw new CertificateException("checkClientTrusted");
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[] { caCert };
}
}
}
--------------010109070003000909070005
Content-Type: text/plain; charset=us-ascii
---------------------------------------------------------------------
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email
--------------010109070003000909070005--