Search code examples
javahttpsslsaaj

Selecting specific client certificate from keystore using SAAJ


I have written a Java client in SAAJ which works successfully when sending SOAP messages over HTTP however when I attempt to send any SOAP messages to a web service over HTTPS that requires a client certificate it doesnt work.

At the bottom of the page at the following link - SAAJ Security - it states the following:

From the SAAJ side, all you need to do is use URLs with HTTPS as the protocol. This will work only if the certificate was successfully imported into /jre/lib/security/cacerts; otherwise JSSE will not allow the connection

I imported the client certificate along with the associated root certificate into my Java cacerts as instructed to do so above and ran the program however I get the following error:

java.net.SocketException: Connection reset

I ran a wireshark trace on the traffic and noticed that the Java code isnt presenting a client certificate when asked to do so by the server therefore I have the following questions:

1) By only passing the HTTPS URL to the soapConnection.call() method along with importing the certs to my cacerts file, is this enough for authentication to occur i.e. does SAAJ handle this automatically? Or are there more steps required that arent described in the above link?

2) By importing the certificates into the cacerts file within my JAVA_HOME, does the Java SAAJ client automatically know to look here when calling the soapConnection.call()? Or do I need to explicitly tell my code what cacerts file to use?

3) If the Java SAAJ client is automatically using the cacerts file under my JAVA_HOME then how does it know what client certificate to use? Again, shouldnt I need to explicitly code this or does SAAJ handle this automatically?

Thanks in advance


Solution

  • I managed to figure this out. I used the following code:

    static public void doTrustToCertificates() throws Exception 
    {
    
        // Set truststore that contains root / intermediary certs
        System.setProperty("javax.net.ssl.trustStore", "C:\\cert\\trusted.jks");
        System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
    
        // Set keystore that contains private key
        File pKeyFile = new File("C:\\cert\\privatekey.pfx");
        String pKeyPassword = "Password01";
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        InputStream keyInput = new FileInputStream(pKeyFile);
        keyStore.load(keyInput, pKeyPassword.toCharArray());
        keyInput.close();
        keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());
    
        // Set ssl context with private key and truststore details
        SSLContext sc = SSLContext.getInstance("TLSv1");
        sc.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
        SSLSocketFactory sockFact = sc.getSocketFactory();
    
        // Add ssl context to https connection
        HttpsURLConnection.setDefaultSSLSocketFactory(sockFact);
    
    }
    

    Then called the doTrustToCertificates() method just before the soapConnection.call() method of SAAJ and it worked like so:

    doTrustToCertificates();
    SOAPMessage soapResponse = soapConnection.call(soapMsgXml, ENDPOINT_URL);