JavaLdapCerts

Java, LDAP, and Chained Self-Signed Certificates

Introduction

I had a java / tomcat app (Artifactory) that was configured to use LDAP for user authentication, and I was asked to make it use LDAP over SSL with a self-signed certificate. That turned out to be not very well documented, so here are my notes on how to do it.

First of all, you need to understand how java applications use security certificates. There's a certificate store file (typically called cacerts) that contains all the public ssl trusted root keys. That file is loaded by the java interpreter when it starts up and serves as the source of truth for which certificates to trust. If your application uses an 'officially' signed certificate, everything will automatically work.

However, you may need to use a self-signed certificate. In this case, java doesn't know about the authenticity of your certificate because it isn't a descendent of one of the official root certificates. Thus you need to manually add your server public key to a new cacerts file and tell java to use that file instead of the default one.

Obtaining Public Certificates

In my case, I needed to make my tomcat application accept a self-signed certificate from my ldap server. The first step is to download the public certificate(s) from the ldap server (or whatever sort of server you are trying to use):

openssl s_client -connect 192.168.1.10:636 -showcerts < /dev/null  > certs.txt

Next, examine the certs.txt file. It will contain at least one certificate between BEGIN CERTIFICATE and END CERTIFICATE lines. It may as in my case contain two or more certificates. You need to put each of these certificates in separate files, preserving only the BEGIN and END lines and everything in between. All the other text in the certs.txt file can be discarded. I created two files, 192.168.1.10 and intermediate.

Next, you need a copy of your existing java cacerts file. As I mentioned, that file contains all the public ssl certificates that java trusts. In my case, that file was /usr/lib/jvm/java-8-oracle/jre/lib/security/cacerts. I made a copy of that file in /tmp.

Adding Certificates

Use the keytool program to add the new certificates to the cacerts file. You need to add every certificate that the server returned. Documentation on the internet is not clear on this point. Each certificate must be added with a different alias in the cacerts file. In my case:

$ for i in intermediate 192.168.1.10; do keytool -import -trustcacerts -file $i -keystore cacerts -alias $i

keytool will ask you for the cacerts file password each time it tries to add a certificate. The default file for the cacerts file is changeit. keytool will also ask you whether to trust each certificate as you add it. Choose 'yes'.

You can verify that your new certificates are part of the cacerts file by running keytool -list -v -keystore cacerts |less.

You now have an updated cacerts files, next you need to tell java to use it. Place the file in an appropriate location (in my case /etc/opt/jfrog/artifactory/cacerts) and modify your startup JAVA_OPTIONS:

echo "export JAVA_OPTIONS=\"\$JAVA_OPTIONS -Djavax.net.ssl.trustStore=/etc/opt/jfrog/artifactory/cacerts\"" \
>> /etc/opt/jfrog/artifactory/default

this will obviously vary depending on what tomcat application you are using. Restart your java app and verify that the new cacerts file has been loaded by using ps to check that the javax.net.ssl.trustStore option is set and points to your new cacerts file.

Server Config

Now that java has your self-signed certificates, you need to modify your server configuration to use ssl. Again in my case I'm using Artifactory but other apps will need similar configuration changes. Navigate to the Artifactory web page and log on as an administrator. Go to Admin->Security->LDAP and modify your ldap server to ldaps, i.e. ldaps://192.168.1.10. Save your settings and use the 'Test LDAP Connection' box to confirm that user authentication is now working over SSL.

Troubleshooting

It can be useful to verify that java can make ssl connections without going through your tomcat application. To do this, use the java utility SSLPoke, which you can download from Atlassian. To test that you can make ssl connections to an ldap server, use

$ /usr/bin/java -Djavax.net.ssl.trustStore=/etc/opt/jfrog/artifactory/cacerts SSLPoke 192.168.1.10 636

if that command succeeds, you know that your java is loading your self-signed certificates correctly.

You can also turn on ssl debugging by passing -Djavax.net.debug=all in your java startup. That will print lots and lots of debug messages about ssl when your app tries to make ssl connections. In the case of Artifactory, those messages will be printed to /etc/opt/jfrog/artifactory/logs/catalina/catalina.out. You can also check /etc/opt/jfrog/artifactory/logs/artifactory.log for any other ldap error messages. If you see

Authentication failed. Probably a wrong manager dn or manager password: [LDAP: error code 49 - 80090308: LdapErr:
nested exception is javax.naming.AuthenticationException: [LDAP: error code 49 - 80090308: LdapErr: DSID-0C0903A9,
 comment: AcceptSecurityContext error, data 52e

that means that the ldap server is not accepting your username/password. If you see

[Root exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPath BuilderException: unable to find valid certification path to requested target]

that means that java is not finding your self-signed certificates in the cacerts file.

Conclusion

Well, that's how you deal with self-signed certificates in java. It's a confusing process so I hope that these notes help others.


CategoryGeekStuff
CategoryLinuxStuff



Our Founder
ToolboxClick to hide/show