Java 使用信任库时HTTPS证书验证失败

Java 使用信任库时HTTPS证书验证失败,java,ssl,https,Java,Ssl,Https,我得到以下错误 sun.security.validator.validator异常:PKIX路径生成失败:sun.security.provider.certpath.SunCertPathBuilderException:找不到请求目标的有效证书路径 连接到谷歌地图地理编码API时。 我能够在一个简单的主程序中重现错误。以下是如何使用此测试程序复制它: import javax.net.ssl.*; import java.net.*; import java.io.*; public c

我得到以下错误

sun.security.validator.validator异常:PKIX路径生成失败:sun.security.provider.certpath.SunCertPathBuilderException:找不到请求目标的有效证书路径

连接到谷歌地图地理编码API时。 我能够在一个简单的主程序中重现错误。以下是如何使用此测试程序复制它:

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 编译它

javac Main.java

运行它

javamain

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

但是如果我从这里创建一个带有证书的信任库: 我下载了SSL证书并将其保存为一个名为clic.gob.ar的X.509 PEM文件

创建一个名为keystorefede.jks的新信任库

keytool-import-file clic.gob.ar-alias clicCert-keystorekeystorefede.jks

我给了它密码。我可以把它列出来

keytool-list-keystorekeystorefede.jks-storepass-tompass

阿拉木图:JKS 阿尔马西恩德克拉夫斯学院院长:太阳

苏阿尔马塞恩·德克拉夫斯·康塔达1号酒店

cliccert,2014年8月1日,trustedCertEntry, Huella数字认证(SHA1):15:3B:67:EE:51:C9:F2:CF:68:7C:24:51:A4:B6:6E:AE:EA:61:D5:B5

现在使用信任库运行相同的程序

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

Java8和Java7都会出现这种情况

java版本“1.8.0_11”

Java(TM)SE运行时环境(build 1.8.0_11-b12)

Java HotSpot(TM)64位服务器虚拟机(构建25.11-b03,混合模式)

这个问题首先是在Tomcat内部运行的web应用程序中发现的。对于另一个请求,证书必须位于Tomcat命令行的信任库中;信任库没有与google相关的内容,只有一个证书。 如果我将谷歌的证书添加到信任存储中,那么问题就解决了,但这不是一个合适的解决方案。 谷歌不接受使用API密钥通过http进行地理编码的请求。
如果-Djavax.net.ssl.trustStore覆盖了根CA的所有Java知识?

应用程序找不到正确的信任库。 您可以这样定义它:

setProperty(“javax.net.ssl.trustStore”,“\\src\\trustStore.jks”);

setProperty(“javax.net.ssl.trustStorePassword”、“psw123”)

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

默认情况下,JVM附带一个信任库,该信任库预先填充了相当多的知名权限(Oracle JVM将其存储在JAVA_HOME/jre/lib/security/cacerts中)

默认情况下,JSSE(Java安全套接字扩展)将使用该密钥库作为默认值来验证SSL握手


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

展望未来,您有一些解决方案:
一个是:你建立你自己的JKS,包含你所需要的一切。
第二个是:将证书添加到JVM的默认文件中。
第三个是:你的代码

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

HTTPURLConnection下面的套接字由
SocketFactory
实例组成。 当涉及HTTPS时,您需要使用呼叫所需的证书/私钥初始化自己的SSLSocketFactory,并在连接之前将SocketFactory与HTTPURLConnection关联:请参阅

这个是这样工作的。首先,您需要加载密钥库(包含您的证书的JKS文件,用于缩短的异常处理剪切):

一旦有了密钥库实例,就可以构建一个“TrustManager”,它将使用密钥库中声明为受信任的任何证书作为有效的信任锚

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执行相同的操作(如果使用双向SSL),模式完全相同。一旦有了TrustManagerFactory和KeyManagerFactory实例,就可以构建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();

我编写了一个手工构建SSLContext的小型Java应用程序(与您在回答中描述的非常类似,只是我在
sslCtx.init(…)
信任管理器方法中传递null)。我问了一个关于这个的问题。我遇到的问题是,我从Azure KeyVault获得X509Certificate对象的句柄,而不是密钥库文件输入流。不知道如何从那里开始。请给我一些建议。
  URL url = new URL("https://test.com/test");
  URLConnection conn = url.openConnection();
  if(conn instanceof HttpsURLConnection) {
    ((HttpsURLConnection) conn).setSSLSocketFactory(sslSF);
  }
  conn.connect();