1
votes

I try to set up a mutually authenticated connection in a custom camel endpoint. I try to run a test. When I set ClientAuthentication.REQUIRE to NONE, a TLS connection is established and all is fine. I would like to have mutual authentication on connections, though. When i set the flag to REQUIRE, I get (with enabled full debug output):

CamelJettyWebSocketServer-40, fatal error: 42: null cert chain javax.net.ssl.SSLHandshakeException: null cert chain

What puzzles me: When i do not activate client authentication, both client and server side set up a web socket connection and do unidirectional authentication. So the certificates and trust chains seem to be fine?

My cert chain is set up like this: Root CA -> Sub CA -> Client & Server Cert. So the truststore, containing RootCA and SubCA certs are identical on both server and client side, only the keystore differs.

This is the last part of the log (raw data stripped):

* ServerHello, TLSv1.2 RandomCookie: GMT: 1478504471 bytes = ... 192, 99, 62} Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 Compression Method: 0 Extension renegotiation_info, renegotiated_connection: * %% Initialized: [Session-2, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384] ** TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 [read] MD5 and SHA1 hashes: len = 81 0000: ...
. *** Certificate chain chain [0] = [ [ Version: V3 Subject: CN=dptmt idl Server, OU=dptmt, O=company, L=Muenchen, ST=Bayern, C=DE Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

Key: Sun RSA public key, 2048 bits modulus: ... public exponent: 65537 Validity: [From: Tue Oct 25 11:50:28 CEST 2016, To: Thu Oct 25 11:50:28 CEST 2018] Issuer: CN=idl SubCA 2016, O=company, C=DE SerialNumber: [ 01]

Certificate Extensions: 8 1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false AuthorityInfoAccess [ [ accessMethod: cklmsuers accessLocation: URIName: http://downloads.dptmt.company.de/rootcacert2016.cert ] ]

[2]: ObjectId: 2.5.29.35 Criticality=false AuthorityKeyIdentifier [ KeyIdentifier [ 0000: F3 85 C5 44 9F 10 A7 BE AF 8E 6C 54 73 07 31 CB ...D......lTs.1. 0010: FB 4F 73 81
.Os. ] ]

[3]: ObjectId: 2.5.29.19 Criticality=false BasicConstraints:[
CA:false PathLen: undefined ]

[4]: ObjectId: 2.5.29.31 Criticality=false CRLDistributionPoints [
[DistributionPoint: [URIName: http://crl.dptmt.company.de/idl.crl] ]]

[5]: ObjectId: 2.5.29.37 Criticality=false ExtendedKeyUsages [
clientAuth serverAuth ]

[6]: ObjectId: 2.5.29.15 Criticality=false KeyUsage [
DigitalSignature Key_Encipherment ]

[7]: ObjectId: 2.5.29.17 Criticality=false SubjectAlternativeName [
DNSName: localhost DNSName: 127.0.0.1 DNSName: idlconnector.dptmt.company.de ]

[8]: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: C8 39 51 89 74 35 FF AB 07 38 12 79 C0 AA 8C 17 .9Q.t5...8.y.... 0010: 22 D3 81 34
"..4 ] ]

] Algorithm: [SHA256withRSA]...

] *** Found trusted certificate: [ [ Version: V3 Subject: CN=idl SubCA 2016, O=company, C=DE Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

Key: Sun RSA public key, 2048 bits modulus: ... public exponent: 65537 Validity: [From: Tue Oct 25 11:49:47 CEST 2016, To: Wed Oct 23 11:49:47 CEST 2024] Issuer: CN=idl Root CA 2016, O=company, C=DE SerialNumber: [ 01]

Certificate Extensions: 6 1: ObjectId: 1.3.6.1.5.5.7.1.1 Criticality=false AuthorityInfoAccess [ [ accessMethod: cklmsuers accessLocation: URIName: http://downloads.dptmt.company.de/rootcacert2016.cert ] ]

[2]: ObjectId: 2.5.29.35 Criticality=false AuthorityKeyIdentifier [ KeyIdentifier [ 0000: 7B 98 1D B2 FF 81 B4 DB D8 C2 5F 1D 88 8A AD 1A .........._..... 0010: 52 2D 60 2C
R-`, ] ]

[3]: ObjectId: 2.5.29.19 Criticality=true BasicConstraints:[ CA:true PathLen:2147483647 ]

[4]: ObjectId: 2.5.29.31 Criticality=false CRLDistributionPoints [
[DistributionPoint: [URIName: http://crl.dptmt.company.de/idl.crl] ]]

[5]: ObjectId: 2.5.29.15 Criticality=false KeyUsage [
DigitalSignature Key_CertSign Crl_Sign ]

[6]: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: F3 85 C5 44 9F 10 A7 BE AF 8E 6C 54 73 07 31 CB ...D......lTs.1. 0010: FB 4F 73 81
.Os. ] ]

] Algorithm: [SHA256withRSA] Signature: ...

] [read] MD5 and SHA1 hashes: len = 1156 0000: 0B ...
F..T * ECDH ServerKeyExchange Signature Algorithm SHA512withRSA Server key: Sun EC public key, 256 bits public x coord: 28847400382242897438936230468661390225193273270317541849731947641016598202380 public y coord: 44672647556933576846980891280419719564112470643460642037901420136126664747387 parameters: secp256r1 [NIST P-256, X9.62 prime256v1] (1.2.840.10045.3.1.7) [read] MD5 and SHA1 hashes: len = 333 0000: ... CertificateRequest Cert Types: RSA, DSS, ECDSA Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA Cert Authorities: [read] MD5 and SHA1 hashes: ... ServerHelloDone [read] MD5 and SHA1 hashes: len = 4 0000: 0E 00 00 00 .... Warning: no suitable certificate found - continuing without client authentication * Certificate chain


* ECDHClientKeyExchange ECDH Public value: { ... Finished verify_data: { 99, 243, 110, 215, 154, 6, 238, 190, 185, 65, 205, 89 } [write] MD5 and SHA1 hashes: len = 16 0000: 14 00 00 0C 63 F3 6E D7 9A 06 EE BE B9 41 CD 59 ....c.n......A.Y Padded plaintext before ENCRYPTION: len = 96 0000: ...
LC 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectorManager - Selector loop woken up from select, 1/1 selected 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectChannelEndPoint - Key interests updated 1 -> 0 on SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,R,-,177/30000,SslConnection}{io=1,kio=0,kro=1} 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectChannelEndPoint - Local interests updating 1 -> 0 for SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,R,-,177/30000,SslConnection}{io=0,kio=0,kro=1} 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectorManager - Queued change org.eclipse.jetty.io.SelectChannelEndPoint$1@431394e9 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.AbstractConnection - FILL_INTERESTED-->FILLING SslConnection@3c4546fe{NEED_UNWRAP,eio=-1/-1,di=-1} -> HttpConnection@80ef4f3[FILL_INTERESTED,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,R,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.ssl.SslConnection - onFillable enter DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,R,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1} 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.AbstractConnection - FILL_INTERESTED-->FILLING HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] 08:41:11.655 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.ssl.SslConnection - onFillable exit DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1} 08:41:11.656 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.AbstractConnection - FILLING-->IDLE SslConnection@3c4546fe{NEED_UNWRAP,eio=-1/-1,di=-1} -> HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] 08:41:11.656 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectorManager - Running change org.eclipse.jetty.io.SelectChannelEndPoint$1@431394e9 08:41:11.656 [CamelJettyWebSocketServer-31-selector-ServerConnectorManager@3fcdcf/0] DEBUG org.eclipse.jetty.io.SelectorManager - Selector loop waiting on select 08:41:11.656 [CamelJettyWebSocketServer-40] DEBUG org.eclipse.jetty.server.HttpConnection - HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,177/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] onFillable HttpChannelState@772152e{s=IDLE i=true a=null} 08:41:11.656 [CamelJettyWebSocketServer-40] DEBUG org.eclipse.jetty.io.ssl.SslConnection - SslConnection@3c4546fe{NEED_UNWRAP,eio=-1/-1,di=-1} -> HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,179/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] fill enter 08:41:11.656 [CamelJettyWebSocketServer-40] DEBUG org.eclipse.jetty.io.ChannelEndPoint - filled 82 SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,178/30000,SslConnection}{io=0,kio=0,kro=1} [Raw write]: length = 6 0000: 14 03 03 00 01 01
...... [Raw write]: length = 101 0000: ... 08:41:11.656 [CamelJettyWebSocketServer-40] DEBUG org.eclipse.jetty.io.ssl.SslConnection - SslConnection@3c4546fe{NEED_UNWRAP,eio=82/-1,di=-1} -> HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,179/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,0/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] filled 82 encrypted bytes E5 28 74 84 36 9F 56 D8 11 0C 38 .f..L.(t.6.V...8 0060: 38 2C AC 97 6C
[Raw read]: length = 5 8,..l 0000: ... CamelJettyWebSocketServer-40, READ: TLSv1.2 Handshake, length = 77 08:41:11.666 [CamelJettyWebSocketServer-40] DEBUG org.eclipse.jetty.io.ssl.SslConnection - SslConnection@3c4546fe{NEED_TASK,eio=0/-1,di=-1} -> HttpConnection@80ef4f3[FILLING,DecryptedEndPoint@4f49498{/127.0.0.1:37052<->9292,Open,in,out,-,-,189/30000,HttpConnection}->SelectChannelEndPoint@37823e21{/127.0.0.1:37052<->9292,Open,in,out,-,-,10/30000,SslConnection}{io=0,kio=0,kro=1}][p=HttpParser{s=START,0 of 0},g=HttpGenerator{s=START},c=HttpChannelOverHttp@40d049f5{r=0,c=false,a=IDLE,uri=}] unwrap Status = OK HandshakeStatus = NEED_TASK bytesConsumed = 82 bytesProduced = 0 *
Certificate chain *** CamelJettyWebSocketServer-40, fatal error: 42: null cert chain javax.net.ssl.SSLHandshakeException: null cert chain %% Invalidated: [Session-1, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384]

The full log (containing RAW data) with enabled SSL debug can be found here: Full Log

My test is defined in here:

public void startTestServer() throws Exception {
    // start a simple websocket echo service
    server = new Server(PORT);
    Connector connector = new ServerConnector(server);
    server.addConnector(connector);

    ServletContextHandler ctx = new ServletContextHandler();
    ctx.setContextPath("/");
    ctx.addServlet(TestServletFactory.class.getName(), "/*");

    server.setHandler(ctx);

    server.start();
    assertTrue(server.isStarted());      
}

public void stopTestServer() throws Exception {
    server.stop();
    server.destroy();
}

@Override
public void setUp() throws Exception {

    ClassLoader classLoader = getClass().getClassLoader();
    URL trustStoreURL = classLoader.getResource("jsse/client-truststore.jks");
    System.setProperty("javax.net.ssl.trustStore", trustStoreURL.getFile());
    System.setProperty("javax.net.ssl.trustStorePassword", "password");
    startTestServer();
    super.setUp();
}

@Override
public void tearDown() throws Exception {
    super.tearDown();
    stopTestServer();
}

@Test
public void testTwoRoutes() throws Exception {
    MockEndpoint mock = getMockEndpoint("mock:result");
    mock.expectedBodiesReceived(TEST_MESSAGE);

    template.sendBody("direct:input", TEST_MESSAGE);

    mock.assertIsSatisfied();
}

private static SSLContextParameters defineClientSSLContextClientParameters() {

    KeyStoreParameters ksp = new KeyStoreParameters();
    ksp.setResource(Thread.currentThread().getContextClassLoader().getResource("jsse/client-keystore.jks").toString());
    ksp.setPassword(PWD);

    KeyManagersParameters kmp = new KeyManagersParameters();
    kmp.setKeyPassword(PWD);
    kmp.setKeyStore(ksp);

    KeyStoreParameters tsp = new KeyStoreParameters();
    tsp.setResource(Thread.currentThread().getContextClassLoader().getResource("jsse/client-truststore.jks").toString());

    tsp.setPassword(PWD);

    TrustManagersParameters tmp = new TrustManagersParameters();
    tmp.setKeyStore(tsp);

    SSLContextServerParameters scsp = new SSLContextServerParameters();
    scsp.setClientAuthentication(ClientAuthentication.REQUIRE.name());
    //scsp.setClientAuthentication(ClientAuthentication.NONE.name());

    SSLContextParameters sslContextParameters = new SSLContextParameters();
    sslContextParameters.setKeyManagers(kmp);
    sslContextParameters.setTrustManagers(tmp);
    sslContextParameters.setServerParameters(scsp);


    return sslContextParameters;
}

private static SSLContextParameters defineServerSSLContextParameters() {
    KeyStoreParameters ksp = new KeyStoreParameters();
    ksp.setResource(Thread.currentThread().getContextClassLoader().getResource("jsse/server-keystore.jks").toString());
    ksp.setPassword(PWD);

    KeyManagersParameters kmp = new KeyManagersParameters();
    kmp.setKeyPassword(PWD);
    kmp.setKeyStore(ksp);

    KeyStoreParameters tsp = new KeyStoreParameters();
    tsp.setResource(Thread.currentThread().getContextClassLoader().getResource("jsse/server-truststore.jks").toString());
    tsp.setPassword(PWD);

    TrustManagersParameters tmp = new TrustManagersParameters();
    tmp.setKeyStore(tsp);

    SSLContextServerParameters scsp = new SSLContextServerParameters();
    scsp.setClientAuthentication(ClientAuthentication.REQUIRE.name());
    //scsp.setClientAuthentication(ClientAuthentication.NONE.name());

    SSLContextParameters sslContextParameters = new SSLContextParameters();
    sslContextParameters.setKeyManagers(kmp);
    sslContextParameters.setTrustManagers(tmp);
    sslContextParameters.setServerParameters(scsp);


   return sslContextParameters;
}

@Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
    RouteBuilder[] rbs = new RouteBuilder[2];

    // A consumer
    rbs[0] = new RouteBuilder() {
        public void configure() {

            // Needed to configure TLS on the client side
            WsComponent wsComponent = (WsComponent) context.getComponent("someclient");
            wsComponent.setSslContextParameters(defineClientSSLContextClientParameters());

            from("direct:input").routeId("foo")
                .log(">>> Message from direct to WebSocket Client : ${body}")
                .to("someclient://localhost:9292/echo")
                .log(">>> Message from WebSocket Client to server: ${body}");
            }
    };

    // A provider
    rbs[1] = new RouteBuilder() {
        public void configure() {

                // Needed to configure TLS on the server side
                WebsocketComponent websocketComponent = (WebsocketComponent) context.getComponent("someserver");
                websocketComponent.setSslContextParameters(defineServerSSLContextParameters());

                // This route is set to use TLS, referring to the parameters set above
                from("someserver:localhost:9292/echo")
                .log(">>> Message from WebSocket Server to mock: ${body}")
                .to("mock:result");
        }
    };
    return rbs;
}

Can you help me get an idea of what is going on?

1

1 Answers

0
votes

Late answer but still, let me try:

You wrote:

"When I set ClientAuthentication.REQUIRE to NONE, a TLS connection is established and all is fine"

and:

"What puzzles me: When i do not activate client authentication, both client and server side set up a web socket connection"

In my opinion there's no problem here, it's expected behaviour isn't it? If you don't require the client to authenticate, then you have a connection.

If you do require the client to authenticate and the client doesn't send the correct certificate chain then you get a fatal error: 42: null cert chain on the server side.

You can test this with openssl, but enable SSL debuging first on the server side with -Djavax.net.debug=all, then do:

openssl s_client -connect localhost:443 -CAfile cacert.pem -cert clientcert.pem -key clientkey.pem -state -tls1_2

You should see Verify return code: 0 (ok) at the end of the openssl output.

On the server side, copy that part of the server log into srv-side-openssl-client-log.txt (for example).

Now test the connection with your Java client and copy that server log into srv-side-java-client-log.txt.

Compare the two and see what is different. Your srv-side-java-client-log.txt probably is missing a huge block of hex codes because it doesn't send the certificate chain.