商管测试环境突然出现接口时好时坏的情况,报错内容是:
Received fatal alert: handshake_failure
看起来像是 HTTPS 握手出问题了,查看了下代码,正好是 HTTPS 调用一个外部系统时出问题了。
搜索了一下, 大部分同样报错的原因是 TLS 协议版本不一致,导致服务端断开连接。
查询 TLS 版本
在 Java 中,使用的 TLS 协议版本跟 JDK 版本密切关联。
JDK 8 (March 2014 to present) | JDK 7 (July 2011 to present) | JDK 6 (2006 to end of public updates 2013) | |
---|---|---|---|
TLS Protocols | TLSv1.2 (default) TLSv1.1 TLSv1 SSLv3 | TLSv1.2 TLSv1.1 TLSv1 (default) SSLv3 | TLS v1.1 (JDK 6 update 111 and above) TLSv1 (default) SSLv3 |
JSSE Ciphers: | Ciphers in JDK 8 | Ciphers in JDK 7 | Ciphers in JDK 6 |
Reference: | JDK 8 JSSE | JDK 7 JSSE | JDK 6 JSSE |
Java Cryptography Extension, Unlimited Strength (explained later) | JCE for JDK 8 | JCE for JDK 7 | JCE for JDK 6 |
查看服务器的 JDK 版本,果然是 JDK 7。
[root@web-119 ~]# java -version java version "1.7.0_261" OpenJDK Runtime Environment (rhel-2.6.22.1.el6_10-x86_64 u261-b02) OpenJDK 64-Bit Server VM (build 24.261-b02, mixed mode)
使用 TLSv1.2 测试
因为升级 JDK 版本比较耗时,而且系统大部分 HTTP 调用是内网,没有用 HTTPS,所以还是在这个调用手动处理下成本比较低。
这个系统使用的是 hutool 的 HTTP 工具,指定 TLS 版本的示例如下:
HttpResponse httpResponse = HttpRequest.post("https://example.com") .setSSLProtocol("TLSv1.2") .execute();
如果是用 HttpClient,也差不多:
SSLContext ctx = SSLContexts.custom().setProtocol("TLSv1.2").build(); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory( new SSLConnectionSocketFactory(ctx)).build(); HttpPost httpPost = new HttpPost("https://example.com"); CloseableHttpResponse response = httpClient.execute(httpPost); response.close(); httpClient.close();
指定使用 TLSv1.2 后,接口依然是调用时好时坏,难道不是 TLS 版本的问题?
查看 HTTPS 日志
在虚拟机启动命令上加上 -Djavax.net.debug=all
,打开网络调用 debug 日志,可以看到 HTTPS 的连接过程。
本地调用相同接口,查看日志,报错的详细信息如下:
*** ClientHello, TLSv1.2 RandomCookie: GMT: 1624612822 bytes = { 254, 212, 134, 151, 163, 225, 57, 186, 148, 31, 177, 156, 242, 22, 0, 81, 201, 253, 85, 51, 42, 73, 109, 93, 61, 206, 148, 236 } Session ID: {} Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV] Compression Methods: { 0 } Extension elliptic_curves, curve names: {secp256r1, sect163k1, sect163r2, secp192r1, secp224r1, sect233k1, sect233r1, sect283k1, sect283r1, secp384r1, sect409k1, sect409r1, secp521r1, sect571k1, sect571r1, secp160k1, secp160r1, secp160r2, sect163r1, secp192k1, sect193r1, sect193r2, secp224k1, sect239k1, secp256k1} Extension ec_point_formats, formats: [uncompressed] Extension signature_algorithms, signature_algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA224withECDSA, SHA224withRSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA, MD5withRSA Extension server_name, server_name: [host_name: uis-sit.cmsk1979.com] *** [write] MD5 and SHA1 hashes: len = 222 0000: 01 00 00 DA 03 03 61 D6 A0 D6 FE D4 86 97 A3 E1 ......a......... 0010: 39 BA 94 1F B1 9C F2 16 00 51 C9 FD 55 33 2A 49 9........Q..U3*I 0020: 6D 5D 3D CE 94 EC 00 00 38 C0 23 C0 27 00 3C C0 m]=.....8.#.'.<. 0030: 25 C0 29 00 67 00 40 C0 09 C0 13 00 2F C0 04 C0 %.).g.@...../... 0040: 0E 00 33 00 32 C0 08 C0 12 00 0A C0 03 C0 0D 00 ..3.2........... 0050: 16 00 13 C0 07 C0 11 00 05 C0 02 C0 0C 00 04 00 ................ 0060: FF 01 00 00 79 00 0A 00 34 00 32 00 17 00 01 00 ....y...4.2..... 0070: 03 00 13 00 15 00 06 00 07 00 09 00 0A 00 18 00 ................ 0080: 0B 00 0C 00 19 00 0D 00 0E 00 0F 00 10 00 11 00 ................ 0090: 02 00 12 00 04 00 05 00 14 00 08 00 16 00 0B 00 ................ 00A0: 02 01 00 00 0D 00 1A 00 18 06 03 06 01 05 03 05 ................ 00B0: 01 04 03 04 01 03 03 03 01 02 03 02 01 02 02 01 ................ 00C0: 01 00 00 00 19 00 17 00 00 14 75 69 73 2D 73 69 ..........uis-si 00D0: 74 2E 63 6D 73 6B 31 39 37 39 2E 63 6F 6D t.cmsk1979.com main, WRITE: TLSv1.2 Handshake, length = 222 [Raw write]: length = 227 0000: 16 03 03 00 DE 01 00 00 DA 03 03 61 D6 A0 D6 FE ...........a.... 0010: D4 86 97 A3 E1 39 BA 94 1F B1 9C F2 16 00 51 C9 .....9........Q. 0020: FD 55 33 2A 49 6D 5D 3D CE 94 EC 00 00 38 C0 23 .U3*Im]=.....8.# 0030: C0 27 00 3C C0 25 C0 29 00 67 00 40 C0 09 C0 13 .'.<.%.).g.@.... 0040: 00 2F C0 04 C0 0E 00 33 00 32 C0 08 C0 12 00 0A ./.....3.2...... 0050: C0 03 C0 0D 00 16 00 13 C0 07 C0 11 00 05 C0 02 ................ 0060: C0 0C 00 04 00 FF 01 00 00 79 00 0A 00 34 00 32 .........y...4.2 0070: 00 17 00 01 00 03 00 13 00 15 00 06 00 07 00 09 ................ 0080: 00 0A 00 18 00 0B 00 0C 00 19 00 0D 00 0E 00 0F ................ 0090: 00 10 00 11 00 02 00 12 00 04 00 05 00 14 00 08 ................ 00A0: 00 16 00 0B 00 02 01 00 00 0D 00 1A 00 18 06 03 ................ 00B0: 06 01 05 03 05 01 04 03 04 01 03 03 03 01 02 03 ................ 00C0: 02 01 02 02 01 01 00 00 00 19 00 17 00 00 14 75 ...............u 00D0: 69 73 2D 73 69 74 2E 63 6D 73 6B 31 39 37 39 2E is-sit.cmsk1979. 00E0: 63 6F 6D com 00B0: 01 00 .. [Raw read]: length = 5 0000: 15 03 03 00 02 ..... [Raw read]: length = 2 0000: 02 28 .( main, READ: TLSv1.2 Alert, length = 2 main, RECV TLSv1.2 ALERT: fatal, handshake_failure main, called closeSocket() main, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.Alerts.getSSLException(Alerts.java:154) at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1979) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1086) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1332) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1359) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1343) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
日志中客户端和服务端版本都是 TLSv1.2:
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1.2 ALERT: fatal, handshake_failure
如果不手动指定版本,日志中 TLS 版本不一致:
main, READ: TLSv1.2 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, handshake_failure
查询密码套件
TLSv1.2 的连接过程如下图:
从上面的 TLSv1.2 连接示意图看出,ClientHello 和 ServerHello 阶段需要协商交换的信息就只有协议版本、随机数和密码套件。
在协议版本一致的情况下,随机数是生成会话密钥用的,没法出什么问题,那么问题可能就出在密码套件不匹配了。
日志里包含了当前协议版本下客户端支持的密码套件:
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
再查询服务端支持的密码套件,系统如果开放了外网访问的话,可以用 https://myssl.com/ 查询。
如果是内网系统的话,可以使用 SSLyz 查询。
PS D:\sslyze-5.0.2-exe> .\sslyze.exe --tlsv1_2 example.com:443 CHECKING CONNECTIVITY TO SERVER(S) ---------------------------------- example.com:443 => 100.76.53.18 SCAN RESULTS FOR EXAMPLE.COM:443 - 100.76.53.18 -------------------------------------------------------- * TLS 1.2 Cipher Suites: Attempted to connect using 156 cipher suites. The server accepted the following 27 cipher suites: TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 128 TLS_RSA_WITH_ARIA_256_GCM_SHA384 256 TLS_RSA_WITH_ARIA_128_GCM_SHA256 128 TLS_RSA_WITH_AES_256_GCM_SHA384 256 TLS_RSA_WITH_AES_256_CCM_8 128 TLS_RSA_WITH_AES_256_CCM 256 TLS_RSA_WITH_AES_256_CBC_SHA256 256 TLS_RSA_WITH_AES_256_CBC_SHA 256 TLS_RSA_WITH_AES_128_GCM_SHA256 128 TLS_RSA_WITH_AES_128_CBC_SHA256 128 TLS_RSA_WITH_AES_128_CBC_SHA 128 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 256 ECDH: X25519 (253 bits) TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 128 ECDH: X25519 (253 bits) TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 256 ECDH: prime256v1 (256 bits) TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 256 ECDH: prime256v1 (256 bits) TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 256 ECDH: prime256v1 (256 bits) TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 128 ECDH: prime256v1 (256 bits) TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 128 ECDH: prime256v1 (256 bits) TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 128 ECDH: prime256v1 (256 bits) TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 256 DH (2048 bits) TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 128 DH (2048 bits) TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 128 DH (2048 bits) The group of cipher suites supported by the server has the following properties: Forward Secrecy OK - Supported Legacy RC4 Algorithm OK - Not Supported SCANS COMPLETED IN 0.865380 S ----------------------------- COMPLIANCE AGAINST MOZILLA TLS CONFIGURATION -------------------------------------------- Disabled; use --mozilla-config={old, intermediate, modern}.
使用密码套件测试
找到一个客户端和服务端都支持的密码套件,然后测试指定密码套件的情况:
HttpClientBuilder builder = HttpClientBuilder.create(). setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContext.getDefault(), new String[]{"TLSv1.2"}, new String[]{"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"}, new DefaultHostnameVerifier())); CloseableHttpClient httpClient = builder.build(); HttpPost httpPost = new HttpPost("https://example.com"); CloseableHttpResponse response = httpClient.execute(httpPost); response.close(); httpClient.close();
结果还是一样,有时候可以有时候不行。如果换成一个服务端不支持的密码套件,结果是每次都不行。
通过这些测试,可以判断出大致情况了:服务端肯定存在多个反向代理服务器,而这些服务器配置的密码套件不一致,只有在交集内的密码套件,才有可能每次成功,否则会出现一会儿成功一会儿失败的情况。
但是 JDK 7 只支持 AES-128 加密,所以支持的主流密码套件比较少,两端没法协商找到一个能满足每台反向代理要求的密码套件。
修改密码套件配置
于是乎询问运维,果然是昨天上线了新的 Nginx 管理系统,老系统也没下线,两边的 Nginx 各承担一半流量,配置还不一样。
ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers TLS13-AES-128-GCM-SHA256:TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
一个有指定密码套件,一个没有;一个支持 TLSv1 TLSv1.1,一个不支持。把两个配置调整一致后,问题解决。
更新 Nginx 配置后,客户端即使是用 TLSv1,只要有服务端支持的密码套件,也是能够正常握手的。
另外网上有使用替换 JCE 包来解决 handshake_failure 这个错误的方法,本质上应该也是通过扩展 JDK 的 AES-256 支持,增加客户端能支持的密码套件列表,达到和服务端密码套件一致来解决这个问题的。
我也尝试下载了 JCE 包,在 %JDK_HOME%\jre\lib\security 内替换 JDK 7 对应的 jar 包,再次发起调用,就可以支持 AES-256 的密码套件了。
Cipher Suites: [TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_DHE_DSS_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_DHE_DSS_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_SHA, TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS_ECDH_RSA_WITH_RC4_128_SHA, SSL_RSA_WITH_RC4_128_MD5, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
还有些引入第三方密码支持的方法,也是一个道理,就不赘述了。
总结
从 Received fatal alert: handshake_failure 的字面意思能够清晰地看出是握手阶段出了问题,而 TLS 握手的原理让我们能快速定位到 TLS 协议版本和密码套件这两个重要信息,然后再重点排查就能找到问题所在了。
而事实上 HTTPS 的异常设计比我们想象的更精确,Received fatal alert: handshake_failure 几乎就可以等同于密码套件不一致。服务端不支持的协议版本会有一个不一样的的错误信息:Received fatal alert: protocol_version。
在尝试用 TLSv1 连接本站后,由于关闭了 TLSv1 的支持,控制台会输出错误信息:
main, READ: TLSv1 Alert, length = 2
main, RECV TLSv1 ALERT: fatal, protocol_version
main, called closeSocket()
main, handling exception: javax.net.ssl.SSLException: Received fatal alert: protocol_version
TLSv1.2 的密码套件列表如此庞大,让人看得眼花缭乱,原来众多的算法、参数组合导致密码套件非常复杂,难以选择。而且有的加密算法安全性不够高,可能会出现安全问题。
所以 TLSv1.3 里只 5 个套件,无论是客户端还是服务器都不会再有一大堆密码套件列表了。
TLS_AES_128_GCM_SHA256
TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_CCM_SHA256
TLS_AES_128_CCM_8_SHA256
看上去 TLSv1.3 的密码套件更短了,是因为密钥交换算法明确废除 RSA 和 DH ,全部改用 ECDHE,就不需要指定密钥交换算法了。
发表回复