简体   繁体   English

使用trustStore时,HTTPS证书验证失败

[英]HTTPS certificate validation fails when using a trustStore

I'm getting the following error 我收到以下错误

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到请求目标的有效证书路径

when connecting to google maps geocoding API. 当连接谷歌地图地理编码API时。 I am able to reproduce the error in a simple Main program. 我能够在一个简单的主程序中重现错误。 Here's how to reproduce it with this test program: 以下是使用此测试程序重现它的方法:

import javax.net.ssl.*;
import java.net.*;
import java.io.*;

public class Main {

    public static void main(String[] args) {
        try {
            String httpsURL = "https://maps.googleapis.com/maps/api/geocode/json?address=49+874%2Cla+plata%2Cbuenos+aires%2Cargentina&sensor=false&key=AIzaSyAJ1QS0C6KjiWajwxx4jUb_Jz0b8lBZyyE";
            URL myurl = new URL(httpsURL);
            HttpsURLConnection con = (HttpsURLConnection) myurl.openConnection();
            InputStream ins = con.getInputStream();
            InputStreamReader isr = new InputStreamReader(ins);
            BufferedReader in = new BufferedReader(isr);
            String inputLine;
            while ((inputLine = in.readLine()) != null) {
                System.out.println(inputLine);
            }
            in.close();
        } catch (IOException ex) {
            System.err.println(ex.getMessage());
        }
    }
}

Saved as Main.java Compile it 保存为Main.java编译它

javac Main.java

Run it 运行

java Main

I get the normal result (json response is printed). 我得到了正常的结果(打印了json响应)。

But if I create a TrustStore with a certificate from here: https://www.clic.gob.ar/ I downloaded the SSL certificate and saved it as an X.509 PEM file named clic.gob.ar 但是,如果我从这里创建一个带有证书的TrustStore: https ://www.clic.gob.ar/我下载了SSL证书并将其保存为名为clic.gob.ar的X.509 PEM文件

Create a new TrustStore named keystorefede.jks 创建一个名为keystorefede.jks的新TrustStore

keytool -import -file clic.gob.ar -alias clicCert -keystore keystorefede.jks

I gave it password tompass. 我给了它密码tompass。 I can list it 我可以列出来

keytool -list -keystore keystorefede.jks -storepass tompass

Tipo de Almacén de Claves: JKS Proveedor de Almacén de Claves: SUN TipodeAlmacéndeClaves:JKS ProveedordeAlmacéndeClaves:SUN

Su almacén de claves contiene 1 entrada Sualmacéndeclaves contiene 1 entrada

cliccert, 01/08/2014, trustedCertEntry, Huella Digital de Certificado (SHA1): 15:3B:67:EE:51:C9:F2:CF:68:7C:24:51:A4:B6:6E:AE:EA:61:D5:B5 cliccert,01/08/2014,trustedCertEntry,Huella Digital de Certificado(SHA1):15:3B:67:EE:51:C9:F2:CF:68:7C:24:51:A4:B6:6E:AE: EA:61:D5:B5

Now run the same program using the trustStore 现在使用trustStore运行相同的程序

java -Djavax.net.ssl.trustStore=/home/fede/keystorefede.jks -Djavax.net.ssl.trustStorePassword=tompass Main sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target java -Djavax.net.ssl.trustStore=/home/fede/keystorefede.jks -Djavax.net.ssl.trustStorePassword=tompass Main sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath。 SunCertPathBuilderException:无法找到请求目标的有效证书路径

This happens with Java 8 and also Java 7. Java 8和Java 7都会发生这种情况。

java version "1.8.0_11" java版“1.8.0_11”

Java(TM) SE Runtime Environment (build 1.8.0_11-b12) Java(TM)SE运行时环境(版本1.8.0_11-b12)

Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode) Java HotSpot(TM)64位服务器VM(内置25.11-b03,混合模式)

The problem was first discovered inside a web application running inside Tomcat. 该问题最初是在Tomcat内部运行的Web应用程序中发现的。 The certificate has to be in a TrustStore in Tomcat's command line for another request; 证书必须在Tomcat的命令行中的TrustStore中用于另一个请求; the trust store has nothing google related, just one certificate. 信任商店没有谷歌相关的,只有一个证书。 If I add Google's certificate to the trust store then the problem is solved, but this is not a proper solution. 如果我将Google的证书添加到信任存储区,那么问题就解决了,但这不是一个合适的解决方案。 Google does not accept geocoding requests over http using an API key. Google不接受使用API​​密钥通过http进行地理编码请求。 It the -Djavax.net.ssl.trustStore overriding all the root CA's Java knows? -Djavax.net.ssl.trustStore覆盖所有根CA的Java知道吗?

The javax.net.ssl.trustStore property, indeed, overrides all known certificates of the JVM with the one you provide. 实际上,javax.net.ssl.trustStore属性会使用您提供的JVM覆盖JVM的所有已知证书。

By default, the JVM comes with a trustStore prepopulated with a fairly decent number of well known authorities (the Oracle JVM stores it in JAVA_HOME/jre/lib/security/cacerts). 默认情况下,JVM附带一个trustStore,预先填充了相当多的众所周知的权限(Oracle JVM将其存储在JAVA_HOME / jre / lib / security / cacerts中)。

This keyStore will be used as the default by the JSSE (Java Secure Socket Extension) by default to validate SSL handshakes. 默认情况下,此keyStore将被JSSE(Java安全套接字扩展)用作默认值,以验证SSL握手。

The javax.net.ssl.trustStore environnement variable overrides this default location, meaning none of its content's are relevant any more. javax.net.ssl.trustStore环境变量会覆盖此默认位置,这意味着其内容不再相关。

Going forward, you have a few solutions: 展望未来,您有几个解决方案:
One is : you build your own JKS containing everything you need. 一个是:你建立自己的JKS,包含你需要的一切。
Second is : you add certificates to your JVM's default file. 其次是:您将证书添加到JVM的默认文件中。
Third is : you code. 第三是:你编码。

Getting your own SSL Context "by hand" ? “手动”获取自己的SSL上下文?

Sockets that underly HTTPURLConnection are made out of SocketFactory instances. 底层HTTPURLConnection的套接字由SocketFactory实例构成。 When HTTPS is involved, what happens is that you need to initialize your own SSLSocketFactory with wathever certificate/private keys are needed for your call, and associate the SocketFactory with the HTTPURLConnection before connecting it : see http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/HttpsURLConnection.html#setSSLSocketFactory%28javax.net.ssl.SSLSocketFactory%29 当涉及HTTPS时,会发生的情况是您需要使用监视器初始化您自己的SSLSocketFactory,并且在连接之前将SocketFactory与HTTPURLConnection关联起来:请参阅http://docs.oracle.com/ JavaSE的/ 7 /文档/ API /的javax /净/ SSL / HttpsURLConnection.html#setSSLSocketFactory%28javax.net.ssl.SSLSocketFactory%29

This works like this. 这是这样的。 First, you need to load your KeyStore (JKS file containing your certificate, exception handling cut for shortening) : 首先,您需要加载KeyStore(包含证书的JKS文件,用于缩短的异常处理):

InputStream keyStoreStream = ... // Wherever it is
KeyStore ks = KeyStore.getInstance("JKS"); // or "PKCS12" for pfx/p12
ks.load(is, password);

Once you have a KeyStore instance, you can build a "TrustManager" that will use any certificates declared as trusted in the Keystore as valid trust anchors. 获得KeyStore实例后,您可以构建一个“TrustManager”,它将使用在Keystore中声明为受信任的任何证书作为有效的信任锚。

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); // PKIX
tmf.init(yourKeyStore); // if you pass null, you get the JVM defaults
                        // which is CACerts file or javax.net.ssl.trustStore

You can do the same for your SSL KeyManagerFactory (if you use 2 way SSL), the pattern is exactly the same. 您可以对SSL KeyManagerFactory执行相同的操作(如果使用双向SSL),则模式完全相同。 Once you have TrustManagerFactory and KeyManagerFactory instances, you are ready to build a SSLSocketFactory. 一旦有了TrustManagerFactory和KeyManagerFactory实例,就可以构建一个SSLSocketFactory。

  SSLContext sslCtx = SSLContext.getInstance("TLS");
  sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
  SSLSocketFactory sslSF = sslCtx.getSocketFactory();

At this point, you can do 此时,你可以做到

  URL url = new URL("https://test.com/test");
  URLConnection conn = url.openConnection();
  if(conn instanceof HttpsURLConnection) {
    ((HttpsURLConnection) conn).setSSLSocketFactory(sslSF);
  }
  conn.connect();

The application doesn't find the right truststore. 该应用程序找不到合适的信任库。 You can define it that way: System.setProperty("javax.net.ssl.trustStore", ".\\\\src\\\\truststore.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "psw123"); 您可以通过以下方式定义它: System.setProperty("javax.net.ssl.trustStore", ".\\\\src\\\\truststore.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "psw123"); System.setProperty("javax.net.ssl.trustStore", ".\\\\src\\\\truststore.jks"); System.setProperty("javax.net.ssl.trustStorePassword", "psw123");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM