Does Java support Let's Encrypt certificates?

[Update 2016-06-08: According to the IdenTrust CA will be included in Oracle Java 8u101.]

[Update 2016-08-05: Java 8u101 has been released and does indeed include the IdenTrust CA: release notes]

Does Java support Let's Encrypt certificates?

Yes. The Let's Encrypt certificate is just a regular public key certificate. Java supports it (according to Let's Encrypt Certificate Compatibility, for Java 7 >= 7u111 and Java 8 >= 8u101).

Does Java trust Let's Encrypt certificates out of the box?

No / it depends on the JVM. The truststore of Oracle JDK/JRE up to 8u66 contains neither the Let's Encrypt CA specifically nor the IdenTrust CA that cross signed it. new URL("").openConnection().connect(); for example results in

You can however provide your own validator / define a custom keystore that contains the required root CA or import the certificate into the JVM truststore. discusses the topic as well.

Here is some example code that shows how to add a certificate to the default truststore at runtime. You'll just need to add the certificate (exported from firefox as .der and put in classpath)

Based on How can I get a list of trusted root certificates in Java? and

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;


public class SSLExample {
    // BEGIN ------- ADDME
    static {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            Path ksPath = Paths.get(System.getProperty("java.home"),
                    "lib", "security", "cacerts");

            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            try (InputStream caInput = new BufferedInputStream(
                    // this files is shipped with the application
                    SSLExample.class.getResourceAsStream("DSTRootCAX3.der"))) {
                Certificate crt = cf.generateCertificate(caInput);
                System.out.println("Added Cert for " + ((X509Certificate) crt)

                keyStore.setCertificateEntry("DSTRootCAX3", crt);

            if (false) { // enable to see
                System.out.println("Truststore now trusting: ");
                PKIXParameters params = new PKIXParameters(keyStore);

            TrustManagerFactory tmf = TrustManagerFactory
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);
        } catch (Exception e) {
            throw new RuntimeException(e);
    // END ---------- ADDME

    public static void main(String[] args) throws IOException {
        // signed by default trusted CAs.
        testUrl(new URL(""));
        testUrl(new URL(""));

        // signed by letsencrypt
        testUrl(new URL(""));
        // signed by LE's cross-sign CA
        testUrl(new URL(""));
        // expired
        testUrl(new URL(""));
        // self-signed
        testUrl(new URL(""));


    static void testUrl(URL url) throws IOException {
        URLConnection connection = url.openConnection();
        try {
            System.out.println("Headers of " + url + " => "
                    + connection.getHeaderFields());
        } catch (SSLHandshakeException e) {
            System.out.println("Untrusted: " + url);


I know the OP asked for a solution without local configuration changes, but in case you want to add the trust chain to the keystore permanently:

$ keytool -trustcacerts \
    -keystore $JAVA_HOME/jre/lib/security/cacerts \
    -storepass changeit \
    -noprompt \
    -importcert \
    -file /etc/letsencrypt/live/


Detailed answer for those of us willing to make local config changes that includes backing up the config file:

1. Test if it is working before the changes

If you don't have a test program already, you can use my java SSLPing ping program which tests the TLS handshake (will work with any SSL/TLS port, not just HTTPS). I'll use the prebuilt SSLPing.jar, but reading the code and building it yourself is a quick and easy task:

$ git clone
Cloning into 'SSLPing'...
[... output snipped ...]

Since my Java version is earlier than 1.8.0_101 (not released at the time of this writing), a Let's Encrypt certificate will not verify by default. Let's see what failure looks like before applying the fix:

$ java -jar SSLPing/dist/SSLPing.jar 443
About to connect to '' on port 443 PKIX path building failed: unable to find valid certification path to requested target
[... output snipped ...]

2. Import the certificate

I'm on Mac OS X with the JAVA_HOME environment variable set. Later commands will assume this variable is set for the java installation you are modifying:

$ echo $JAVA_HOME 

Make a backup of the cacerts file we will be modifying so you can back out any change without reinstalling the JDK:

$ sudo cp -a $JAVA_HOME/jre/lib/security/cacerts $JAVA_HOME/jre/lib/security/cacerts.orig

Download the signing certificate we need to import:

$ wget

Perform the import:

$ sudo keytool -trustcacerts -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias lets-encrypt-x3-cross-signed -file lets-encrypt-x3-cross-signed.der 
Certificate was added to keystore

3. Verify that it is working after the changes

Verify that Java is now happy connecting to the SSL port:

$ java -jar SSLPing/dist/SSLPing.jar 443
About to connect to '' on port 443
Successfully connected