So, as I am finally looking to document/share more I?ll start with this ?little? SSL error and nonetheless pain in the ass one that I had to troubleshoot a couple weeks ago with a customer.
Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
If you are lazy or in a hurry just go to the TLDR part in the end.
Actually there is a lot of information in the Web about this error and other SSL/TLS errors. My aim here is to explain how you can debug TLS connections at a lower level to see what is happening.
I work at Datasystems btw, an IT solutions company based in Paraguay, as a tech consultant.
So, this error appears in a Java environment. In my (customer) case, there was a service trying to communicate to another one. As the services are behind load balancers with HTTPS, the clients, in this case JBoss EAP needs to trust the endpoint that is connecting to. The client needs to have in its Truststore the CA Root certificate that signed the Certificate or the Intermediate Certificate that the client is connecting to.
A common practice, for intra company connections, is using certificates signed by local CA Roots. So the client then uses a Truststore that includes the CA Root certificate of the company and therefore can trust and connect to endpoints that uses certificates signed by the CA Root.
And when does this trustAnchors parameter must be non-empty appears? It does when the Java cannot read for whatever reason the Truststore that was specified for it [1]. Take into account that if no Truststore was specified then it will default to the one in the JRE security dir.
But, how can I certify if and what Truststore is my Java process using? That is the correct questions. There is a way to debug SSL connections by adding this property javax.net.debug [2][3] to the Java Process with the values listed in [2] or simply with All. Just take into account that it will print a lot.
Let me show you how with an example. I wanna verify that I am using a specific Truststore who has the correct CA Root certificate of where I want to connect. I will use a Java client that my coworker Hctor Alfonso helped me built [4].
First I remove the cacerts Truststore file:
Then I test a simple web server with a cert that was issued by a local CA Root.
java -jar test_get_https-0.0.1-SNAPSHOT-jar-with-dependencies.jar https://www.calca.com.py
As the Java cannot find/open the TrustStore file it gives the error:
So, to see at a more low level what is happening in the TLS/SSL exchange, I will add the javax.net.debug property:
# java -Djavax.net.debug=all -jar test_get_https-0.0.1-SNAPSHOT-jar-with-dependencies.jar https://www.calca.com.py 2>&1| grep -i truststore | grep -e ?Inaccessible trust store? -e ?No available key store? -e ?Reloaded?
I can even specify a specific Truststore with the javax.net.ssl.trustStore property:
# java -Djavax.net.debug=all -Djavax.net.ssl.trustStore=/Another/path/to/cacerts -jar test_get_https-0.0.1-SNAPSHOT-jar-with-dependencies.jar https://www.calca.com.py 2>&1| grep -i truststore | grep -e ?Inaccessible trust store? -e ?No available key store? -e ?Reloaded?
So, I will rename correctly again the Truststore and test again:
# java -Djavax.net.debug=all -jar test_get_https-0.0.1-SNAPSHOT-jar-with-dependencies.jar https://www.calca.com.py 2>&1| grep -i truststore
And it works:
# java -jar test_get_https-0.0.1-SNAPSHOT-jar-with-dependencies.jar https://www.calca.com.py
FTR, for anyone (as me) who wants to know where is pointing the symbolic link of the cacerts of the JVM:
The TL;DR then:
The error trustAnchors parameter must be non-empty means the Java cannot find or read the client Truststore [1]. Use the properties javax.net.debug=all and javax.net.ssl.trustStore=/path/to/Truststore to debug the issue.
[1] https://stackoverflow.com/a/6788682
[2] https://docs.oracle.com/javase/8/docs/technotessecurity/JSSERefGuide.html#Debug
[3] https://access.redhat.com/solutions/973783
[4] https://github.com/gcalcaterra/http_client