I am trying to get the example code in this older thread to work:
How do I pass the client certificate with HTTP client?
I originally built the example in Eclipse on my Windows laptop, then copied the compiled class/JAR to a Redhat machine and ran it on the Redhat machine, and it worked. It was able to connect and send a request to a app that was configured for 2-way SSL.
In that Eclipse project I had a main() method and also most of the example in a separate method, because what my end-goal is to use that method on in an application (the application runs on Redhat) that we are working on. Here is the method I ended up with (sorry there is lots of debug logging):
protected String getRbsResponse(String strId, String pathToTruststore, String pathToClientCertPfx) {
logger.error("---+++++ In getRbsResponse: V1.00");
logger.error("---+++++ In getRbsResponse: strId=[" + strId + "]");
logger.error("---+++++ In getRbsResponse: pathToTruststore=[" + pathToTruststore + "]");
logger.error("---+++++ In getRbsResponse: pathToClientCertPfx=[" + pathToClientCertPfx + "]");
String TEST_URL = "https://xxxx/ID=" + strId;
logger.error("---+++++ In getRbsResponse: TEST_URL=[" + TEST_URL + "]");
//String TEST_CLIENT_KEYSTORE_RESOURCE = pathToClientCertPfx;
String TEST_CLIENT_KEYSTORE_RESOURCE = "/apps/common/certs/mutual-tls-keystore.p12";
logger.error("---+++++ In getRbsResponse: TEST_CLIENT_KEYSTORE_RESOURCE=[" + TEST_CLIENT_KEYSTORE_RESOURCE + "]");
String response = "";
logger.error("---+++++ In getRbsResponse: ABOUT TO CHECK IF IMPORTANT FILES EXIST !!!");
logger.error("---+++++ In getRbsResponse: Check IF CLIENT CERT PFX [" + TEST_CLIENT_KEYSTORE_RESOURCE + "] EXISTS !!!");
if (new File(TEST_CLIENT_KEYSTORE_RESOURCE).isFile()) {
logger.error("---+++++ In getRbsResponse: SUCCESS - CLIENT CERT PFX: [" + TEST_CLIENT_KEYSTORE_RESOURCE + "] EXISTS !!!");
} else {
logger.error("---+++++ In getRbsResponse: FAILED - CLIENT CERT PFX: [" + TEST_CLIENT_KEYSTORE_RESOURCE + "] DOES NOT EXIST !!!");
}
logger.error("---+++++ In getRbsResponse: Check IF TRUSTSTORE JKS [" + pathToTruststore + "] EXIST !!!");
if (new File(pathToTruststore).isFile()) {
logger.error("---+++++ In getRbsResponse: SUCCESS - TRUSTSTORE JKS: [" + pathToTruststore + "] EXISTS !!!");
} else {
logger.error("---+++++ In getRbsResponse: FAILED - TRUSTSTORE JKS: [" + pathToTruststore + "] DOES NOT EXIST !!!");
}
logger.error("---+++++ In getRbsResponse: About to set SYSTEM PROPERTY javax.net.ssl.truststore to [" + pathToTruststore + "] !!!");
System.setProperty("javax.net.ssl.trustStore", pathToTruststore);
logger.error("---+++++ In getRbsResponse: FINISHED setting SYSTEM PROPERTY javax.net.ssl.truststore to [" + pathToTruststore + "] !!!");
//Console console = System.console();
//char[] storePassword = console.readPassword("Key+Keystore password: ");
logger.error("---+++++ In getRbsResponse: About to set password for Client cert PFX and for TRUST STORE JKS into set SYSTEM PROPERTY javax.net.ssl.truststore to into two char arrays !!!");
String mypassword = "password";
logger.error("---+++++ In getRbsResponse: BOTH passwords are set to=[" + mypassword + "]");
char[] storePassword = mypassword.toCharArray();
char[] keyPassword = storePassword;
SSLContext sslContext = null;
try {
sslContext = SSLContexts.custom().loadKeyMaterial(FederationUtil.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE), storePassword, keyPassword, (aliases, socket) -> aliases.keySet().iterator().next() ).build();
try (CloseableHttpClient httpclient = HttpClients.custom().setSSLContext(sslContext).build();
CloseableHttpResponse closeableHttpResponse = httpclient.execute(new HttpGet(URI.create(TEST_URL)))) {
//console.writer().println(closeableHttpResponse.getStatusLine());
logger.error("---+++++ In getRbsResponse: closeableHttpResponse.getStatusLine()=[" + closeableHttpResponse.getStatusLine() + "]");
HttpEntity entity = closeableHttpResponse.getEntity();
try (InputStream content = entity.getContent();
ReadableByteChannel src = Channels.newChannel(content);
WritableByteChannel dest = Channels.newChannel(System.out)) {
ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
while (src.read(buffer) != -1) {
buffer.flip();
dest.write(buffer);
buffer.compact();
}
buffer.flip();
while (buffer.hasRemaining())
dest.write(buffer);
}
}
} catch (Exception e) {
logger.error("---+++++ In getRbsResponse: **EXCEPTION** e.getMessage()=" + e.getMessage() + "]!!");
logger.error("---+++++ In getRbsResponse: **EXCEPTION** - [", e);
}
// try moving catch above here....
logger.error("---+++++ Returning from getRbsResponse with response=[" + response + "]");
return response;
} // end getRbsResponse()
However when our app (which again, is running on Redhat) calls the the above method an exception is being thrown on the following line:
sslContext = SSLContexts.custom().loadKeyMaterial(FederationUtil.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE), storePassword, keyPassword, (aliases, socket) -> aliases.keySet().iterator().next() ).build();
The message in the exception is:
e.getMmessage()=Keystore URL may not be null]!!
As you can see from the code above, I am also doing some checks that the truststore file and the file with the client cert exist, and those checks are succeeding. The message from the exception seems to be saying that the "URL" is null?
I cannot figure out why I am getting that error :( , so I was hoping someone here might have some insight about that?
Thanks again, and sorry for the longish post !!
Jim
EDIT1: Where I am getting stuck: The "TEST_CLIENT_KEYSTORE_RESOURCE" contains a String, like "/apps/certs/multi.p12", which is the path on the filesystem where the client cert is located. When our app (an OUD plugin) calls the above method, I get an exception on that line and the e.Message says "Keystore URL may not be null". I understand from comments that (putting the path to the p12 file) is not what we should be doing, but I am trying to find out what I SHOULD be doing instead.
EDIT2: Based on comments I tried changing this part of the code:
File file = new File("/apps/common/certs/mutual-tls-keystore.p12");
try {
url = file.toURI().toURL();
} catch (Exception e) {
// TBD
}
SSLContext sslContext = null;
try {
//sslContext = SSLContexts.custom().loadKeyMaterial(FederationUtil.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE), storePassword, keyPassword, (aliases, socket) -> aliases.keySet().iterator().next()).build();
sslContext = SSLContexts.custom().loadKeyMaterial(file, storePassword, keyPassword).build();
Then, when I test I am getting a different error:
e.getMessage()=java.security.cert.CertificateException: No X509TrustManager implementation available]!!
EDIT 3: Based on additional comments I changed the sslContext to:
sslContext = SSLContexts.custom().loadKeyMaterial(new File(TEST_CLIENT_KEYSTORE_RESOURCE).toURI().toURL(), storePassword, keyPassword, (aliases, socket) -> aliases.keySet().iterator().next() ).build();
and when I tested I got the error:
e.getMessage()=java.security.cert.CertificateException: No X509TrustManager implementation available]
EDIT4: I think (HOPE) that I MAY be making some progress. Based on comments from @tgdavies and @dave_thompson_085, I was doing searching about the Trustmanager error, and also using the system property, and I found this SO thread - Keep getting No X509TrustManager implementation available error when trying to connect to web socket server.
In there, it mentions about ALSO SETTING the System property for the truststore PASSWORD, WHICH I WAS NOT DOING!! So, I added that and ran another tests, and I THINK it got past the Trustmanager error, but now is getting a different error:
e.getMessage()=java.util.NoSuchElementException
That error APPEARS to be on the line:
CloseableHttpResponse closeableHttpResponse = httpclient.execute(new HttpGet(URI.create(TEST_URL)))) {
I'm going to add some more debug but if anyone has any idea what might be causing that error, please let me know?
EDIT5: I've added following, just to see if it was having problems with the URL in TEST_URL:
HttpGet doGetFromRbs = new HttpGet(URI.create(TEST_URL));
but that seems to work fine, and I even did "doGetFromRbs.getURI().toString()" after that, and it returned the same URL string as in TEST_URL.
Also, I can see the logging on the server that this request SHOULD be going to, and I confirmed that when I run a test, there is no request hitting the server side yet :(, so it seems like code is blowing up before it even sends a request to the server?