SSLHandshakeException:no 个共同的密码套件

SSLHandshakeException:no cipher suites in common

我看到过其他关于此错误的问题,但大多数都没有使用 Spring 集成。我看到确实使用 Spring 集成的那个是由于损坏的密钥库。我已经多次重新创建我的密钥库和信任库,所以这似乎不是问题所在。

我正在尝试在 TcpNetClientConnectionFactoryTcpNetServerConnectionFactory 之间启用 TLS。这是我的 Spring 配置:

@Bean
public TcpNetClientConnectionFactory clientConnectionFactory() {
    TcpNetClientConnectionFactory factory = new TcpNetClientConnectionFactory("localhost", 6000);
    factory.setTcpSocketFactorySupport(sslSocketFactorySupport(
            "security/empty.jks",
            "security/client-truststore.jks",
            "changeit",
            "changeit"));
    factory.setTcpSocketSupport(new DefaultTcpSocketSupport());
    factory.setSerializer(TcpCodecs.lengthHeader2());
    factory.setDeserializer(TcpCodecs.lengthHeader2());
    return factory;
}

@Bean
TcpNetServerConnectionFactory serverConnectionFactory() {
    TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(6000);
    factory.setTcpSocketFactorySupport(sslSocketFactorySupport(
            "security/server.jks",
            "security/empty.jks",
            "changeit",
            "changeit"));
    factory.setTcpSocketSupport(new DefaultTcpSocketSupport());
    factory.setSerializer(TcpCodecs.lengthHeader2());
    factory.setDeserializer(TcpCodecs.lengthHeader2());
    return factory;
}

@Bean
public DefaultTcpNetSSLSocketFactorySupport sslSocketFactorySupport(String keyStore, String trustStore, String keyStorePassword, String trustStorePassword) {
    TcpSSLContextSupport contextSupport = new DefaultTcpSSLContextSupport(keyStore, trustStore, keyStorePassword, trustStorePassword);
    return new DefaultTcpNetSSLSocketFactorySupport(contextSupport);
}

@Bean
public IntegrationFlow requestFlow() {
    return IntegrationFlows
            .from(Tcp.inboundAdapter(serverConnectionFactory()))
            .<byte[]>handle((p, h) -> MessageBuilder
                    .withPayload(p)
                    .build())
            .handle(Tcp.outboundAdapter(serverConnectionFactory()))
            .get();
}

@Bean
public IntegrationFlow responseFlow() {
    return IntegrationFlows
            .from(Tcp.inboundAdapter(clientConnectionFactory()))
            .channel(MessageChannels
                    .queue("responseChannel")
                    .get())
            .get();
}

empty.jks 文件是一个空密钥库。以下是我用来生成服务器密钥库和客户端信任库的命令:

#!/bin/bash

keytool -genkey \
        -keypass changeit \
        -storepass changeit \
        -keystore src/main/resources/security/server.jks

keytool -export \
        -storepass changeit \
        -file src/main/resources/security/server.cer \
        -keystore src/main/resources/security/server.jks

keytool -import \
        -v \
        -trustcacerts \
        -file src/main/resources/security/server.cer \
        -keypass changeit \
        -storepass changeit \
        -keystore src/main/resources/security/client-truststore.jks

最后,这是一个在我的两个连接工厂之间来回发送 Message 的简单测试。没有 TLS,它工作正常。使用 TLS,我得到了关于密码套件的 SSL 读取异常。

@Autowired
private TcpNetClientConnectionFactory client;

@Autowired
private QueueChannel responseChannel;

@Test
public void requestAndResponse() throws Exception {
    Message<byte[]> request = MessageBuilder
            .withPayload("foo".getBytes())
            .build();

    client.getConnection().send(request);

    assertNotNull(responseChannel.receive(1000));
}

我的 SSL 设置有什么问题?

更新

我正在使用 Java 8. 这是客户端密码:

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_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV

服务器密码:

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_CB    C_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_12    8_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_E    CDH_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_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AE    S_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_    SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV

我可以立即看出第一个匹配。

更新 2

使用javax.net.debug=all,这是ClientHello:

*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1567538676 bytes = { 28, 158, 236, 83, 48, 241, 225, 168, 30, 197, 146, 201, 129, 246, 199, 156, 9, 78, 61, 157, 11, 64, 25, 26, 41, 181, 233, 45 }
Session ID:  {}
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_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }

我看到了 ClientHello 的原始写入(204 字节),然后是 5 字节的原始读取,然后是 199 字节。有趣的是,原始读取格式化后,它似乎是另一个 ClientHello,而不是 ServerHello:

pool-1-thread-2, READ: TLSv1.2 Handshake, length = 199
 C0 0A C0 14  .(.=.&*** ClientHello, TLSv1.2
RandomCookie:  GMT: 1567539109 bytes = { 119, 78, 120, 53, 121, 162, 190, 126, 136, 74, 228, 139, 71, 227, 132, 120, 177, 116, 234, 187, 14, 134, 253, 158, 109, 174, 41, 17 }
Session ID:  {}
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_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, TLS_DHE_DSS_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, TLS_DHE_DSS_WITH_AES_128_GCM_SHA256, TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
Compression Methods:  { 0 }

紧随其后的是:

pool-1-thread-2, SEND TLSv1.2 ALERT:  fatal, description = handshake_failure
pool-1-thread-2, WRITE: TLSv1.2 Alert, length = 2
[Raw write]: length = 7
0000: 15 03 03 00 02 02 28                               ......(
pool-1-thread-2, called closeSocket()
pool-1-thread-2, handling exception: javax.net.ssl.SSLHandshakeException: no cipher suites in common
pool-1-thread-2, called close()
pool-1-thread-2, called closeInternal(true)
2019-09-04 08:48:21.637 ERROR 1755 --- [pool-1-thread-2] o.s.i.i.tcp.connection.TcpNetConnection  : Read exception localhost:63727:6000:51c3e8e9-194c-4f56-b238-960fa3b7c05c SSLHandshakeException:no cipher suites in common

知道为什么第二个 Hello 是另一个 ClientHello,而不是 ServerHello 吗?

这原来是应用程序上下文问题。我在 src/test/java 下定义了服务器 SSL 配置。我的 Autowired 字段之一是从 src/main/java 拉取客户端 SSL 配置 bean,而不是我认为的服务器 bean。因此,双重 ClientHellos.

添加了几个@Qualifiers 以确保我的服务器配置在我认为的地方被自动装配