Hi Colleagues,

With a help of TestNG test case, we are hosting CAMEL JETTY HTTPS service in 
one camel context and invoking HTTPS call using CAMEL-HTTP4. This works if I 
used the standard approach of providing 
'org.apache.camel.util.jsse.KeyStoreParameters.KeyStoreParameters()' by giving 
Server Keystore (for JETTY) and Client Trust Store (for HTTP4).

Use-case : Check the X509 Trust Certificate validation based on first the 
default JVM Trust Manager and if that fails, then fall back to another 
custom-truststore (containing X509 certificates).

Exception: java.security.InvalidAlgorithmParameterException: the trustAnchors 
parameter must be non-empty

Complete code below (generate simple key-pair using keytool and extracted 'cer' 
file into another JKS file to create client trust store)
Exception Stack Track after the code. If in MyTrustedManagersParameters class 
below, we swap the commented line (return), it WORKS, but that doesn't solve 
our use-case of merging two different TrustManagers.

Regards,
Arpit.

*********************************************************
TEST CASE
*********************************************************

public class TestHttpsX509Handshake {

  private static final String PASSPHRASE = "changeit";
  private static final String DIRECT_START_X509 = "direct:start-x509";
  private static CamelContext jettyServerContext;
  private static CamelContext http4ClientContext;

  private static URL SERVER_KEYSTORE = 
TestHttpsX509Handshake.class.getResource("server-keystore.jks");
  private static URL CLIENT_TRUSTSTORE = 
TestHttpsX509Handshake.class.getResource("client-truststore.jks");
  private static int HTTPS_PORT;
  private static final Logger LOG = LogManager.getLogger();

  static {
    System.setProperty("javax.net.debug", "all");
    System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", 
Boolean.TRUE.toString());
    System.setProperty("sun.security.ssl.allowLegacyHelloMessages", 
Boolean.TRUE.toString());
  }

  @BeforeClass
  public static void beforeClass() throws Exception {
    try {
      HTTPS_PORT = AvailablePortFinder.getNextAvailable(12100);
      jettyServerContext = new DefaultCamelContext();
      setJettyServerKeyStoreParameters();
      jettyServerContext.addRoutes(createServerRouteBuilder());
      jettyServerContext.setTracing(true);
      jettyServerContext.start();

      http4ClientContext = new DefaultCamelContext();
      setHttp4ClientTrustStoreParameters();
      http4ClientContext.addRoutes(createClientRouteBuilder());
      http4ClientContext.setTracing(true);
      http4ClientContext.start();

    } catch (Exception e) {
      if (jettyServerContext != null) {
        jettyServerContext.stop();
      }
      if (http4ClientContext != null) {
        http4ClientContext.stop();
      }
      throw e;
    }
  }

  @AfterClass
  public static void afterClass() throws Exception {
    jettyServerContext.stop();
    http4ClientContext.stop();
  }

  @Test
  public void testX509Communication() throws Exception {
    ProducerTemplate template = http4ClientContext.createProducerTemplate();
    DefaultExchange exchange = (DefaultExchange) 
template.request(DIRECT_START_X509, new Processor() {

      @Override
      public void process(Exchange exchange) throws Exception {
        exchange.getIn().setBody("HELLO WORLD");
      }
    });
    if (exchange.getException() != null) {
      Assert.fail("Unable to establish communication with server", 
exchange.getException());
    }
  }

  private static void setJettyServerKeyStoreParameters() {
    KeyStoreParameters ksp = new KeyStoreParameters();
    ksp.setResource(SERVER_KEYSTORE.getFile());
    ksp.setPassword(PASSPHRASE);

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

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

    JettyHttpComponent9 jettyComponent = 
jettyServerContext.getComponent("jetty", JettyHttpComponent9.class);
    jettyComponent.setSslContextParameters(scp);

  }

  private static void setHttp4ClientTrustStoreParameters() throws Exception {
    KeyStoreParameters ksp = new KeyStoreParameters();
   ksp.setResource(CLIENT_TRUSTSTORE.getFile());
    ksp.setPassword(PASSPHRASE);

    TrustManagersParameters tmp = new 
MyTrustedManagersParameters(CLIENT_TRUSTSTORE.getFile(), PASSPHRASE);
    tmp.setKeyStore(ksp);

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

    HttpComponent http4Component = http4ClientContext.getComponent("https4", 
HttpComponent.class);
    http4Component.setSslContextParameters(scp);

  }

  private static RouteBuilder createServerRouteBuilder() {
    return new RouteBuilder() {

      @Override
      public void configure() throws Exception {
        from("jetty:https://localhost:"; + HTTPS_PORT + "/api/v1").process(new 
Processor() {

          @Override
          public void process(Exchange exchange) throws Exception {
            String body = exchange.getIn().getBody(String.class);
            Assert.assertNotNull(body);
            Assert.assertEquals(body, "HELLO WORLD");
          }
        });
      }
    };
  }

  private static RouteBuilder createClientRouteBuilder() {

    return new RouteBuilder() {

      @Override
      public void configure() throws Exception {
        from(DIRECT_START_X509).to("https4://localhost:" + HTTPS_PORT + 
"/api/v1");
      }
    };
  }
}


********************************************************************************************************
EXTENDED CAMEL org.apache.camel.util.jsse.TrustManagersParameters
********************************************************************************************************
public class MyTrustedManagersParameters extends TrustManagersParameters {

  private String trustStorePath;
  private String trustStorePassphrase;

  public MyTrustedManagersParameters(String trustStorePath, String 
trustStorePassphrase) {
    this.trustStorePath = trustStorePath;
    this.trustStorePassphrase = trustStorePassphrase;
  }

  public TrustManager[] createTrustManagers() throws GeneralSecurityException, 
IOException {
    CustomX509TrustManager customX509TrustManager = null;
    KeyStoreParameters keyStoreParams = this.getKeyStore();
    if(keyStoreParams != null && 
!StringUtils.isEmpty(keyStoreParams.getResource())) {
      String existingTrustStore = keyStoreParams.getResource();
      try(InputStream is = 
TestHttpsX509Handshake.class.getResourceAsStream(existingTrustStore)) {
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(is, keyStoreParams.getPassword().toCharArray());
        customX509TrustManager = new CustomX509TrustManager(ks, 
this.trustStorePath, this.trustStorePassphrase);
      }
    }
    return new TrustManager[]{customX509TrustManager};
//    return super.createTrustManagers();
  }
}

********************************************************************************************************
CUSTOM X509 Trust manager implementation
********************************************************************************************************

/**
* Using Default X509 Trust Manager and Customer X509 Trust manager
*
 * Based on -
* http://stackoverflow.com/questions/19005318/implementing-x509trustmanager-
* passing-on-part-of-the-verification-to-existing/19005844#19005844
*
 */
public class CustomX509TrustManager implements X509TrustManager {

  private static final String JAVA_TRUST_STORE_PWD_SYS_PROP = 
"javax.net.ssl.trustStorePassword";
  private static final String JAVA_TRUST_STORE_SYS_PROP = 
"javax.net.ssl.trustStore";
  private X509TrustManager defaultTrustManager;
  private String myTrustStorePath;
  private String myTrustStorePassphrase;
  private KeyStore myTrustStore;

  static {
    System.setProperty("javax.net.ssl.keyStore", 
TestHttpsX509Handshake.class.getResource("server-keystore.jks").getFile());
    System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
  }

  public CustomX509TrustManager(KeyStore myTrustStore, String myTrustStorePath, 
String myTrustStorePassphrase) throws GeneralSecurityException {
    this.myTrustStore = myTrustStore;
    this.myTrustStorePassphrase = myTrustStorePassphrase;
    this.myTrustStorePath = myTrustStorePath;

    fetchDefaultX509TrustManager();
  }

  @Override
  public void checkClientTrusted(X509Certificate[] chain, String authType) 
throws CertificateException {
    // If you're planning to use client-cert auth,
    // do the same as checking the server.
    this.defaultTrustManager.checkClientTrusted(chain, authType);
  }

  @Override
  public void checkServerTrusted(X509Certificate[] chain, String authType) 
throws CertificateException {
    try {
      this.defaultTrustManager.checkServerTrusted(chain, authType);
    } catch (CertificateException ex) {
      String defaultTrustStorePath = 
System.getProperty(JAVA_TRUST_STORE_SYS_PROP);
      String defaltTrustStorePassphrase = 
System.getProperty(JAVA_TRUST_STORE_PWD_SYS_PROP);
      if (defaultTrustStorePath == null) {
        System.setProperty(JAVA_TRUST_STORE_SYS_PROP, this.myTrustStorePath);
        System.setProperty(JAVA_TRUST_STORE_PWD_SYS_PROP, 
this.myTrustStorePassphrase);
      }

      try {
        TrustManagerFactory trustManagerFactory = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(this.myTrustStore);
        for (TrustManager mgr : trustManagerFactory.getTrustManagers()) {
          if (mgr instanceof X509TrustManager) {
            ((X509TrustManager) mgr).checkServerTrusted(chain, authType);
          }
        }
      } catch (KeyStoreException | NoSuchAlgorithmException e) {
        throw new CertificateException("Unable to fetch X509 Trust Manager", e);
      } finally {
        if (defaultTrustStorePath == null) {
          System.clearProperty(JAVA_TRUST_STORE_SYS_PROP);
        } else {
          System.setProperty(JAVA_TRUST_STORE_SYS_PROP, defaultTrustStorePath);
        }
        if (defaltTrustStorePassphrase == null) {
          System.clearProperty(JAVA_TRUST_STORE_PWD_SYS_PROP);
        } else {
          System.setProperty(JAVA_TRUST_STORE_PWD_SYS_PROP, 
defaltTrustStorePassphrase);
        }
      }
    }
  }

  @Override
  public X509Certificate[] getAcceptedIssuers() {
    // If you're planning to use client-cert auth,
    // merge results from "defaultTm" and "myTm".
    return this.defaultTrustManager.getAcceptedIssuers();
  }

  private void fetchDefaultX509TrustManager() throws GeneralSecurityException {
    TrustManagerFactory trustManagerFactory = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    trustManagerFactory.init((KeyStore) null);
    for (TrustManager mgr : trustManagerFactory.getTrustManagers()) {
      if (mgr instanceof X509TrustManager) {
        this.defaultTrustManager = (X509TrustManager) mgr;
      }
    }
  }
}

********************************************************************************************************
Exception Stacktrace
********************************************************************************************************

Caused by: java.lang.RuntimeException: Unexpected error: 
java.security.InvalidAlgorithmParameterException: the trustAnchors parameter 
must be non-empty
                at 
sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:90)
                at 
sun.security.validator.Validator.getInstance(Validator.java:179)
                at 
sun.security.ssl.X509TrustManagerImpl.getValidator(X509TrustManagerImpl.java:314)
                at 
sun.security.ssl.X509TrustManagerImpl.checkTrustedInit(X509TrustManagerImpl.java:173)
                at 
sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:186)
                at 
sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:107)
                at 
com.successfactors.integrationframework.http.support.CustomX509TrustManager.checkServerTrusted(CustomX509TrustManager.java:62)
                at 
sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:897)
                at 
sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1454)
                at 
sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:213)
                at sun.security.ssl.Handshaker.processLoop(Handshaker.java:913)
                at 
sun.security.ssl.Handshaker.process_record(Handshaker.java:849)
                at 
sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1035)
                at 
sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1344)
                at 
sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1371)
                ... 59 more
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors 
parameter must be non-empty
                at 
java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:200)
                at 
java.security.cert.PKIXParameters.<init>(PKIXParameters.java:120)
                at 
java.security.cert.PKIXBuilderParameters.<init>(PKIXBuilderParameters.java:104)
                at 
sun.security.validator.PKIXValidator.<init>(PKIXValidator.java:88)
                ... 73 more


Reply via email to