I have an Apache Camel+Camel-email+Springboot project. The IMAP route in my camel-context file fails to start up with error org.apache.camel.FailedToStartRouteException: Failed to start route mail-route-imap because of null
I added a few SSL specific parameters to my application.properties
which I need for interaction with my back-end APIs. I wonder why since the IMAP connection with Exchange server is over an OAUTH2 Client Secret. I wonder why msal4j
library is looking for an SSL handshake when it is supposed to use the myExchangeAuthenticator
that I am passing in the IMAP url? Postscript: The IMAP route works fine if I remove the SSL parameters from my application-context.
ERROR | ForkJoinPool.commonPool-worker-1 | AuthenticationResultSupplier.java logException:155 | [Correlation ID: 272b48e9-bf32-4244-9b4b-61dd0cda568e] Execution of class com.microsoft.aad.msal4j.AcquireTokenByClientCredentialSupplier failed.
com.microsoft.aad.msal4j.MsalClientException: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.microsoft.aad.msal4j.HttpHelper.executeHttpRequest(HttpHelper.java:53)
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
ERROR | main | SpringApplication.java reportFailure:818 | Application run failed
org.apache.camel.FailedToStartRouteException: Failed to start route mail-route-imap because of null
at org.apache.camel.impl.engine.RouteService.setUp(RouteService.java:132)
camel-context.xml
<route id="mail-route-imap" autoStartup="true">
<from id="office365" uri="imaps://outlook.office365.com:993?authenticator=#myExchangeAuthenticator&debugMode=true&mail.imaps.proxy.host={{proxyHost}}&mail.imaps.proxy.port={{proxyPort}}&mail.imaps.auth.mechanisms=XOAUTH2&disconnect=true" />
<to uri="direct:someProcessChainAhead" />
</route>
application.properties
# Enabling SSL Bench
server.http2.enabled=true
server.ssl.enabled=true
server.ssl.enabl=TLSv1.2,TLSv1.3
server.servlet.session.cookie.secure=true
server.servlet.session.cookie.same-site=strict
server.ssl.trust-store=/some-path-here/truststore.ks
server.ssl.key-alias=nfmt
server.ssl.key-store=/some-path-here/keystore.ks
server.ssl.client-auth=want
**MainApplication.java**
`//initialized the SSL params in appContext
public static void main(String[] args) {
MyVaultUtil.initialize();
System.setProperty("javax.net.ssl.trustStore", "/some-path-here/truststore.ks");
System.setProperty("javax.net.ssl.trustStorePassword", MyVaultUtil.getInstance()
.getSecret(MyVaultUtil.SecretKeys.truststore_external_password.getKey()));
System.setProperty("javax.net.ssl.trustStoreType", KeyStore.getDefaultType());
System.setProperty("server.ssl.trust-store-password", MyVaultUtil.getInstance()
.getSecret(MyVaultUtil.SecretKeys.truststore_external_password.getKey()));
System.setProperty("server.ssl.key-store-password", MyVaultUtil.getInstance()
.getSecret(MyVaultUtil.SecretKeys.keystore_external_password.getKey()));
MainApplication.run(MainApplication.class, args);
}
@Bean //initializing inside MainApp
MicrosoftExchangeOnlineOAuth2MailAuthenticator exchangeAuthenticator() {
return new MicrosoftExchangeOnlineOAuth2MailAuthenticator(tenantId, clientId, clientSecret, userName);
}`
My Apache Camel+Camel-email+Springboot project is a client project. Hence having these entries in the application.properties was not Ok nor setting them in the Main class. Instead I used a sslConfig (a dedicated class with the SSLContext intialization for my HTTP client) in my backend service and it worked to establish the certificate based call to the backend service.
Steps:
MyBackendService:
@Autowired
SSLConfig sslConfig;
public String getSomeServiceDetails(String myReqPayload){
String finalResponse=null;
HttpPost httpPost = new HttpPost("some-url-to-backend");
// Set headers
httpPost.setHeader("Content-Type", "application/json");
// Set the request payload
StringEntity entity = new StringEntity(myReqPayload, StandardCharsets.UTF_8);
httpPost.setEntity(entity);
try (CloseableHttpResponse response = sslConfig.getHttpClient().execute(httpPost)) {
// Check the response status and process the response
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == HttpStatus.SC_OK) {
//my business logic here
// Read and store the response entity once
finalResponse = EntityUtils.toString(response.getEntity());
}
} catch (IOException e) {
//some exception handling here
}
return finalResponse;
}
MySSLConfig:
@PostConstruct
public void initSSLContext() {
try {
FileInputStream inputStream = new FileInputStream(
new File("/mydirectory/for/certificates/External/keystore.jks"));
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(inputStream, System.getenv("MY_KEYSTORE_STRING_IN_ENV").toCharArray());
SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(new File("/mydirectory/for/certificates/External/truststore.jks"),
System.getenv("MY_TRUSTSTORE_STRING_IN_ENV").toCharArray())
.loadKeyMaterial(ks, System.getenv("MY_TRUSTSTORE_STRING_IN_ENV").toCharArray()).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
} catch (Exception e) {
//some exception occurred
}
}