在 Netty 客户端中配置的密码列表未发送到服务器

Cipher list configured in Netty client not being sent down to Server

我有下面的代码来配置密码列表并发送到 Netty 客户端中的服务器。

Pair<String, String> config = Pair.of(keyStore, keyStorePassword);
if (!filters.containsKey(config)) {
    HttpClient securedClient = this.httpClient.secure(sslContextSpec -> {
        SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
        sslContextBuilder.keyManager(loadKeyStore(keyStore, keyStorePassword));
        sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
        if(!StringUtils.isBlank(ciphers)) {
            sslContextBuilder.ciphers(getCiphersList(ciphers));
        }
        sslContextSpec.sslContext(sslContextBuilder);
    });
    filters.put(config, new NettyRoutingFilter(securedClient, headersFilters, properties));
}
return filters.get(config).filter(exchange, chain);

这段代码运行良好。最近springboot和cloud版本升级后出现SSL握手失败

根据服务器端 SSL 调试日志,此失败是由于 no ciphers suites in common 错误。

我进一步检查并发现客户端正在尝试与 TLSv1.2 启动握手并且在我看来 Netty 被过滤掉配置的密码(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)。

我可以在客户端的 SSL 日志中看到以下两个日志部分,只有第二部分到达服务器。 (没有 TLS_DHE_DSS_WITH_AES_256_GCM_SHA384

javax.net.ssl|DEBUG|34|es-writer-6|2021-03-25 10:48:25.081 SGT|ClientHello.java:651|Produced ClientHello handshake message (
"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "47 5B 0F EB C7 9C 1B A1 21 C0 60 15 AC FD 5B 88 67 07 F9 2B 04 4A 74 ED 9B 37 CD 84 81 65 87 F8",
  "session id"          : "33 FD 38 2C 7D 71 62 79 2E F1 99 7D FB 5F 69 F6 04 23 9A 31 04 9B 5D 0A 16 B2 76 10 A3 C5 74 E6",
  "cipher suites"       : "[TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302), 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_RSA_WITH_AES_256_GCM_SHA384(0x009D), TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384(0xC02E), TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384(0xC032), TLS_DHE_RSA_WITH_AES_256_GCM_SHA384(0x009F), TLS_DHE_DSS_WITH_AES_256_GCM_SHA384(0x00A3), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256(0xC02D), TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256(0xC031), 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_RSA_WITH_AES_256_CBC_SHA256(0x003D), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384(0xC026), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384(0xC02A), TLS_DHE_RSA_WITH_AES_256_CBC_SHA256(0x006B), TLS_DHE_DSS_WITH_AES_256_CBC_SHA256(0x006A), TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA(0xC00A), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA(0xC005), TLS_ECDH_RSA_WITH_AES_256_CBC_SHA(0xC00F), TLS_DHE_RSA_WITH_AES_256_CBC_SHA(0x0039), TLS_DHE_DSS_WITH_AES_256_CBC_SHA(0x0038), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256(0xC023), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256(0xC027), TLS_RSA_WITH_AES_128_CBC_SHA256(0x003C), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256(0xC025), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256(0xC029), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256(0x0067), TLS_DHE_DSS_WITH_AES_128_CBC_SHA256(0x0040), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA(0xC009), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F), TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA(0xC004), TLS_ECDH_RSA_WITH_AES_128_CBC_SHA(0xC00E), TLS_DHE_RSA_WITH_AES_128_CBC_SHA(0x0033), TLS_DHE_DSS_WITH_AES_128_CBC_SHA(0x0032), TLS_EMPTY_RENEGOTIATION_INFO_SCSV(0x00FF)]",
  
javax.net.ssl|DEBUG|2D|reactor-http-nio-1|2021-03-25 10:48:25.275 SGT|ClientHello.java:651|Produced ClientHello handshake message (
"ClientHello": {
  "client version"      : "TLSv1.2",
  "random"              : "48 3A 89 68 7F CE 49 83 FE EF 12 81 2D E3 EE 54 47 90 A8 33 EE 95 F6 16 76 B9 81 41 E9 DA B6 2E",
  "session id"          : "69 CC E2 A3 BA 6A AF 47 8B 3E 83 53 4E 5F 62 90 B3 91 F3 E3 9D 5C 1A 2D C3 F7 F7 6D 2E EA 38 7C",
  "cipher suites"       : "[TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384(0xC02C), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256(0xC02B), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256(0xC02F), TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384(0xC030), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA(0xC013), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA(0xC014), TLS_RSA_WITH_AES_128_GCM_SHA256(0x009C), TLS_RSA_WITH_AES_128_CBC_SHA(0x002F), TLS_RSA_WITH_AES_256_CBC_SHA(0x0035), TLS_AES_128_GCM_SHA256(0x1301), TLS_AES_256_GCM_SHA384(0x1302)]",

如何发送配置的密码,即使它们已经过时(弱)?这里的 SslContextBuilder 配置有什么问题吗?

更新:

进一步的分析有助于找出错过配置密码的实际原因。

作为 Spring 引导版本升级的一部分,reactor Netty 版本已从 0.9.12 升级到 1.0.3。

我可以看到下面 reactor.netty.tcp.SslProvider

的逻辑差异

0.9.12

void updateDefaultConfiguration() {
switch(this.type) {
case H2:
    this.sslContextBuilder.sslProvider(io.netty.handler.ssl.SslProvider.isAlpnSupported(io.netty.handler.ssl.SslProvider.OPENSSL) ? io.netty.handler.ssl.SslProvider.OPENSSL : io.netty.handler.ssl.SslProvider.JDK).ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE).applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN, SelectorFailureBehavior.NO_ADVERTISE, SelectedListenerFailureBehavior.ACCEPT, new String[]{"h2", "http/1.1"}));
    break;
case TCP:
    this.sslContextBuilder.sslProvider(OpenSsl.isAvailable() ? io.netty.handler.ssl.SslProvider.OPENSSL : io.netty.handler.ssl.SslProvider.JDK);
case NONE:
}

}

1.0.3

void updateDefaultConfiguration() {
    switch(this.type) {
    case H2:
        this.sslContextBuilder.sslProvider(io.netty.handler.ssl.SslProvider.isAlpnSupported(io.netty.handler.ssl.SslProvider.OPENSSL) ? io.netty.handler.ssl.SslProvider.OPENSSL : io.netty.handler.ssl.SslProvider.JDK).ciphers(HTTP2_CIPHERS, SupportedCipherSuiteFilter.INSTANCE).applicationProtocolConfig(new ApplicationProtocolConfig(Protocol.ALPN, SelectorFailureBehavior.NO_ADVERTISE, SelectedListenerFailureBehavior.ACCEPT, new String[]{"h2", "http/1.1"}));
        break;
    case TCP:
        this.sslContextBuilder.sslProvider(OpenSsl.isAvailable() ? io.netty.handler.ssl.SslProvider.OPENSSL : io.netty.handler.ssl.SslProvider.JDK).ciphers((Iterable)null, IdentityCipherSuiteFilter.INSTANCE).applicationProtocolConfig((ApplicationProtocolConfig)null);
    case NONE:
    }

}

在 1.0.3 中,它强制将密码覆盖为空,并且不再能够手动设置密码。

我该如何解决这个问题?

最后,我发现在分配给新反应堆 Netty 版本的上下文规范之前需要构建 SSL 上下文。

工作代码:

HttpClient securedClient = this.httpClient.secure(sslContextSpec -> {
 SslContextBuilder sslContextBuilder = SslContextBuilder.forClient();
 sslContextBuilder.keyManager(loadKeyStore(keyStore, keyStorePassword));
 sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);
 if (!StringUtils.isBlank(ciphers)) {
    sslContextBuilder.ciphers(getCiphersList(ciphers));
 }
 try {
    sslContextSpec.sslContext(sslContextBuilder.build());
 } catch (SSLException e) {
   log.error("Error in building the SSL context ",e);
 }
});