0

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?

Abra
  • 19,142
  • 7
  • 29
  • 41
user555303
  • 1,146
  • 3
  • 20
  • 44
  • Can you elaborate on what "runs on Redhat" means? Just the O/S or something else? – stdunbar Sep 01 '23 at 01:14
  • You need to understand the difference between a resource loaded via class loader, and a path on the filesystem. – tgdavies Sep 01 '23 at 01:30
  • @tgdavies - Can you explain? Are you saying that I'd need to put the .p12 file into (for example) the resources folder in the Eclipse project? And if so, do I need to set the value for the path to the .p12 file in that TEST_CLIENT_KEYSTORE_RESOURCE variable? We actually want the files like the p12 with the client cert and the truststore/JKS file to be separate from the app. Is it possible to do that with the code from that example, and if so how? – user555303 Sep 01 '23 at 03:09
  • Can you update your question to explain where you're stuck? The method `loadKeyMaterial` takes a `URL`, `File.toURI().toURL()` should do what you want. – tgdavies Sep 01 '23 at 03:15
  • @stdunbar - I am working with Oracle's "Unified Directory" (OUD) product. OUD supports plugins and what I am trying to use that example code for is to modify one of our existing OUD plugins to connect to another data source (a web service). So when I aid "runs on Redhat," I meant that the OUD (and the OUD plugins) are running on Redhat servers. I mentioned that because, as I said I first tried the code by building it in Eclipse on a Windows machine, then I took the class/JAR file and ran it on one of our Redhat machines with no problem, and I am puzzled why I am encountering the problems. – user555303 Sep 01 '23 at 03:15
  • @tgdavies - I'v updated the OP. I guess I'd like to figure out/find out how I can get the method that I made from the earlier thread working, because I am "that close" to having something, I think. – user555303 Sep 01 '23 at 04:42
  • @tgdavies -I made a URL but I am not sure what you meant by "should do what you want? Are you saying replace "FederationUtil.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE), storePassword, keyPassword, (aliases, socket) -> aliases.keySet().iterator().next() " with the URL? – user555303 Sep 01 '23 at 04:58
  • try `new File(TEST_CLIENT_KEYSTORE_RESOURCE).toURI().toURL()` in place of `FederationUtil.class.getResource(TEST_CLIENT_KEYSTORE_RESOURCE)`. – tgdavies Sep 01 '23 at 05:43
  • @tgdavies - same error - e.getMessage()=java.security.cert.CertificateException: No X509TrustManager implementation available]!! – user555303 Sep 01 '23 at 06:02
  • Same error? Looks different to me. – tgdavies Sep 01 '23 at 06:12
  • Your new error is indeed a different error (@user555303) and establishes that now the keystore file _is_ found, because the attempt to create a TrustManager occurs in `.build()` which is called only if `.loadKeyMaterial(...)` succeeded. However, `build()` attempts to use the default TrustManager and fails; though the specific exception is discarded, the most like causes are that your _truststore_ file either doesn't exist (which your code checks and should have logged) or can't be accessed with the default password "changeit" or isn't PKCS12 or JKS (check for those). – dave_thompson_085 Sep 01 '23 at 06:48
  • @tgdavies - i should've posted the last attempt by updating the OP instead of in comments (it was really late at night here), but I've done that now (EDIT3, above). The error message I got with the last change looks like the same as from EDIT2? Sorry. I am unclear why you said the EDIT3 error looks different thant the EDIT2 error message? – user555303 Sep 01 '23 at 10:34
  • @dave_thompson_085 - The truststore is set inside the method using "System.setProperty("javax.net.ssl.trustStore", pathToTruststore);" - is that not a valid way to set the truststore (a JKS file)? This code goes inside one of the OUD plugins, so there isn't really a "command line" to use a "-D" to set the system property. I don't understand why doing that is not working :( ... – user555303 Sep 01 '23 at 11:08
  • @dave_thompson_085 - I've made some progress and updated the OP with the info - I think/hope I got past the trustmanager error but now getting a different error. Thanks! – user555303 Sep 01 '23 at 11:54
  • Yes you need to also set the truststore password if it differs from the default "changeit" -- that's why I told you to check it. Your newer error NoSuchElementException is a pretty generic and vague one. Try calling `e.printStackTrace()` to get (more) specific details -- this writes to `System.out` by default; if the OUD environment makes that inaccessible, direct it to a file or an in-memory stream that you then log or display or store or something. – dave_thompson_085 Sep 01 '23 at 19:30

0 Answers0