I am getting the above error, saw a lot of solutions, have implemented the code and yet get the error.
Mar 07, 2018 10:17:59 PM <Class> performHttpRequest
SEVERE: IO error while performing HTTP POST to https://<url>/ with data <randomData>
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1959)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:141)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at <our code line>
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:260)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1596)
... 45 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 51 more
My code is as follows:
private String performHttpRequest(Configuration configuration, HttpEntityEnclosingRequestBase httpRequest, String body) {
CloseableHttpClient httpClient = createHttpClient(configuration);
HttpEntity responseEntity = null;
try {
HttpResponse response = httpClient.execute(httpRequest);
responseEntity = response.getEntity();
return EntityUtils.toString(responseEntity);
} catch (Exception e) {
String errorMessage = "IO error while performing HTTP " + httpRequest.getMethod() + " to " + httpRequest.getURI() + " with data " + body;
errorMessage = stringUtil.removeConfidentialData(errorMessage);
log.error(errorMessage, e);
throw new RuntimeException(errorMessage, e);
} finally {
if (responseEntity != null) {
try {
EntityUtils.consume(responseEntity);
} catch (IOException e) {
String errorMessage = "IO Error closing HTTP Client connection";
log.error(errorMessage, e);
throw new RuntimeException(errorMessage, e);
}
}
}
}
private CloseableHttpClient createHttpClient(Configuration configuration) {
return HttpClients.custom().setSSLSocketFactory(getSSLSocketFactory(configuration)).build();
}
public SSLConnectionSocketFactory getSSLSocketFactory(Configuration configuration) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManager[] keyStore = getKeyManager("PKCS12",
configuration.getString("keystore.path"),
configuration.getString("keystore.password"));
TrustManager[] trustManager = getTrustManager("jks",
configuration.getString("truststore.path"),
configuration.getString("truststore.password"));
sslContext.init(keyStore, trustManager, new SecureRandom());
return new SSLConnectionSocketFactory(sslContext);
} catch (Exception e) {
throw new RuntimeException("Unable to setup keystore and truststore", e);
}
}
private KeyManager[] getKeyManager(String keyStoreType, String keyStoreFile, String keyStorePassword) throws Exception {
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyStore.load(this.getClass().getClassLoader().getResourceAsStream(keyStoreFile), keyStorePassword.toCharArray());
kmf.init(keyStore, keyStorePassword.toCharArray());
return kmf.getKeyManagers();
}
private TrustManager[] getTrustManager(String trustStoreType, String trustStoreFile, String trustStorePassword) throws Exception {
KeyStore trustStore = KeyStore.getInstance(trustStoreType);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustStore.load(this.getClass().getClassLoader().getResourceAsStream(trustStoreFile), trustStorePassword.toCharArray());
tmf.init(trustStore);
return tmf.getTrustManagers();
}
The path for both truststore and the keystore are maintained in the classpath.
We had got the .crt from the 3rd party vendor, I generated the keys following the instructions from this: https://gist.github.com/mtigas/952344
From the p12 generated, I imported them into a jks following this: https://www.webfarmr.eu/2010/04/import-pkcs12-private-keys-into-jks-keystores-using-java-keytool/
Yet, when I run my tests, it fails consistently, with the above error. I don't want to manually add my certificate to the JVM, is it possible to still make the HTTPs calls without adding certificate to the server?
My local unit test passes if I make the below change in code in getting the SSL context:
public SSLConnectionSocketFactory getSSLSocketFactory(Configuration configuration) {
try {
SSLContexts.custom().loadKeyMaterial(getKeyManager("PKCS12",
configuration.getString("keystore"),
configuration.getString("keystore.password")),
configuration.getString("keystore.password").toCharArray()).build();
return new SSLConnectionSocketFactory(sslContext);
} catch (Exception e) {
throw new RuntimeException("Unable to setup keystore and truststore", e);
}
}
I don't know what's difference when I run it as an integration test, as in why it's not loading the certifiate.
The problem was in the generation of the PKCS12 file and in generating the keystore, corrected them and it worked.
The commands used were:
keytool -import -trustcacerts -alias root -file <file>.crt -keystore keystore.jks
keytool -importkeystore -srckeystore keystore.jks -destkeystore <new-file>.p12 -srcstoretype JKS -deststoretype PKCS12
This works with both sets of code mentioned above.