首页 > 解决方案 > Spring 集成中的 TLS 和客户端身份验证

问题描述

我正在我的应用程序中试验 TLS、双向 TLS 和主机验证,并且对集成组件的行为有些困惑。我正在使用TcpNetClientConnection工厂和TcpNetServerConnectionFactory. 我对这些组件如何在后台进行交互没有深入的了解,并希望确保我的身份验证按预期工作(或不工作)。

    @Bean
    public TcpNetClientConnectionFactory tcpClientConnectionFactory() {
        TcpNetClientConnectionFactory factory =
                new TcpNetClientConnectionFactory(upstreamHost, upstreamPort);
        factory.setTcpSocketFactorySupport(tcpSslSocketFactorySupport);
        factory.setTcpSocketSupport(new DefaultTcpSocketSupport(isHostVerificationEnabled));
        return factory;
    }

    @Bean
    TcpNetServerConnectionFactory testTcpServerConnectionFactory() {
        TcpNetServerConnectionFactory factory = new TcpNetServerConnectionFactory(upstreamPort);
        factory.setTcpSocketFactorySupport(tcpSslSocketFactorySupport);
        factory.setTcpSocketSupport(new DefaultTcpSocketSupport(isHostVerificationEnabled));
        return factory;
    }

    @Bean
    public DefaultTcpNetSSLSocketFactorySupport tcpSslSocketFactorySupport() {
        TcpSSLContextSupport sslContextSupport = new DefaultTcpSSLContextSupport(keyStore, trustStore, keyStorePassword,
                trustStorePassword);
        return new DefaultTcpNetSSLSocketFactorySupport(sslContextSupport);
    }

似乎创建 a 的唯一方法TcpSSLContextSupport是将密钥库和信任库都传递给它。为什么需要密钥库?如果我的服务器不需要客户端身份验证,我不应该将密钥库传递给我的客户端连接工厂。客户端工厂应该只需要有一个信任库,来验证它正在与之交谈的服务器。

关于客户端身份验证,我的配置中的服务器连接工厂是否需要客户端身份验证?根据参考资料,为了启用客户端身份验证,我需要重写该postProcessServerSocket()方法。但是,在将客户端的证书添加到服务器的信任库之前,我遇到了 SSL 读取异常。为什么这是必要的,因为我还没有启用客户端身份验证?

最后,如果客户端连接工厂在其密钥库中确实有一个私钥条目,它会在打开与服务器的连接时自动使用该密钥吗?或者仅当服务器要求客户端身份验证时?似乎我的客户端连接工厂正在使用它的私钥,即使我还没有设置任何东西来强制客户端身份验证。

标签: spring-integration

解决方案


服务器默认不需要clientAuth;文档中有一个关于如何要求它的示例

请参阅此测试用例

如果我注释掉第 437 行的代码

        server.setTcpSocketSupport(new DefaultTcpSocketSupport(false) {

            @Override
            public void postProcessServerSocket(ServerSocket serverSocket) {
//              ((SSLServerSocket) serverSocket).setNeedClientAuth(true);
            }

        });

测试失败

java.lang.AssertionError: 
Expecting code to raise a throwable.
    at org.springframework.integration.ip.tcp.connection.SocketSupportTests.testNetClientAndServerSSLDifferentContexts(SocketSupportTests.java:414)
        assertThatExceptionOfType(MessagingException.class)
            .isThrownBy(() -> testNetClientAndServerSSLDifferentContexts(true));

是的,默认实现需要一个密钥库,但是当不需要客户端身份验证时,它可以是空的或不包含客户端密钥的。我们可能应该放松一下;随时打开一个 GitHub 问题来请求它。和/或者你可以简单地提供你自己的实现。

在测试用例(预计会失败)中,我们使用不包含客户端证书的 keyStore。

        TcpSSLContextSupport clientSslContextSupport = new DefaultTcpSSLContextSupport(
                badClient ? "server.ks" : "client.ks",
                "client.truststore.ks", "secret", "secret");

请注意,服务器套接字也有setWantsClientAuth- 也许默认值true可以解释您观察到的内容(如果您在客户端提供证书则失败)。我还没有测试过;尝试将其设置为 false。


推荐阅读