Monday, November 25, 2013

Yet another InstallCert for Java, now with STARTTLS support

Java

⇓ ... now forked at GitHub

added on 2017-10-21



Many of the Java folks, who ever dealt with SSL-enabled protocols and self-made SSL certificates, know of the InstallCert tool. This simple command-line tool, published in 2006 by Andreas Sterbenz at the official Sun blog, allows obtaining SSL certificates as they are presented by the hosts we connect to, with further optional saving them to the system trust store.

Sun's blog is not with us any more, but a copy of the original InstallCert publication and code is still available from some of the users' blogs, like this one, or archives like that one. Curiously, one of the current blogs at Oracle mentions this tool, but without reference to the original author, and with a reference (currently somewhat outdated) to the mentioned user blog instead...

Well, the original Andreas' tool served faithfully to me for quite a while, but every good thing has its limitations... In particular, the original InstallCert could not deal with hosts that operate using STARTTLS technique.

The new code


Diving into STARTTLS required quite a refactoring of the original code, though the main parts of it are still in place . In particular, modular approach was taken to deal with STARTTLS implementations for different protocols, so the code does not fit in single Java file any more, but is rather packaged as an executable .jar.

It is now possible to obtain certificates from hosts that not only speak plain SSL/TLS, but also expose their certificates via STARTTLS over IMAP, POP3, SMTP and LDAP.

For new application-level protocols with STARTTLS extension to be supported, an abstract STARTTLS handler is defined as a StarttlsHandler interface. This interface needs to be implemented by every new protocol handler, and the latter is to be registered with the Starttls wrapper class. This registration needs to be hard-coded so far. But keeping in mind the small number of STARTTLS-compatible application-level protocols yet to be implemented, this should not be a problem .

The certificates collected by the program are now stored at two locations:
  • the standard jssecacerts keystore in the lib/security folder of your JRE;
  • in an extracerts keystore in your current directory; the latter may be handy in order to save collected certificates in pure form for further redistribution.
One of the new features is also the new template for collected certificates' aliases. They are now named like "host - certificate_subject" for better human readability .

Downloads


The following downloads are available:
Please feel free to use and modify. The original license looks like 3-clause BSD one.

Usage – HOW-TO


Prerequisites

  1. Download the binary distribution archive from here or here.
  2. Unzip it to a location of your choice.

Obtaining a certificate from a plain SSL/TLS or an LDAP/STARTTLS server

Run the program like this:
java -jar installcert-usn-20140115.jar host_name
or
java -jar installcert-usn-20140115.jar host_name:port
or
java -jar installcert-usn-20140115.jar host_name:port truststore_password

The default port is 443 (for HTTPS). The default truststore password is "changeit" as per JSSE convention.

Obtaining a certificate from an IMAP / POP3 / SMTP server with STARTTLS extension

In this case you will need the JavaMail library, and make sure you have it on your classpath. Please also keep in mind that it is necessary to indicate the main class explicitly in the command line if you have more than one jar.

To make things easier, two shell scripts are provided: run-with-javamail-starttls.sh for Unix and run-with-javamail-starttls.cmd for Windows. You will have to edit at least one of them first, so to reflect the actual location of the JavaMail .jar file.

General notes and final housekeeping

If the program succeeds in obtaining a certificate (or several of them), and the certificates are not known yet, it will ask you whether you wish to save them. Upon successful run the program saves the new certificate(s) to two files, as mentioned above:
  • the standard jssecacerts keystore in the lib/security folder of your JRE;
  • an extracerts keystore in your current directory; this one may be handy in order to save collected certificates in pure form for further redistribution.
The first one will be needed by your software for normal JSSE operation. The second one is a good candidate for "clean" storage of your selected certificates.

Please keep in mind that in order to have the standard jssecacerts keystore file in the lib/security folder of your JRE successfully created/modified, you will most likely need to have administrative (superuser, root) privileges.

Enjoy!



... rebuilt for Java 1.6 and fixed

added on 2014-01-15

The download links and examples were updated to reflect the new build made to be compatible with Java 1.6 as per Eric's comment. The certificate handling logic was also improved for better discrimination of new certificates vs known ones.


... now forked at GitHub

added on 2017-10-21

This code is now forked and available at Github: https://github.com/spyhunter99/installcert . The fork was created with an intention to achieve embeddability, and looks actively developed...

22 comments:

  1. Thanks this helped me quite a bit, the only snag I came across was due to the fact that I'm still on Java 1.6. I installed 1.7 and was able to run the program successfully. Then I just merged the new extracerts keystore with the 1.6 keystore I was looking to update.

    Thanks again!
    -Eric

    ReplyDelete
    Replies
    1. Wow! :) Thank you for pointing this out. To be frank, I completely forgot people can be still using Java 1.6 :) And in fact nothing in the code prevents running it under 1.6. I'll look into getting it complied for 1.6.

      Just curious: did your case really require STARTTLS handling?

      Delete
    2. Well, the new build compatible with Java 1.6 is available for download instead of the old one... Some minor bug fixes were made also...

      Delete
    3. Yes, our mail server only enabledd SSL via STARTTLS, the previous version of installcert won't work so this helped big time. Thanks again.

      Delete
  2. I was looking for a solution to fetch domain controller's certificate from ldap port 389 (starttls)
    Your code works great.:-)
    Thanks Sergey

    ReplyDelete
    Replies
    1. That's my pleasure :)
      Thank you for letting me know :)

      Delete
  3. It was so simple to use it that I first solved the problem (inadvertently) using your binary and then actually deep dived to understand the problem and solution.
    Works like a charm Sergey :-)

    ReplyDelete
    Replies
    1. Baaton, thank you for the pleasure to know someone is using it :)
      Could you drop a line just for my curiousity :) - what kind of protocol did you use it for, and whether STARTTLS was really involved?

      Delete
    2. This comment has been removed by the author.

      Delete
  4. Hello Sergey ,

    actually still some ppl use even jre1.5 -:))

    [nxuser@as1 installcert-usn-20140115]$ java -jar installcert-usn-20140115.jar xx.xx.xx.xx:xx
    Exception in thread "main" java.lang.UnsupportedClassVersionError: Bad version number in .class file
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:620)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:268)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
    at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
    [nxuser@as1 installcert-usn-20140115]$ java -version
    java version "1.5.0_14"
    Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03)
    Java HotSpot(TM) Server VM (build 1.5.0_1

    ReplyDelete
  5. Hi i getting below on typing this command java -jar installcert-usn-20140115.jar webservice.ibancomplete.com
    ================================================= Error connecting to remote host : java.net.connectException connection timedout

    ReplyDelete
  6. Hi i getting below error on typing this command java -jar installcert-usn-20140115.jar webservice.ibancomplete.com
    ================================================= Error connecting to remote host : java.net.connectException connection timed out

    ReplyDelete
  7. Well Naidu, this is the response that I got for your command:
    ...>java -jar installcert-usn-20140115.jar webservice.ibancomplete.com
    ... loading system truststore from 'C:\Program Files\Java\jre1.8.0_121\lib\security\cacerts' ...
    ... creating extra truststore as a new one ...
    ... opening connection to webservice.ibancomplete.com:443 ...
    ... starting SSL handshake ...
    No errors, certificate is already trusted.
    No new certificates found to be added to the extra truststore.

    And it sounds reasonable, as the certificate is issued by Trustwave...

    My first idea is that your networking settings may prohibit connection to webservice.ibancomplete.com over port 443. Could you maybe cross-check it with telnet?
    > telnet webservice.ibancomplete.com 443
    I get a connection for such a command too...

    ReplyDelete
  8. hello,

    I couldn't access/download the source/binary , not sure if its still available.

    Thanks,
    Raj

    ReplyDelete
    Replies
    1. Hi Raj, I've just tested the download links, and they worked for me...
      I admit the hosting site may be down occasionally from time to time. Please try again and let me know. If the problem persists, we can discuss some other options for getting the tool available to you...

      Delete
    2. Thanks for the reply. May be the issue is at the network level at my end because of which i'm not able to get the file downloaded.

      Delete
    3. Hi Raaj, try these Google Drive links then:
      - https://drive.google.com/file/d/0B2nLiheurgV7Z1hxc3hMMGo0NWc/view and
      - https://drive.google.com/file/d/0B2nLiheurgV7WU9lOU9tdXpPR2c/view
      And I'll be glad to know if you succeed with downloading and further usage... :)

      Delete
  9. I am struggling with installing a cert from an LDAP server that runs on port 389. If I run the tool against this port, I get an SSL handshake exception "java.net.SocketException: Connection reset". Is it possible anyway to retrieve the cert via that port?

    ReplyDelete
  10. Hi Guido,
    can you provide more info? Is the exception accompanied with a stacktrace? Can you publish it here?

    ReplyDelete
  11. I'm trying to automate this, is there a way to accept the cert without prompting the user?

    ReplyDelete
    Replies
    1. Hi B-ran,
      there is a fork of this code at Github: https://github.com/spyhunter99/installcert .
      That fork was created with an intention to achieve embeddability ( see https://stackoverflow.com/questions/7084482/how-to-save-the-ldap-ssl-certificate-from-openssl/20280562#comment78250553_20280562 ), so your wish for promptless operation might have already been implemented. The project seems to be actively developed, so you may be welcome there with your ideas...

      Delete
  12. Hi Sergey, I am getting below exception, can u guide me

    java -jar installcert-usn-20140115.jar xxxxxxxxx
    ... loading system truststore from '/opt/jdk1.8.0_131/jre/lib/security/cacerts' ...
    ... creating extra truststore as a new one ...
    ... opening connection to xxxx.xxx.xx:443 ...
    ... starting SSL handshake ...
    Exception in thread "main" java.lang.NullPointerException
    at usn.net.ssl.util.InstallCert.main(InstallCert.java:277)

    ReplyDelete