关于调用第三方接口,证书的认证问题

关于证书验证,网上许多文章可能会有很大区别,有些属于自签证书,这个没在实战中使用过,所以文章只说明一些特殊接口的证书问题。

文章只以我自己项目举例,不通用所有情况。

文章证书来源

由于我负责的接口,是需要先向第三方接口提供方提交证书,进行报备,然后使用提交的证书,进行证书认证,如果你的问题是自签证书,可能本文章的思路无法帮助到你,文章我会贴出部分源码

证书说明

由客户提供的证书(证书格式为PEM文件),同时提供了一个.key的私钥文件

PEM文件内容为

-----BEGIN CERTIFICATE-----

加密后的数据

-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----

加密后的数据

-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----

加密后的数据

-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----

加密后的数据

-----END CERTIFICATE-----

其实是四个证书组合成的一个证书,分别是Internal Root CA,apigateway,BlueCerts,OnDemand Issuing可能描述的不太准确,这个是我在第三方接口说明获取到的信息,可以不用纠结这个,因为证书生成跟你没关系

KEY文件内容为

----BEGIN RSA PRIVATE KEY-----

加密后的数据

-----END RSA PRIVATE KEY-----

网上许多参考文献,其实对这个说明很模棱两可,有些说需要格式转换,但是我在使用过程中,并没有进行格式转换

组合证书

到这一步,可能会难倒很多人,因为网上各种说法,真的很容易让人混乱,我大概整理了一下我的思路

关于证书

PEM文件:采用 PEM 编码格式的 X.509 证书的文件扩展名;

DER文件:采用 DER 编码格式的 X.509 证书的文件扩展名;

CRT文件:UNIX 系统,可能是 PEM 编码,也可能是 DER 编码,大多数情况下都是采用 PEM 编码;

CER文件:Windows 系统,可能是 PEM 编码,也可能是 DER 编码,大多数情况下都是采用 DER 编码

他们都是使用X.509的证书文件,只是编码格式有区别,这个需要搞清楚,不要搞混了,都是可以相互转换的,具体转换命令自行百度

key文件

key文件有很多种格式,key文件是私钥,可能是.key.pem或者其他,其实跟上面证书意思差不多,都是编码格式,这里客户提供给我们的文件是.key的后缀文件

-----BEGIN PRIVATE KEY-----

-----END PRIVATE KEY-----

-----BEGIN RSA PRIVATE KEY-----

-----END RSA PRIVATE KEY-----

证书 + key组合最终用于认证的文件

最终的文件为PKCS#12文件,他有两种后缀,一种为pfx和p12,两种区别不大

使用下列命令

将PEM证书文件和私钥转换为PKCS#12(.pfx .p12)

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt

-out文件,我理解为一个组合文件,证书 + 私钥,可以为.pfx文件,p12文件

-inkey文件,表示私钥,一般为带有key后缀或者,.key.pem等,如果没有带有key后缀,可以查看文件,参看上面

-in文件,表示证书,参考证书说明,可以是.crt .cer .pem .der

上面OpenSSL命令还有另一个版本,但是我实际应用中,没有用到(我没有深入了解原理,只是开发中使用到了)

openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CACert.crt

这里的-certfile文件 CACert.crt文件,其实我不太懂,他具体是用来干嘛的

执行上面的命令以后,会提示你输入密码,我设置了密码(这个不设密码我没有试过)

下面是我遇到的坑,仅供参考

  1. 我设置的密码很长,因为是密码是第三方提供的接口账号的密码,在Windows操作窗口使用OpenSSL执行转换命令后,生成的文件会有问题(具体问题未知,可能是我没有配置环境变量),不是无法生成,而是生成的文件有问题,所以我在公司服务器Linux上进行转换的文件

关于代码应用

先说代码的坑,之前网上各种找案例,最后发现,好像与自己的需求不太匹配。

/*
clientConfig是我定义的配置信息,包括需要访问的地址,搭配证书使用的参数等
*/
private HttpResponse merchantQARegisterSend(String param, String fileUrl, ClientConfig clientConfig) {
        HttpResponse response = null;
        try (FileInputStream inStream = new FileInputStream(fileUrl)) {
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(inStream, clientConfig.getClientSecret().toCharArray());
            SSLContext sslcontext = SSLContexts.custom()
                    .loadKeyMaterial(keyStore, clientConfig.getClientSecret().toCharArray()).build();

            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                    sslcontext, new String[]{"TLSv1", "TLSv1.2"}, null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

            org.apache.http.client.config.RequestConfig defaultRequestConfig = org.apache.http.client.config.RequestConfig.custom()
                    .setSocketTimeout(5000)
                    .setConnectTimeout(5000)
                    .setConnectionRequestTimeout(5000)
                    .setStaleConnectionCheckEnabled(true)
                    .build();

            CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(defaultRequestConfig)
                    .setSSLSocketFactory(sslsf)
                    .build();

            HttpPost post = new HttpPost(clientConfig.getUrl());

            org.apache.http.client.config.RequestConfig requestConfig = org.apache.http.client.config.RequestConfig.custom()
                    .setConnectTimeout(90 * 1000)
                    .setSocketTimeout(90 * 1000)
                    .build();

            post.setConfig(requestConfig);
            properties.put("KEY", clientConfig.getKey());
            StringEntity entity = new StringEntity(param, "utf-8");
            entity.setContentType("application/json");
            post.setEntity(entity);

            Properties properties = new Properties();

            PropertiesConfigurationProvider configurationProvider = new PropertiesConfigurationProvider();
            configurationProvider.setProperties(properties);

            AuthProvider authProvider = HmacAuthBuilder.getBuilder()
                    .setConfiguration(configurationProvider)
                    .build();
            //转换成map,写入请求的header中
            Map<String, String> headers = authProvider.generateAuthHeaders(param, clientConfig.getUrl(), "POST");
            for (Map.Entry<String, String> header : headers.entrySet()) {
                post.setHeader(header.getKey(), header.getValue());
            }

            response = httpclient.execute(post);
        } catch (Exception e) {
            
        }
        return response;
    }
### 可能的原因 当调用第三方API时遇到SSL证书验证失败的问题,这通常是由于源应用程序所在的JVM信任库中不存在目标服务器的证书或其完整的证书链所引起的[^3]。 ### 解决方案概述 针对这一问题存在多种解决方案,具体取决于使用的HTTP客户端以及安全需求。以下是几种常见的处理方式: #### 方法一:导入有效的CA根证书Java的信任存储 如果问题是因缺少特定的CA签发的有效证书,则最正规的做法是从官方渠道获取正确的CA根证书并将其安装到运行环境中的`cacerts`文件里。对于基于Java的应用来说,这意味着更新JRE/JDK自带的信任库。 #### 方法二:禁用SSL验证(仅限测试) 虽然这种方法简单直接,但在生产环境下强烈不建议这样做,因为这样会使通信变得不再安全。然而,在某些情况下为了快速定位其他潜在问题或者是在受控环境中进行临时性的调试工作,可以通过修改HttpClinet、Feign Client 或 OkHttpClient 配置来跳过SSL握手过程中的身份校验步骤[^4]。 对于Feign Client而言,可以在启动参数中加入如下配置项以关闭默认的安全检查机制[^1]: ```properties feign.httpclient.disableSslValidation=true ``` 而对于OkHttpClient则需编程实现自定义的`X509TrustManager` 和 `HostnameVerifier`, 并应用到实例构建器上: ```java import javax.net.ssl.*; ... // 创建一个接受所有证书的信任管理器 final TrustManager[] trustAllCerts = new TrustManager[]{ new X509TrustManager() { @Override public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {} @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() {return null;} } }; try { SSLContext sc = SSLContext.getInstance("TLS"); sc.init(null, trustAllCerts, new java.security.SecureRandom()); // 使用上述上下文创建 SSLSocketFactory 实例 final SSLSocketFactory sslSocketFactory = sc.getSocketFactory(); client = new OkHttpClient.Builder() .sslSocketFactory(sslSocketFactory, (X509TrustManager)trustAllCerts[0]) .hostnameVerifier((hostname, session) -> true) .build(); } catch (Exception e){ throw new RuntimeException(e); } ``` 请注意这种方式极大地削弱了HTTPS协议所提供的安全保障功能,因此只应在绝对必要的情况下谨慎采用,并尽快恢复正常的认证流程。 #### 方法三:利用 mkcert 工具生成本地可信证书 在开发阶段或是内部网络部署服务时,可以考虑使用像[mkcert](https://github.com/FiloSottile/mkcert)[^5]这样的工具来自动生成被操作系统认可的SSL/TLS证书。这些证书适用于localhost和其他任意指定域名,非常适合用于模拟真实世界的HTTPS交互而不必担心SSL警告信息干扰正常业务逻辑测试。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值