java程序跳过https证书验证

问题描述

在企业应用开发场景中,跨系统接口对接是常见需求。现代企业往往部署多个异构系统,这些系统由不同供应商开发,采用不同的技术架构实现。系统间需要通过HTTP/HTTPS协议进行功能调用和数据交互。

HTTPS协议的安全机制可能带来开发挑战。当对接方的HTTPS证书存在配置问题时,标准的Java HTTP客户端会因证书验证失败而拒绝连接。

Java安全体系对HTTPS连接有严格的证书验证机制。客户端会检查服务器证书的颁发机构、有效期、域名匹配等信息。任何一项验证失败都会导致SSLHandshakeException异常。当对接方使用自签名证书或证书链不完整时,这种严格验证机制反而会成为系统集成的障碍。这种情况通常需要特殊处理,暂时绕过证书验证环节,以保障系统间的正常通信。

问题复现

未跳过验证的代码

public static String request(String requestUrl,
                                                  String requestMethod, String body, String contentType, Map<String, String> heads)  throws Exception{
        HttpsURLConnection connection;
        StringBuilder buffer = new StringBuilder();
        try {


            URL url = new URL(requestUrl);
            connection = (HttpsURLConnection) url.openConnection();
            // 创建不验证主机名的 HostnameVerifier
            connection.setHostnameVerifier((hostname, session) -> true);

            // 设置请求头属性
            if(heads != null){
                for (Map.Entry<String, String> map : heads.entrySet()) {
                    String key = map.getKey();
                    String value = map.getValue();
                    connection.setRequestProperty(key, value);
                }
            }

            // 设置请求方式
            connection.setRequestMethod(requestMethod);

            if (StrUtil.isNotEmpty(contentType)){
                connection.setRequestProperty("content-type", contentType);
            }
            // 当有数据需要提交时
            if (null != body) {
                OutputStream outputStream = connection.getOutputStream();
                // 设置编码格式,防止中文乱码
                outputStream.write(body.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(
                    inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            connection.disconnect();
            return buffer.toString();
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

运行结果
在这里插入图片描述

调用上述方法后,运行出现了" javax.net.ssl.SSLHandshakeException"的错误,看这句错误 提示信息:Caused by: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target。这条错误提示信息意味着Java无法找到一条有效的证书链来验证请求的目标(通常是服务器)的证书。这通常是因为服务器的证书没有被一个Java信任的证书颁发机构(CA)签发,或者证书链中的某个证书不在Java的信任库中。

解决方案一

我们可以通过在调用HTTPS接口时跳过证书验证的方式处理上述问题。

具体代码
public static String requestIgnoreSSL(String requestUrl,
                                                  String requestMethod, String body, String contentType, Map<String, String> heads)  throws Exception{
        HttpsURLConnection connection;
        StringBuilder buffer = new StringBuilder();
        try {
            // 创建一个信任所有证书的 TrustManager
            TrustManager[] trustManagers = new TrustManager[]{
                    new X509TrustManager() {
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return null;
                        }
                        public void checkClientTrusted(
                                java.security.cert.X509Certificate[] certs, String authType) {
                        }
                        public void checkServerTrusted(
                                java.security.cert.X509Certificate[] certs, String authType) {
                        }
                    }
            };
            // 安装信任所有证书的TrustManager
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustManagers, new java.security.SecureRandom());

            URL url = new URL(requestUrl);
            connection = (HttpsURLConnection) url.openConnection();
            connection.setSSLSocketFactory(sslContext.getSocketFactory());
            // 创建不验证主机名的 HostnameVerifier
            connection.setHostnameVerifier((hostname, session) -> true);

            // 设置请求头属性
            if(heads != null){
                for (Map.Entry<String, String> map : heads.entrySet()) {
                    String key = map.getKey();
                    String value = map.getValue();
                    connection.setRequestProperty(key, value);
                }
            }

            // 设置请求方式
            connection.setRequestMethod(requestMethod);

            if (StrUtil.isNotEmpty(contentType)){
                connection.setRequestProperty("content-type", contentType);
            }
            // 当有数据需要提交时
            if (null != body) {
                OutputStream outputStream = connection.getOutputStream();
                // 设置编码格式,防止中文乱码
                outputStream.write(body.getBytes("UTF-8"));
                outputStream.close();
            }

            // 将返回的输入流转换成字符串
            InputStream inputStream = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(
                    inputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);

            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            bufferedReader.close();
            inputStreamReader.close();
            // 释放资源
            inputStream.close();
            connection.disconnect();
            return buffer.toString();
        } catch (Exception e) {
            throw new Exception(e);
        }
    }

说明:

  1. 自定义信任管理器(TrustManager):创建一个X509TrustManager,覆盖验证方法以信任所有证书。

    - checkClientTrusted和checkServerTrusted方法留空,跳过证书验证。
    - getAcceptedIssuers返回空数组,表示不限制可接受的CA。
    
  2. 配置SSLContext:使用自定义的TrustManager初始化SSLContext。
    - 使用TLS协议初始化SSLContext。
    - init方法的第一个参数为null表示不使用客户端证书,第二个参数传入信任所有证书的TrustManager数组。

  3. 设置默认的SSL套接字工厂和主机名验证器:替换HttpsURLConnection的默认设置,跳过主机名验证。
    - 设置HostnameVerifier直接返回true,接受所有主机名。

解决方案二

直接使用Hutool工具提供的 HttpRequest 对象,它会默认跳过 SSL 验证。

具体代码

1、引入依赖

<dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
     <version>5.8.24</version>
</dependency>

2、代码示例:

public static void main(String[] args) throws Exception {
   // GET示例
   System.out.println(HttpRequest.get("https://localhost:8080/testGet/").execute().body());
   
   JSONObject jsonObject = new JSONObject();
   jsonObject.put("username", "admin");
   jsonObject.put("password", "123456"); 
	// POST示例   
   System.out.println(HttpRequest.post("https://localhost:8080/login").body(jsonObject.toString()).execute().body());
}

总结

总的来说,如果程序需要跳过HTTPS证书验证,使用Hutool工具可以用最少最简单的代码达到我们的需求,如果项目中未使用hutool就需要修改我们的代码创建并安装一个信任所有证书的 TrustManager便可解决问题。

在Java应用程序中跳过HTTPS证书验证通常是不推荐的,因为这会削弱应用程序的安全性,使其容易受到中间人攻击。因此跳过HTTPS证书验证仅适用于受控的环境中。推荐使用有效证书,或通过正规CA签发自签名证书,并将证书导入Java信任库(cacerts)来解决问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值