HTTPS certificate validation fails when using a trustStore

后端 未结 2 1076
时光说笑
时光说笑 2021-02-03 14:23

I\'m getting the following error

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException

2条回答
  •  伪装坚强ぢ
    2021-02-03 14:39

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

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

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

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

    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.

    Getting your own SSL Context "by hand" ?

    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

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

    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
    

    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();
    

    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();
    

提交回复
热议问题