Will the cross root cover trust by the default list in the JDK/JRE?

http://alvinalexander.com/blog/post/java/simple-https-example

Works fine for me with LE issued certificates.

java version "1.7.0_80"
Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.80-b11, mixed mode)
1 Like

Confirmed NOT working with java version "1.8.0_65"
Java™ SE Runtime Environment (build 1.8.0_65-b17)
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

1 Like

Which certificate store does Java use? Does it use the system one? Really don’t see any issue here on Linux with Java. Which domain did you test?

It works on OpenJDK on Ubuntu. But Oracle Java does not accept it; not even on Ubuntu.

Since there is no OpenJDK für Microsoft Windows, I assume that is broken for all java based Feed readers on Windows.

Did you try the oracle java with the openJDK’s keystore? It could simply be that the openJDK one’s been updated to include the relevant root certs

OpenJDK on ubuntu definitely uses a different keystore. You can look at the contents with:

keytool -keystore /etc/ssl/certs/java/cacerts -storepass changeit -list

Similar command for the Oracle JRE/JDK keystore

1 Like

This is the root that allows it to trust LE:

debian:dst_root_ca_x3.pem, Nov 9, 2015, trustedCertEntry,
Certificate fingerprint (SHA1): DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13

2 Likes

Sorry, if this has been posted or answered already, but it may help some people until an official support is made available. Though JAVA may not support the CA out-of-the-box, the CA can be added during runtime of a java program by this simple code.

public static void addRootCA() throws Exception {
InputStream fis = new BufferedInputStream(new FileInputStream(“target/classes/dst_root_ca_x3.pem”));
Certificate ca = CertificateFactory.getInstance(“X.509”).generateCertificate(fis);
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
ks.load(null, null);
ks.setCertificateEntry(Integer.toString(1), ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
SSLContext ctx = SSLContext.getInstance(“TLS”);
ctx.init(null, tmf.getTrustManagers(), null);
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
}

So, calling this function inside URLConnectionReader.java from Firefishy should do the job. I prefer this rather than disabling certificate path checking completely in java by creating a dummy trust manager.

1 Like

It’s pretty trivial to add the CA to the trust list for any standard JDK with the keytool command. Point is - if you have to add the CA it’s not much different than you adding your own local CA — it’s not something you can expect an end-user to do.

If you use services with certificates issued by LE, just load the CA dynamically. The end user doesn’t have anything to do with it then and you’re fine.

Can someone give me instructions for adding to the JRE:s truststore that a retard like me understands?

~/dev/projects/kostbevakningen $ java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)


sudo keytool -trustcacerts -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -file ~/Downloads/isrgrootx1.pem

Still get errors:

[error] Server access Error: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target url…

I’m struggling. I have exactly the same use case (nexus and sbt/ivy) but JDK build 1.8.0_65-b17 (the latest for OS X it seems).

I’ve run:

sudo keytool -trustcacerts -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -file ~/Downloads/isrgrootx1.pem

but it doesn’t help. I guess I need the full chain. Individually adding the three pem:s from here doesn’t work: https://letsencrypt.org/certificates/. I get errors:

keytool error: java.lang.Exception: Certificate not imported, alias <mykey> already exists

on the two later and on second runnings of the first.

You need to use a different alias for each imported cert.

i.e. if It’s a chain of “root”, “intermediate1”, “intermediate2” - specify “-alias leroot”, “-alias leint1”, “-alias leint2” for example when doing the imports.

You should also validate that your server is returning the intermediates/chain.

1 Like

Found out the pem-file didn’t work for me here: https://letsencrypt.org/certs/letsencryptauthorityx1.pem.

`keytool error: java.lang.Exception: Input not an X.509 certificate``

But the .der file worked fine. So for me:

wget https://letsencrypt.org/certs/isrgrootx1.pem
wget https://letsencrypt.org/certs/letsencryptauthorityx1.der

sudo keytool -trustcacerts -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias isrgrootx1 -file ~/Downloads/isrgrootx1.pem
sudo keytool -trustcacerts -keystore /Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home/jre/lib/security/cacerts -storepass changeit -noprompt -importcert -alias letsencryptauthorityx1 -file ~/Downloads/letsencryptauthorityx1.der

Hi, this is the output of keytool -list

lets-encrypt-x2, 20/01/2016, trustedCertEntry, 
Huella Digital de Certificado (SHA1): 02:00:7A:05:CE:D3:68:99:AA:8A:03:A2:CF:30:7F:1C:04:49:FC:31
lets-encrypt-x1, 20/01/2016, trustedCertEntry, 
Huella Digital de Certificado (SHA1): 3E:AE:91:93:7E:C8:5D:74:48:3F:F4:B7:7B:07:B4:3E:2A:F3:6B:F4
isrgrootx1, 20/01/2016, trustedCertEntry, 
Huella Digital de Certificado (SHA1): CA:BD:2A:79:A1:07:6A:31:F2:1D:25:36:35:CB:03:9D:43:29:A5:E8
signfiles, 20/01/2016, PrivateKeyEntry, 
Huella Digital de Certificado (SHA1): 38:11:0A:E1:0C:00:31:6C:AF:84:9A:DA:98:B9:10:F1:DA:EA:9A:86
letsencryptauthorityx1, 20/01/2016, trustedCertEntry, 
Huella Digital de Certificado (SHA1): E0:45:A5:A9:59:F4:27:80:FA:5B:D7:62:35:12:AF:27:6C:F4:2F:20

My program is still returning:
```sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target````

What should I check?

What about getting cross signature from another authority that’s already in java keystore in older versions as well?
The problem is still present with jre 1.8.0_71.

FYI: problem is still here with jdk-8u74.
It seems that, at least under Linux debian/ubuntu/mint/…, OpenJDK installer symlinks $JAVA_HOME/jre/lib/secutity/cacerts to /etc/ssl/certs/java/cacerts in order to have all certifications centralized.
Oracle JDK/JRE rely on it’s own internal cacerts (as expected).
I “solved” the problem doing the same with my hand-installed Oracle JDK:

cd $JAVA_HOME/jre/lib/security/ && mv cacerts cacerts-orig && ln -s /etc/ssl/certs/java/cacerts .

To solve the problem it might be enough to provide a specialized installer for Oracle JDK.

I still get SunCertPathBuilderException even with your code. I’m using Unirest which uses Apache HttpComponents.

Could you give the following a try?
Just change the URL part to your ssl enabled site (which is signed by letsencrypt):

package com.sandbox;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

public class CheckSSL {

public static void main(String[] args) throws Exception {
	addRootCA();
    URL oracle = new URL("https://yoursslsitewhichissignedbytheca/");
    URLConnection yc = oracle.openConnection();
    BufferedReader in = new BufferedReader(new InputStreamReader(
                                yc.getInputStream()));
    String inputLine;
    while ((inputLine = in.readLine()) != null)
        System.out.println(inputLine);
    in.close();
}

public static void addRootCA() throws Exception {
	InputStream fis = new BufferedInputStream(new FileInputStream("target/classes/dst_root_ca_x3.pem"));
	Certificate ca = CertificateFactory.getInstance("X.509").generateCertificate(fis);

	KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
	ks.load(null, null);
	ks.setCertificateEntry(Integer.toString(1), ca);

	TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
	tmf.init(ks);

	SSLContext ctx = SSLContext.getInstance("TLS");
	ctx.init(null, tmf.getTrustManagers(), null);		
	
	HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
	
}

public static void disableCertificateValidation() {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] { 
      new X509TrustManager() {
        public X509Certificate[] getAcceptedIssuers() { 
          return new X509Certificate[0]; 
        }
        public void checkClientTrusted(X509Certificate[] certs, String authType) {}
        public void checkServerTrusted(X509Certificate[] certs, String authType) {}
    }};

    // Ignore differences between given hostname and certificate hostname
    HostnameVerifier hv = new HostnameVerifier() {
      public boolean verify(String hostname, SSLSession session) { return true; }
    };

    // Install the all-trusting trust manager
    try {
      SSLContext sc = SSLContext.getInstance("SSL");
      sc.init(null, trustAllCerts, new SecureRandom());
      HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
      HttpsURLConnection.setDefaultHostnameVerifier(hv);
    } catch (Exception e) {}
  }

}

And if that doesn’t work, try using the disableCertificateValidation() instead of addRootCA() in the main method

Thanks for offering this fix. I just spent $15 and half an hour to make the problem go away for three years, so I’m not really in a position to test it now.