使用trustStore时HTTPS证书验证失败 [英] HTTPS certificate validation fails when using a trustStore

查看:189
本文介绍了使用trustStore时HTTPS证书验证失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到以下错误

sun.security.validator.ValidatorException:PKIX路径构建失败:sun.security.provider.certpath.SunCertPathBuilderException:无法找到到请求目标的有效认证路径

连接到Google Maps地理编码API时. 我能够在一个简单的Main程序中重现该错误.以下是通过此测试程序重现它的方法:

when connecting to google maps geocoding 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());
        }
    }
}

另存为Main.java 编译

Saved as Main.java Compile it

javac Main.java

运行

java Main

我得到正常结果(打印出json响应).

I get the normal result (json response is printed).

但是,如果我从此处创建带有证书的TrustStore: https://www.clic.gob.ar/ 我下载了SSL证书,并将其保存为名为clic.gob.ar

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

创建一个名为keystorefede.jks的新TrustStore

Create a new TrustStore named keystorefede.jks

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

我给它设置了密码tompass.我可以列出

I gave it password tompass. I can list it

keytool -list -keystore keystorefede.jks -storepass tompass

Tipo deAlmacénde Claves:JKS 阿尔马克·德·克拉夫斯(Provedor deAlmacénde Claves):周日

Tipo de Almacén de Claves: JKS Proveedor de Almacén de Claves: SUN

Sualmacénde claves contiene 1 entrada

Su almacén de claves 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

现在使用trustStore运行相同的程序

Now run the same program using the trustStore

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:无法找到到请求目标的有效认证路径

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 8和Java 7都会发生这种情况.

This happens with Java 8 and also Java 7.

java版本"1.8.0_11"

java version "1.8.0_11"

Java(TM)SE运行时环境(内部版本1.8.0_11-b12)

Java(TM) SE Runtime Environment (build 1.8.0_11-b12)

Java HotSpot(TM)64位服务器VM(内部版本25.11-b03,混合模式)

Java HotSpot(TM) 64-Bit Server VM (build 25.11-b03, mixed mode)

该问题最早是在Tomcat内运行的Web应用程序中发现的.该证书必须位于Tomcat命令行中的TrustStore中,才能进行其他请求.信任库与Google没有任何关系,只有一个证书. 如果我将Google的证书添加到信任存储中,则可以解决此问题,但这不是适当的解决方案. Google不接受使用API​​密钥通过http进行地理编码请求. -Djavax.net.ssl.trustStore是否覆盖了Java知道的所有根CA?

The problem was first discovered inside a web application running inside Tomcat. The certificate has to be in a TrustStore in Tomcat's command line for another request; 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 does not accept geocoding requests over http using an API key. It the -Djavax.net.ssl.trustStore overriding all the root CA's Java knows?

推荐答案

确实,javax.net.ssl.trustStore属性用您提供的JVM覆盖了所有已知的JVM证书.

The javax.net.ssl.trustStore property, indeed, overrides all known certificates of the JVM with the one you provide.

默认情况下,JVM附带一个trustStore,其中预填充了相当数量的知名机构(Oracle JVM将其存储在JAVA_HOME/jre/lib/security/cacerts中).

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).

默认情况下,JSSE(Java安全套接字扩展)将使用此keyStore作为默认值,以验证SSL握手.

This keyStore will be used as the default by the JSSE (Java Secure Socket Extension) by default to validate SSL handshakes.

javax.net.ssl.trustStore环境变量将覆盖此默认位置,这意味着其任何内容都不再相关.

The javax.net.ssl.trustStore environnement variable overrides this default location, meaning none of its content's are relevant any more.

展望未来,您有几种解决方案:
一种是:构建包含所需内容的自己的JKS.
第二个是:将证书添加到JVM的默认文件中.
第三是:您编写代码.

Going forward, you have a few solutions:
One is : you build your own JKS containing everything you need.
Second is : you add certificates to your JVM's default file.
Third is : you code.

手动"获取自己的SSL上下文?

基于HTTPURLConnection的套接字由SocketFactory实例组成. 当涉及HTTPS时,发生的事情是您需要使用Wathever证书/私钥来初始化自己的SSLSocketFactory,并且将其与HTTPURLConnection关联后才能进行连接:请参阅

Sockets that underly HTTPURLConnection are made out of SocketFactory instances. 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

这是这样的.首先,您需要加载您的KeyStore(包含证书的JKS文件,为了缩短而减少了异常处理):

This works like this. First, you need to load your KeyStore (JKS file containing your certificate, exception handling cut for shortening) :

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

一旦拥有KeyStore实例,就可以构建一个"TrustManager",它将使用在Keystore中声明为受信任的任何证书作为有效的信任锚.

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.

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

您可以对SSL KeyManagerFactory进行相同的操作(如果使用2路SSL),则模式是完全相同的.有了TrustManagerFactory和KeyManagerFactory实例后,就可以构建SSLSocketFactory了.

You can do the same for your SSL KeyManagerFactory (if you use 2 way SSL), the pattern is exactly the same. Once you have TrustManagerFactory and KeyManagerFactory instances, you are ready to build a SSLSocketFactory.

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

此时,您可以完成

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

这篇关于使用trustStore时HTTPS证书验证失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆