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