Java 无法发送 https 请求 "unable to find valid certification path"

Java can't send https request "unable to find valid certification path"

我正在尝试通过 https: https://api-sandbox.rabobank.nl/openapi/sandbox 访问沙箱 api 但在发送请求时出现此错误: https://pastebin.com/5X4h3wsu

我尝试将上述 url 的证书添加到 jre11、jdk8、jdk7 信任库,并尝试将我的项目 jdk/jre 切换到这些版本。但是错误没有改变。我也尝试将其设置为 vm options: -Dcom.sun.net.ssl.checkRevocation=false 也没有运气。当我启用:-Djavax.net.debug=ssl(使用 java 8,选项不适用于 11)我得到这个输出到控制台: https://pastebin.com/5L7Lei8J

这是我所有的代码,代码并不多,因为这个应用程序是为了测试 api 用一个最小的工作示例。 Application.java:

package com.thebooks;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

import java.io.File;
import java.security.KeyStore;

@SpringBootApplication
public class Application {

    static {
        System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
        System.setProperty("https.protocols", "TLSv1.2");
        System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.dir") + "/key/cert.p12");
        System.setProperty("javax.net.ssl.trustStorePassword", "secret");
        System.setProperty("javax.net.ssl.keyStore",  System.getProperty("user.dir") + "/key/key.p12");
        System.setProperty("javax.net.ssl.keyStorePassword", "secret");

        javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier(
                new javax.net.ssl.HostnameVerifier() {

                    public boolean verify(String hostname,
                                          javax.net.ssl.SSLSession sslSession) {
                        // TODO: CODE TO VERIFY Host
                        return true;
                    }
                });
    }

    @Bean
    public RestTemplate template() throws Exception {
        return new RestTemplate();
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

HttpClient.java:

package com.thebooks.httpclient;

import com.thebooks.enums.EScope;
import com.thebooks.providers.PropertyProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class HttpClient implements CommandLineRunner {
    private String apiUrl = "https://api-sandbox.rabobank.nl/openapi/sandbox/";

    private final RestTemplate template;
    private PropertyProvider propProvider = new PropertyProvider();
    private final String CLIENT_ID = propProvider.get("client.id");

    public HttpClient(RestTemplate template) {
        this.template = template;
    }

    @Override
    public void run(String... args) throws Exception {
        ResponseEntity<Object> response = template.getForEntity(
                apiUrl + "oauth2/authorize?client_id=" + CLIENT_ID +
                        "&response_type=code&scope=" + EScope.AIS_BALANCES_READ.getValue(),
                Object.class);
        System.out.println(response.getStatusCode());
        System.out.println(response.getBody());
        System.out.println(response.getHeaders());
    }
}

我从拥有我正在尝试使用的 api 的网站获得大部分代码: https://developer-sandbox.rabobank.nl/mutual-tls 我很确定这与我的 pc/app 不信任他们的证书有关。但是就像我说的那样,我将他们的证书添加到我拥有的 java 的所有 3 个版本的所有 3 个证书中......

我在您的日志中看到的是:

trustStore is: C:\Projects\Java\rabobank-test\key\cert.p12
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
  Subject: CN=BAS, O=Internet Widgits Pty Ltd, L=rotterdam, ST=Zuid-Holland, C=NL
  Issuer:  CN=BAS, O=Internet Widgits Pty Ltd, L=rotterdam, ST=Zuid-Holland, C=NL
  Algorithm: RSA; Serial number: 0x28038baf12a3bb7ac23561ced39bccfcd39f4320
  Valid from Sat Oct 05 01:53:40 CEST 2019 until Sun Oct 04 01:53:40 CEST 2020

 keyStore is : C:\Projects\Java\rabobank-test/key/key.p12
...

也就是说,您在trusstore 中只有一个自签名证书。但是服务器正在使用:

*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: CN=api-sandbox.rabobank.nl, OU=IT Infrastructure, O=Cooperatieve Rabobank U.A., L=Utrecht, ST=Utrecht, C=NL
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: 27656546023135416343313786390568312089793225750931433930372419682926699102088570108432798752674580685572610333594008304037355692016847885153850390570343526804649453871166596416120002483261389717541107277089107192149483397960566607102497541257381555870488778889499740452903944947628925771418610305207680346062007754393210604748206767028477705328447039960783496889675884109837662283459562174450768283022227873621702545924115688805804041495718666206232889227995689049914624465380330588827667219738388577693826776185042246003908945385397658276988973592052614956050490934357249690728764920020238886239735311604792591584317
  public exponent: 65537
  Validity: [From: Mon Aug 05 02:00:00 CEST 2019,
               To: Mon Aug 09 14:00:00 CEST 2021]
  Issuer: CN=DigiCert SHA2 High Assurance Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US
  SerialNumber: [    05d8fed0 ed99a7c7 20081752 711f1798]
...
chain [1] = [
[
  Version: V3
  Subject: CN=DigiCert SHA2 High Assurance Server CA, OU=www.digicert.com, O=DigiCert Inc, C=US
  Signature Algorithm: SHA256withRSA, OID = 1.2.840.113549.1.1.11

  Key:  Sun RSA public key, 2048 bits
  modulus: 23085922014910748503624791917480115148492919026914207610707020942093828159221184419960399297678177590153378092714640886296044490661625022319263060388275515964365478738040978664516396912933675650257207760237777280773935047177225664304566903694731631728916260237117586511459590661362255543750987738241463266555577715629664656907640120826399947323444556799362651693283202076722872218490347588587929811327918605576169523712767591239193274840826201053308722900104999956283622772648025895714833602740679819670062830777938157004975732087864164660384513848296643542134747514357423990884765641067184766081973460304136714018531
  public exponent: 65537
  Validity: [From: Tue Oct 22 14:00:00 CEST 2013,
               To: Sun Oct 22 14:00:00 CEST 2028]
  Issuer: CN=DigiCert High Assurance EV Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US
  SerialNumber: [    04e1e7a4 dc5cf2f3 6dc02b42 b85d159f]
...

它不是自签名的,也不会查看任何无法使用默认信任库和 SSL 设置进行验证的地方。

所以只需禁用这些行

        System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.dir") + "/key/cert.p12");
        System.setProperty("javax.net.ssl.trustStorePassword", "secret");

of 如果您需要信任库中的证书,最好使用 java 提供的官方信任库,并将您在 yout cert.p12 中拥有的证书添加到该证书中 - 或者添加服务器链中的任何其他证书提供给那个精简的自定义信任库。 (理想情况下,根证书 "CN=DigiCert High Assurance EV Root CA, OU=www.digicert.com, O=DigiCert Inc, C=US" 是您在 digicert 处获得的,或者可能是在所有当前的默认信任库中,以及安迪应用程序或操作系统随附的类似证书。)

api 沙箱 URL 具有由 Digicert 颁发的证书,它是一个有效的 CA 提供商。 但是根据 spring 引导启动代码,您正在使用自签名证书启动服务器。因此,当在 CA 签名的 SSL 和非 CA 签名的证书之间进行握手时,会出现 PKIK 异常。

请将您在 spring 引导启动中使用的证书包含在 java 的 cacert 文件中。

SSL 的真正本质是握手,它们交换加密密钥。两个启用 SSL 的站点之间的数据发生在它们首先交换加密密钥并继续使用该加密密钥加密数据的地方。而且他们没有解密密钥(与 SSH 相同)。

@BasVelden 基本上 JAVA 信任任何自定义生成的(非 CA 生成的)密钥,我们执行以下操作:

keytool -import -trustcacerts -noprompt -keystore <full path to cacerts> -storepass changeit -alias $REMHOST -file $REMHOST.pem

现在,上面代码的解释是: keytool 是 java 提供的实用程序 : 通常位置是 $JAVA_HOME/jre/lib/security/cacerts。它包含所有经过验证的 (CA) 证书提供商的条目。您正在修改此条目

System.setProperty("javax.net.ssl.trustStore", System.getProperty("user.dir") + "/key/cert.p12");
        System.setProperty("javax.net.ssl.trustStorePassword", "secret");

修改cacerts文件是可以的,但你不应该删除原来的条目。因此,删除该条目对您有帮助,而不是提供您的版本提供程序的路径,您应该提供所有提供程序的上述路径。