JAVA client https请求报错:Received fatal alert: protocol_version和handshake_failure问题处理过程记录

问题背景

1、老的项目,正式环境为Linux环境,使用IBM-jdk,因使用的用户不多,因此客户方没有使用WAS(没有购买WAS)

2、后续扩展开发接口需要调用第三方系统的https接口,2020年3月上线时,该接口可以正常调用第三方的https接口服务;2020年6月第三方系统的证书过期了,后来更换了新的厂商的安全证书,导致原来的接口无法调用第三方的https接口服务。报错为:javax.net.ssl.SSLException: Received fatal alert: protocol_version。后来试过在正式环境新加了一个1.0协议的链路,使用该链路可以正常调通第三方的https接口,但是该方法只能临时解决。

老项目的jdk版本比较旧:

java version "1.7.0"
Java(TM) SE Runtime Environment (build pxa6470sr4fp1ifix-20130423_02(SR4 FP1+IV38579+IV38399+IV40208))
IBM J9 VM (build 2.6, JRE 1.7.0 Linux amd64-64 Compressed References 20130421_145945 (JIT enabled, AOT enabled)
J9VM - R26_Java726_SR4_FP1_2_20130421_2353_B145945
JIT  - r11.b03_20130131_32403ifx4
GC   - R26_Java726_SR4_FP1_2_20130421_2353_B145945_CMPRSS
J9CL - 20130422_145945)
JCL - 20130225_01 based on Oracle 7u13-b08

 

在网上找过各种资料,试过调整httpclient客户端发起请求的代码,将第三方系统协议版本1.2直接写在代码里面,报错javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure,这个错基本说明改客户端代码的协议版本到1.2的思路是对的,但是仍旧失败,加了各种常见的jvm参数也仍旧失败。加的参数有

-Djdk.tls.client.protocols=TLSv1.2 -Dhttps.protocols=TLSv1.2 -Ddeployment.security.SSLv2Hello=false -Ddeployment.security.SSLv3=false -Ddeployment.security.TLSv1=false -Ddeployment.security.TLSv1.1=false -Ddeployment.security.TLSv1.2=true

看过IBM官网:https://www.ibm.com/support/pages/node/254939

里面有一段这么描述的:

也就是说,ibm1.7的jdk,作为客户端发送请求,仅支持1.0的协议,对于更高版本的1.2的协议是不支持。

而且我这边出现了官网类似的一段报错:

且官网这里并没有给出解决办法,所以该问题在IBM1.7版的jdk下无法解决(特殊说明,有项目也是使用的该版本jdk,后面解决了是因为他们用了IBM配套的was中间件,有专门的解决方案可以解决该问题)

解决方案

1、更换为oracle的1.7的jdk,版本为jdk1.7.0_51,使用上面的代码,仍旧报错,也是上面握手失败的问题。后来试了jdk1.7.0_80版本的jdk也不行。

2、我将httpclient端的代码单独拿出来,写了一个main方法单独测试,并加上jvm调试参数-Djavax.net.debug=all,单独运行后有一段重要的解决如下:

上面的日志说明写/读都是1.2的协议,但是RECV用的TLSv1,说明光改代码没有用。

3、后来查资料,也找同事问了,可能这个问题需要升级jdk的小版本(虽然直接升级到1.8能解决,但是产品底层用的1.7,但是直接升级到1.8系统没法运行,所以直接在小版本内升级),官网说明如下:https://community.oracle.com/tech/developers/discussion/3965168/how-to-enable-tlsv1-2-in-java-1-7

https://www.java.com/en/configure_crypto.html#enableTLSv1_2   

(感谢帮忙在网上各种找资料的小哥哥)

4、后来升级了jdk版本到1.7的最后一个版本jdk-7u191,这个问题终于解决。

main, WRITE: TLSv1.2 Application Data, length = 235
[Raw write]: length = 240
0000: 17 03 03 00 EB 00 00 00   00 00 00 00 01 35 80 C6  .............5..
0010: F1 4E 6D 4F 0B 91 5B 30   4F DC B8 3B 0A 9F 32 56  .NmO..[0O..;..2V
......(此处省略一部分日志)
[Raw read]: length = 5
0000: 17 03 03 01 88                                     .....
[Raw read]: length = 392
0000: 99 1A E3 1F 93 6F 79 CC   43 13 04 0A 3F B5 6A 88  .....oy.C...?.j.
0010: F5 FA 02 EF 15 C4 5A A5   57 2B 67 A6 80 C6 B7 64  ......Z.W+g....d
......(此处省略一部分日志)
main, READ: TLSv1.2 Application Data, length = 392
Padded plaintext after DECRYPTION:  len = 368
0000: 48 54 54 50 2F 31 2E 31   20 34 30 31 20 55 6E 61  HTTP/1.1 401 Una
0010: 75 74 68 6F 72 69 7A 65   64 0D 0A 44 61 74 65 3A  uthorized..Date:
......(此处省略一部分日志)
HTTP/1.1 401 Unauthorized
{"errorCode":2,"msg":"Authorization Failed"}--(这里已经正常报错返回了,不再是握手失败)
main, called close()
main, called closeInternal(true)
main, SEND TLSv1.2 ALERT:  warning, description = close_notify
Padded plaintext before ENCRYPTION:  len = 2
0000: 01 00                                              ..
main, WRITE: TLSv1.2 Alert, length = 26
[Raw write]: length = 31
0000: 15 03 03 00 1A 00 00 00   00 00 00 00 02 E9 D1 1D  ................
0010: 21 E2 47 01 A0 43 90 0E   6F 4E D2 C4 DE 14 BB     !.G..C..oN.....
main, called closeSocket(selfInitiated)

测试代码(仅供参考)

	public static void main(String args[]) throws FileNotFoundException {
        KeyStore keyStore = null;
		try {
			keyStore = KeyStore.getInstance(KeyStore.getDefaultType());//RSA PKCS12
		} catch (KeyStoreException e) {
			System.err.println("获取证书实例异常");
			e.printStackTrace();
			return;
		}
        try {
        	//加载证书,需要附上证书的密码
			keyStore.load(new FileInputStream(new File("/XXXXXX.jks")), "123456".toCharArray());
		} catch (NoSuchAlgorithmException | CertificateException
				| IOException e) {
			System.err.println("加载证书异常");
			e.printStackTrace();
			return;
		}
        SSLContext sslcontext = null;
		try {
			sslcontext = SSLContexts.custom()
			        //忽略掉对服务器端证书的校验
			        .loadTrustMaterial(new TrustStrategy() {
			            @Override
			            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
			                return true;
			            }
			        })
			        .loadKeyMaterial(keyStore, "123456".toCharArray())
			        .build();
		} catch (KeyManagementException | UnrecoverableKeyException
				| NoSuchAlgorithmException | KeyStoreException e) {
			System.err.println("创建连接对象异常");
			e.printStackTrace();
			return;
		}
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"TLSv1.2"},
                null,
                SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslConnectionSocketFactory)
                .build();

        try {

        	HttpPost httppost = new HttpPost("https://www.12306.com");//这里是请求的URL路径
        	 /* 设置参数param到请求对象中去,参数为json对象 如果是json参数,可以使用下面的方法 */
            //httpPost.setEntity(new StringEntity(param,"utf-8"));
            System.out.println("Executing request " + httppost.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httppost);
            try {
                HttpEntity entity = response.getEntity();
                System.out.println(response.getStatusLine());
                System.out.println(IOUtils.toString(entity.getContent()));
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } catch (IOException e) {
        	System.err.println("客户端返回异常");
			e.printStackTrace();
		} finally {
            try {
				httpclient.close();
			} catch (IOException e) {
				System.err.println("客户端关闭异常");
				e.printStackTrace();
				return;
			}
        }
    }

总结

1、遇到问题,可以先在网上找下,可能网上会提供各种资料和解决办法,可以尝试着解决,但这些不一定完全适合自己。

2、有时候可以关注国外网站的论坛,特别是官方网站,或许就能找到解决问题的思路。

 

  

 

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值