• received fatal alert: handshake_failure; nested exception 异常分析和解决方法


    先了解一下https四次握手其过程如下:

    1、客户端请求建立连接,发送支持的加密方式以及一个随机数client random给服务器;
    2、服务器选择其中服务端也支持的加密方式,并且再加上另外一个随机数server random,和数字证书(其中有公钥),发送给客户端;
    3、客户端确认这个数字证书是有效的,并且再生成一个新的随机数,将这个随机数用服务器发送给它的数字证书中的公钥进行加密发送给服务器;
    4、服务器收到客户端的回复,利用自己的私钥进行解密,获得这个随机数,然后通过将前面这三个随机数以及他们协商的加密方式,计算生成一个对称密钥。

    至此握手阶段完成,之后的会话他们就通过这个对称密钥进行加密传输。
    数据包传输如下
    在这里插入图片描述
    received fatal alert: handshake_failure异常是在这个握手过程中出现异常,直接开始调试查看握手过程中的数据传输情况。
    编译执行以下代码

    
    import javax.net.ssl.*;
    import java.io.*;
    import java.net.URL;
    
    /**
     * @author edui 2022/8/11
     */
    public class Main {
    
        public static void main(String[] args) throws Exception {
            Main main = new Main();
            System.out.println("---------------------------------------------------------------------");
            main.TestRiQingAPI_SaleOrder();
        }
    
            public void TestRiQingAPI_SaleOrder() throws Exception {
                String postData = getJson();
                String url = "https://xxxxxxxx/";
                HttpsURLConnection conn = null;
                OutputStream out = null;
                String rsp = null;
                byte[] byteArray = postData.getBytes("utf-8");
                try {
                    URL uri = new URL(url);
                    conn = (HttpsURLConnection) uri.openConnection();
                    //忽略证书验证--Begin
                    conn.setHostnameVerifier(new TrustAnyHostnameVerifier());
                    //忽略证书验证--End
                    conn.setRequestMethod("POST");
                    conn.setDoInput(true);
                    conn.setDoOutput(true);
                    conn.setRequestProperty("Host", uri.getHost());
                    conn.setRequestProperty("Content-Type", "application/json");
                    out = conn.getOutputStream();
                    out.write(byteArray);
                    out.close();
                    if(conn.getResponseCode()==200) {
                        rsp = getStreamAsString(conn.getInputStream(), "utf-8");
                    }else {
                        rsp = getStreamAsString(conn.getErrorStream(), "utf-8");
                    }
    
                    System.out.println(rsp);
    
                } catch (Exception e) {
                    if(null!=out)
                        out.close();
                    e.printStackTrace();
    
                }
    
            }
    
            /**
             * getJson
             *
             */
            private static String getJson() {
                return "{" + "\"name\"" + ":" + "\"robo_blogs_zh123\"" + "}";
            }
    
            private static String getStreamAsString(InputStream stream, String charset) throws IOException {
                try {
                    Reader reader = new InputStreamReader(stream, charset);
                    StringBuilder response = new StringBuilder();
    
                    final char[] buff = new char[1024];
                    int read = 0;
                    while ((read = reader.read(buff)) > 0) {
                        response.append(buff, 0, read);
                    }
    
                    return response.toString();
                } finally {
                    if (stream != null) {
                        stream.close();
                    }
                }
            }
            
        class TrustAnyHostnameVerifier implements HostnameVerifier {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    增加如下jvm启动参数开启调试模式查看握手情况。

    java -Djavax.net.debug=ssl Main 
    
    • 1

    正常输出如下
    在这里插入图片描述
    报错输出如下
    在这里插入图片描述
    其中
    ClientHello—客户端发送支持的加密算法套件CipherSuite
    ServerHello—服务端选择客户端和服务端都支持的加密算法套件CipherSuite
    可以发现服务端使用的加密算法套件CipherSuite是
    TLS_RSA_WITH_AES_128_CBC_SHA

    ps:
    TLS----使用tls安全协议
    RSA----使用RSA非对称加密来传输AES密钥
    AES_128_CBC----应用数据使用AES128 CBC模式加密
    SHA----摘要算法使用SHA
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是在异常的jvm里客户端发送的加密算法套件CipherSuite列表里并不包含该加密套件
    在这里插入图片描述
    所以异常的这个JDK客户端不支持服务器所使用的加密件套。

    ps:可以看到 ClientHello和ServerHello后面都跟着TLS版本,网上很多人遇到版本不一致问题,JDK8默认使用TLSv1.2JDK7默认使用TLSv1.1
    • 1

    不包含那么如何让你它包含呢
    源码看不懂看到官网介绍https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#Cipher

    jdk8应该是包含该套件的
    在这里插入图片描述
    jdk8有相应的支持,那么加密算法的代码应该是在的吗,那应该是可以在某个地方禁掉
    就在这,安装路劲下
    \jre\lib\security\java.security
    图下的这行配置,看长长的注释可以知道这个配置的作用是当觉得某个算法在某个环境中不适用时就可以通过这个配置禁掉,比如安全过低的算法可以禁掉等。可以看到SHA1就是上述服务端需要的算法,被禁掉了,删掉SHA1问题解决。
    在这里插入图片描述

  • 相关阅读:
    HIVE及SparkSQL优化经验
    文件上传过大被限制问题-springboot
    微信机器人终端1.0未来的设想就是做成telegram一样强大的机器人群体集控终端
    漫谈:C C++ 嵌套包含与前置声明
    C/C++计算邮资 2019年9月电子学会青少年软件编程(C/C++)等级考试一级真题答案解析
    文件上传下载
    JS常见的报错及异常捕获
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java高校学生社团管理系统9p5w4
    【14】基础知识:React - redux
    最新哔哩哔哩邮箱绑定接口签名JS逆向分析
  • 原文地址:https://blog.csdn.net/I_am_hardy/article/details/126300219