Apache Camel Http and SSL

I got to work a ssl proxy with the following code

The route

public class MyRouteBuilder extends RouteBuilder {

public void configure() {

    configureSslForJetty();
    configureSslForHttp4();

    from("jetty:https://0.0.0.0:4443/topython/?matchOnUriPrefix=true")
    .to("https4://backend.fake.com:4444/?q=ssl&bridgeEndpoint=true&throwExceptionOnFailure=false");
}
...

Configuration for jetty (provide a certificate when we are acting as a server)

private void configureSslForJetty()
{
    KeyStoreParameters ksp = new KeyStoreParameters();
    ksp.setResource("c:\\Projects\\blah\\fakefilter.jks");
    ksp.setPassword("123456");

    KeyManagersParameters kmp = new KeyManagersParameters();
    kmp.setKeyStore(ksp);
    kmp.setKeyPassword("export-password");

    SSLContextParameters scp = new SSLContextParameters();
    scp.setKeyManagers(kmp);

    JettyHttpComponent jettyComponent = getContext().getComponent("jetty", JettyHttpComponent.class);
    jettyComponent.setSslContextParameters(scp);
}

Configuration for https4 (what certificate signers do we trust when acting as a client)

private void configureSslForHttp4()
{
    KeyStoreParameters trust_ksp = new KeyStoreParameters();
    trust_ksp.setResource("c:\\Projects\\blah\\fakeca.jks");
    trust_ksp.setPassword("123456");

    TrustManagersParameters trustp = new TrustManagersParameters();
    trustp.setKeyStore(trust_ksp);

    SSLContextParameters scp = new SSLContextParameters();
    scp.setTrustManagers(trustp);

    HttpComponent httpComponent = getContext().getComponent("https4", HttpComponent.class);
    httpComponent.setSslContextParameters(scp);
}

}

Things worth noting

  • you need to configure component https4 not http4
  • -Djavax.net.debug=ssl in the command line provided lots of helpful logging

Ok working now, as it turns out, I had a fundamental misunderstanding of endpoints and protocols within Camel. I should have been registering a scheme with the the https4 protocol and setting my SSLSocketFactory/SSLContext on it. Since it Was registering a scheme with https it was never been using by the Http4 component.

Here is my working solution with 2 caveats.

  1. Why can't I pass in a SchemeRegistry to the ThreadSafeClientConnManager and it is not used when constructing the HttpClient? I have to the HttpClientConfigurer instead

  2. Jetty has an issue where the Keystore and Truststore must be set by path on the SslSelectChannelConnector instead of via SSLContext (bug is in at least jetty 7.2.2 and 7.4.0 ->latest)

Code:

public class CamelProxy {

/**
 * @param args
 */
public static void main(String[] args) throws Exception {
    CamelContext context = new DefaultCamelContext();

    final Endpoint jettyEndpoint = configureJetty(context);

    final Endpoint https4Endpoint = configureHttpClient(context);

    context.addRoutes(new RouteBuilder() {

        @Override
        public void configure() {
            from(jettyEndpoint).to("log:com.smithforge.request?showAll=true").to(https4Endpoint);
        }
    });

    context.start();

    context.stop();
}

private static Endpoint configureHttpClient(CamelContext context) throws Exception {
    KeyStore keystore = KeyStore.getInstance("PKCS12");
    keystore.load(new FileInputStream(new File("/home/brian/User2.p12")), "Password1234!".toCharArray());

    KeyStore truststore = KeyStore.getInstance("JKS");
    truststore.load(new FileInputStream(new File("/home/brian/jboss.truststore")), "changeit".toCharArray());

    KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("SunX509");
    keyFactory.init(keystore, "Password1234!".toCharArray());

    TrustManagerFactory trustFactory = TrustManagerFactory.getInstance("SunX509");
    trustFactory.init(truststore);

    SSLContext sslcontext = SSLContext.getInstance("TLSv1");
    sslcontext.init(keyFactory.getKeyManagers(), trustFactory.getTrustManagers(), null);

    SSLSocketFactory factory = new SSLSocketFactory(sslcontext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

    SchemeRegistry registry = new SchemeRegistry();
    final Scheme scheme = new Scheme("https4", 443, factory);
    registry.register(scheme);

    HttpComponent http4 = context.getComponent("http4", HttpComponent.class);
    http4.setHttpClientConfigurer(new HttpClientConfigurer() {

        @Override
        public void configureHttpClient(HttpClient client) {
            client.getConnectionManager().getSchemeRegistry().register(scheme);
        }

    });
    http4.setClientConnectionManager(new ThreadSafeClientConnManager());
    return http4
            .createEndpoint("https4://soafa-lite-staging:443/axis2/services/SigActService?bridgeEndpoint=true&throwExceptionOnFailure=false");
}

private static Endpoint configureJetty(CamelContext context) throws Exception {
    JettyHttpComponent jetty = context.getComponent("jetty", JettyHttpComponent.class);

    SslSelectChannelConnector sslConnector = new SslSelectChannelConnector();
    sslConnector.setPort(4443);
    sslConnector.setKeystore("/home/brian/jboss.keystore");
    sslConnector.setKeyPassword("changeit");
    sslConnector.setTruststore("/home/brian/jboss.truststore");
    sslConnector.setTrustPassword("changeit");
    sslConnector.setPassword("changeit");
    sslConnector.setNeedClientAuth(true);
    sslConnector.setAllowRenegotiate(true);

    Map<Integer, SslSelectChannelConnector> connectors = new HashMap<Integer, SslSelectChannelConnector>();
    connectors.put(4443, sslConnector);

    jetty.setSslSocketConnectors(connectors);
    return jetty.createEndpoint("jetty:https://localhost:4443/service");
}

// .to("log:com.smithforge.response?showHeaders=true");
}