使用trustStore时HTTPS证书验证失败 [英] HTTPS certificate validation fails when using a trustStore
问题描述
我遇到以下错误
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屋!