当前位置:   article > 正文

HTTPS 发送请求出现TLS握手失败2

HTTPS 发送请求出现TLS握手失败2

之前写过一篇《HTTPS 发送请求出现TLS握手失败》,后续来了;

之前发现https进行握手缺少椭圆曲线,是因为手动删除provider导致的:

  1. static {
  2. Security.removeProvider("SunEC");
  3. }

深入分析之后发现,虽然这里把椭圆曲线删除了,但是其实别的工具类会把

BouncyCastleProvider给加上,代码如下:
  1. static {
  2. Security.removeProvider("BC");
  3. Security.addProvider(new BouncyCastleProvider());
  4. }

这是一个叫CommonUtil的工具类,在工具类被加载的时候会执行static里面的代码;分析后发现,在请求进来之后,切面会拦截请求,切面里面有个参数校验的方法会调用CommonUtil里面的方法,因此这段静态代码是会被执行的,加断点调试,确实是可以执行的;

通义千问对这个BouncyCastle的解释如下:

Bouncy Castle 是一个用 Java 和 C# 编写的加密库,它提供了广泛的算法实现和协议支持。对于 Java 开发者来说,Bouncy Castle 提供了一个名为 `bcprov-jdk15on`(或针对不同 JDK 版本的其他变体)的 Java 密码工具包,可以用来增强标准 Java 安全性框架的功能。

在 Bouncy Castle 中,你可以找到许多标准和非标准的加密算法,包括但不限于:

- 对称加密算法,如 AES, DES, 3DES, Blowfish 等。
- 非对称加密算法,如 RSA, DSA, ECDSA, ElGamal 等。
- 消息摘要算法,如 SHA-1, SHA-256, SHA-512, MD5 等。
- 密钥协商和密钥交换协议,如 DH (Diffie-Hellman) 和 ECDH (Elliptic Curve Diffie-Hellman)。
- 数字签名算法,如 PSS (Probabilistic Signature Scheme) 和 PKCS#1 v1.5。
- 随机数生成器和安全随机数生成器。
- 加密模式,如 CBC (Cipher Block Chaining),CFB (Cipher Feedback),CTR (Counter Mode) 等。
- MACs (Message Authentication Codes),如 CMAC, HMAC 等。

要使用 Bouncy Castle 的 Java 密码包,你需要将其添加到你的项目依赖中。如果你使用 Maven 或 Gradle 构建系统,可以在你的 `pom.xml` 或 `build.gradle` 文件中添加相应的依赖项。例如,在 Maven 中,你可能需要添加以下依赖:

```xml
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>版本号</version>
</dependency>
```

将 `版本号` 替换为最新或所需的 Bouncy Castle 版本。

一旦你添加了依赖项,你就可以在你的代码中使用 Bouncy Castle 提供的类和方法来执行各种加密操作。例如,创建一个 RSA 密钥对、加密数据或验证数字签名等。务必确保你的应用程序正确地初始化了 Bouncy Castle 安全提供程序,以便使用其功能。这通常通过调用 `Security.addProvider(new BouncyCastleProvider());` 来完成。

由此可见,这个包是包含常用的一些加密方法的,同时也包含国密的加密套件,问过写这个代码的同事,他说当时是因为有些项目需要使用国密的签名方法,所以使用这个包的Provider替换了原有的SunEC Provider,避免了名称的冲突;

问题就来了,既然已经加入了可以替换原有的加密套件的新的更强大的套件,那为啥还没握手失败呢?

后面加断点调试,以及分析SSL Handshake报文,发现,在执行外部接口的post请求之前,会有一次handshake的过程, 报文如下:

  1. "ClientHello": {
  2. "client version" : "TLSv1.2",
  3. "random" : "06 A8 9E AD 6D 16 42 E1 69 0B F7 A7 24 00 F0 BE 29 CC 12 1D 2C DA 3A 5C A8 EE FA 13 BB 05 F6 CF",
  4. "session id" : "3A 6B 87 F9 FC 27 80 F8 EE B5 9A AB 5F C7 23 16 B8 D1 56 CB F0 DB 59 4C 47 F0 F0 A7 78 B4 6C FF",
  5. "cipher suites" : "[TLS_AES_256_GCM_SHA384(0x1302), TLS_AES_128_GCM_SHA256(0x1301), TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256(0x009E), TLS_DHE_DSS_WITH_AES_128_GCM_SHA256(0x00A2), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384(0xC024), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(0xC028), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027), TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040), TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(0xC02E), TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(0xC032), TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(0xC02D), TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(0xC031), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(0xC026), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(0xC02A), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(0xC025), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(0xC029), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(0xC005), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(0xC00F), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(0xC004), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(0xC00E), TLS_RSA_WITH_AES_256_GCM_SHA384(0x009D), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_RSA_WITH_AES_256_CBC_SHA256(0x003D), TLS_RSA_WITH_AES_128_CBC_SHA256(0x003C), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F)]",
  6. "compression methods" : "00",
  7. "extensions" : [
  8. "supported_groups (10)": {
  9. "versions": [secp256r1, secp384r1, secp521r1, ffdhe2048, ffdhe3072, ffdhe4096, ffdhe6144, ffdhe8192]
  10. },
  11. "ec_point_formats (11)": {
  12. "formats": [uncompressed]
  13. },
  14. "signature_algorithms (13)": {
  15. "signature schemes": [ed25519, ed448, ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
  16. },
  17. "signature_algorithms_cert (50)": {
  18. "signature schemes": [ed25519, ed448, ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pss_pss_sha256, rsa_pss_pss_sha384, rsa_pss_pss_sha512, rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, dsa_sha256, ecdsa_sha1, rsa_pkcs1_sha1, dsa_sha1]
  19. },
  20. "extended_master_secret (23)": {
  21. <empty>
  22. },
  23. "supported_versions (43)": {
  24. "versions": [TLSv1.3, TLSv1.2]
  25. },
  26. "psk_key_exchange_modes (45)": {
  27. "ke_modes": [psk_dhe_ke]
  28. },
  29. "key_share (51)": {
  30. "client_shares": [
  31. {
  32. "named group": secp256r1
  33. "key_exchange": {
  34. 0000: 04 D6 83 24 55 B1 EC F4 50 9D E6 FD 57 14 EC 68 ...$U...P...W..h
  35. 0010: 5D 7C 40 C8 4F B0 47 9D 3C B9 0F 5C 49 48 52 CF ].@.O.G.<..\IHR.
  36. 0020: 81 ED 8B FF 57 BF CD 6D E9 05 61 0C 95 DD 62 66 ....W..m..a...bf
  37. 0030: 78 9D 8E E5 7A 28 8F B9 FA A3 C3 90 EC B8 7F 65 x...z(.........e
  38. 0040: 14
  39. }
  40. },
  41. ]
  42. },
  43. "renegotiation_info (65,281)": {
  44. "renegotiated connection": [<no renegotiated connection>]
  45. }
  46. ]
  47. }
  48. )
  49. javax.net.ssl|FINE|01 04|Druid-ConnectionPool-Create-491712|2024-07-09 10:30:10.007 CST|ServerHello.java:863|Consuming ServerHello handshake message (
  50. "ServerHello": {
  51. "server version" : "TLSv1.2",
  52. "random" : "BF BB 99 F7 7A 01 93 1D 44 7D 09 E6 9B 2E 61 E9 C1 2C F0 7B 74 D2 A9 E5 1E 3B 35 61 47 BD D1 4A",
  53. "session id" : "36 9D 0D 19 32 B6 2B AC 3B E7 55 12 B9 50 82 36 F8 A8 3B C7 13 73 71 C5 FD 0C 8F 51 A8 1C 2A 2C",
  54. "cipher suite" : "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030)",
  55. "compression methods" : "00",
  56. "extensions" : [
  57. "renegotiation_info (65,281)": {
  58. "renegotiated connection": [<no renegotiated connection>]
  59. },
  60. "ec_point_formats (11)": {
  61. "formats": [uncompressed, ansiX962_compressed_prime, ansiX962_compressed_char2]
  62. },
  63. "extended_master_secret (23)": {
  64. <empty>
  65. }
  66. ]
  67. }
  68. )

为什么会这样呢?同事的解释是这样的:

那次handshake就已经让sslcontext完成了初始化,而为什么随着spring bean生命周期的可以,是因为在接受任何请求前,也就是没有将sslcontext初始化,所以加了provider能够生效,因为之前有bean已经删了sunEC的provider这时已经没有支持快签接口的套件了,由于commonutil初始化前已经进行了handshake并完成了sslcontext的初始化

先看一下sslcontext的源码:

  1. public class SSLContext {
  2. private final Provider provider;
  3. private final SSLContextSpi contextSpi;
  4. private final String protocol;
  5. /**
  6. * Creates an SSLContext object.
  7. *
  8. * @param contextSpi the delegate
  9. * @param provider the provider
  10. * @param protocol the protocol
  11. */
  12. protected SSLContext(SSLContextSpi contextSpi, Provider provider,
  13. String protocol) {
  14. this.contextSpi = contextSpi;
  15. this.provider = provider;
  16. this.protocol = protocol;
  17. }
  18. private static SSLContext defaultContext;

 也就是说sslcontext类里面有个静态的SSLContext:

private static SSLContext defaultContext;

SSLContext sslcontext =ssLcontext.getDefault();

 httpclient密码套件通过这个获取的:

这个里面会初始化provider,也就是说,第一个handshake请求触发的时候,就会触发getInstance方法,从而实例化provider,这样在handshake完成之后再执行下面的语句是没有用的:

  1. static {
  2. Security.removeProvider("BC");
  3. Security.addProvider(new BouncyCastleProvider());
  4. }

 已经实例化了的provider是没有更新的,它不会把新的provider加到已经实例化的静态的instance下的provider成员变量里面;除非调用httpclient那段代码使用新的sslconext;

所以,正确的解决方式是,在被spring容器管理的某个bean中执行:

Security.addProvider(new BouncyCastleProvider());这样在需要使用加密套件的地方就会有provider;

所以,我这里正确的解决方式是,在之前只是删了provider的类的静态代码块里加语句:

原代码:

  1. static {
  2. Security.removeProvider("SunEC");
  3. }

修改后的代码:

  1. static {
  2. Security.removeProvider("SunEC");
  3. Security.addProvider(new BouncyCastleProvider());
  4. }

这样可以解决问题,同时也不会对原有的代码造成影响!到这里,这个问题暂告一段落了,但是实际上这个问题还没有彻底解决;为啥子呢?因为对方服务的运维说他们的服务器最近没有修改过,TLSv1.2以上的版本要求是从2年前就这样的了,而我们这边的代码最近也没有改动到这一块,那这个问题到底是因为什么而触发的,不得而知,是个谜!同事分析说可能是对方服务器最近更新了密码套件之类引起的,因为以前也出现过这样的情况,但是我没遇到过,我只能问一下他们确认一下了, 但是他们说没改动过的,这就无解了,没办法,暂时先这样吧,问题是解决了,就是不知道引发问题的原因。

解释得有点乱:(

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/820621
推荐阅读
  

闽ICP备14008679号