Uploaded image for project: 'FUSE Mediation Router'
  1. FUSE Mediation Router
  2. MR-724

Two camel-https4 producer endpoints configured with different sslContextParametersRef in separate routes in the same CamelContext does not work properly

    XMLWordPrintable

Details

    Description

      I have a camel test case and it shows that two camel-https4 producer endpoints configured with different sslContextParametersRef in two separate routes in the same CamelContext does not work properly. Please see attached test case for more detail.

      There are two routes:

      <route xmlns="http://camel.apache.org/schema/spring" trace="true" id="test.ssltests:tracingHttp4Test:1.0" >
              <from uri="direct:route1" />
              <to uri="https4://localhost:8043/dummy10?throwExceptionOnFailure=false&amp;sslContextParametersRef=sslContextParams1&amp;x509HostnameVerifier=hostnameVerifier"/>
              <setBody>
                        <constant>route1Response</constant>
              </setBody>
              <to uri="stream:out"/>
      </route>
      
      <route xmlns="http://camel.apache.org/schema/spring" trace="true" id="test.ssltests:tracingHttp4Test:2.0" >
              <from uri="direct:route2" />
              <to uri="https4://192.168.0.190:8043/dummy20?throwExceptionOnFailure=false&amp;sslContextParametersRef=sslContextParams2&amp;x509HostnameVerifier=hostnameVerifier"/>
              <setBody>
                      <constant>route2Response</constant>
              </setBody>
              <to uri="stream:out"/>
      </route>
      

      And two sslContextParametersRef were configured with two different sets of certs. So in my unit test case, I invoked on API SpringCamelContext.addRouteDefinitions() to add the first route "route1" and then send a request to it and it invoked out to my JettyServer that was running on localhost 8043 port through the https4 producer endpoint over SSL connection. Then I added the second route and paused by calling Thread.sleep(20000). During this 20 seconds interval, I stopped and restarted the Jetty Server. After resume, I sent the second request to the first route hoping that it will work as the first request. However, this time the test failed with an error:

      23755 [main] ERROR org.apache.camel.processor.DefaultErrorHandler - Failed delivery for (MessageId: ID-jluoMac-51616-1369908080516-0-4 on ExchangeId: ID-jluoMac-51616-1369908080516-0-5). Exhausted after delivery attempt: 1 caught: javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
      javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
      at com.sun.net.ssl.internal.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:352)
      at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128)
      at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397)
      at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148)
      at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:150)

      Basically, the reason the SSL connection failed for the first route on the second request was that when I restarted the Jetty Server in the 20 second pause interval, the original SSL connection was closed. It would need to re-establish a new SSL connection to the Jetty Server. However, because I added the second camel route onto the CamelConext that contains another HTTPS4 producer endpoint with a new sslContextParametersRef, a new Scheme was registered with the same "https4" string and the same port 8043 although the camel-https4 producer endpoint was meant to invoke out on the port 8043 of another machine 192.168.0.190 rather than localhost:

      public class HttpComponent extends HeaderFilterStrategyComponent {
      ...
         protected void registerPort(boolean secure, X509HostnameVerifier x509HostnameVerifier, int port, SSLContextParameters sslContextParams) throws Exception {
              SchemeRegistry registry = clientConnectionManager.getSchemeRegistry();
              if (secure) {
                  SSLSocketFactory socketFactory;
                  if (sslContextParams == null) {
                      socketFactory = SSLSocketFactory.getSocketFactory();
                  } else {
                      socketFactory = new SSLSocketFactory(sslContextParams.createSSLContext());
                  }
                  
                  socketFactory.setHostnameVerifier(x509HostnameVerifier);
                  // must register both https and https4
                  registry.register(new Scheme("https", port, socketFactory));
                  LOG.info("Registering SSL scheme https on port " + port);
                  
                  registry.register(new Scheme("https4", port, socketFactory));
                  LOG.info("Registering SSL scheme https4 on port " + port);
              } else {
                  // must register both http and http4
                  registry.register(new Scheme("http", port, new PlainSocketFactory()));
                  LOG.info("Registering PLAIN scheme http on port " + port);
                  registry.register(new Scheme("http4", port, new PlainSocketFactory()));
                  LOG.info("Registering PLAIN scheme http4 on port " + port);
              }
      ...
      

      Somehow Camel-Https4 component uses a new SSLContext to try to negotiate SSL handshake with the Jetty Server. And the new SSLContext contains a new instance of X509TrustManager with a different sets of certs from the second camel-https4 endpoint. Therefore the Jetty Server returned an error immediately:

      main, SEND TLSv1 ALERT: fatal, description = certificate_unknown

      due to the fact that it did not understand the complete new sets of certs that the client sent over during SSL handshake since it was from the second camel-https4 producer endpoint. As a result, the SSL handshake failed with above error.

      Here is instruction to run the test case:
      1. unzip the test case to your local file system;
      2. go to Jetty Server folder by "cd JettyHttpsSpringServer";
      3. rebuild the project and start the Jetty Server by command "mvn camel:run". It will start a Jetty Server with SSL on port 8043 on localhost;
      4. go to unit test client by "cd Http4ComponentExample";
      5. rebuild the project and start the unit test by command "mvn test";
      6. the unit test client will send the first request to route1 and add the route2 definition to the CamelContext. Then it will sleep for 20 seconds. During the 20 seconds interval, restart the Jetty Server. The reason we need to restart the Jetty Server is to close the original SSL connection to force the unit test client to re-establish a new connection. Otherwise, the test case will succeed because it will use cached original SSL connection and of course the invocation will succeed.
      7. let the unit test client to resume and send the second request to the route1, you will see the error described above "javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated".

      Note, you do not need to start the second Jetty Server (JettyHttpsSpringServer-v2) on another machine for this test as it is not needed.

      Attachments

        Activity

          People

            cibsen@redhat.com Claus Ibsen
            rhn-support-qluo Joe Luo
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: