使用 Spring AMQP 客户端执行 TLS 主机名验证
Performing TLS hostname validation with the Spring AMQP client
用例:作为 Spring AMQP 客户端的用户通过 TLS 连接到 RabbitMQ 代理,我想验证主机名是否在X.509 证书公用名字段或证书 X.509 扩展中的主题备用名称之一与我用于连接到代理的主机名匹配。
一个可能的解决方案:Spring Rabbit 连接工厂 bean org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean 有一个 setSocketConfigurator(com.rabbitmq.client.SocketConfigurator) 方法在这个简单的 SocketConfigurator
中使用 javax.net.ssl.HandshakeCompletedListener 配置 SSLSocket,如下所示
static class MySocketConfigurator implements SocketConfigurator {
private final String[] validHostnames;
public MySocketConfigurator(String[] validHostnames) {
this.validHostnames = validHostnames;
}
@Override
public void configure(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(final HandshakeCompletedEvent event) {
try {
if (event.getPeerCertificates()[0] instanceof X509Certificate) {
X509Certificate x509Certificate = (X509Certificate) event.getPeerCertificates()[0];
boolean verified = verifyHost(validHostnames, x509Certificate);
if (!verified) {
event.getSocket().close();
}
} else {
event.getSocket().close();
}
} catch (SSLPeerUnverifiedException e) {
throw new RuntimeException(e);
} catch (CertificateParsingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
// verify that one of the validHostname items matches a host found in the broker certificate
boolean verifyHost(String[] validHostnames, X509Certificate serverCertificate) throws CertificateParsingException {
...
}
}
关闭 event.getSocket().close() 中引用的套接字似乎有点笨手笨脚,但如果应用程序认为证书中的主机名不够接近,则足以关闭连接比赛。我最初设想在确定主机名不匹配时抛出 RuntimeException,但看起来 Spring 堆栈吞噬了这些并且不会导致所需的应用程序引起的连接设置失败。
上面显示的 SocketConfigurator 方法,它直接调用 socket.close(),是在证书主机名被认为不匹配时使 TLS 连接设置失败的推荐方法吗?
我不知道你说的 "spring stack swallows the exception" 是什么意思;听起来不对;如果你能给我指出执行该操作的代码,我可以看一下。
spring 连接工厂只是委托给兔子连接工厂。
我不知道你关于最佳实践的基本问题的答案;你可能想要 ping rabbitmq-users google 组中的兔子家伙。
用例:作为 Spring AMQP 客户端的用户通过 TLS 连接到 RabbitMQ 代理,我想验证主机名是否在X.509 证书公用名字段或证书 X.509 扩展中的主题备用名称之一与我用于连接到代理的主机名匹配。
一个可能的解决方案:Spring Rabbit 连接工厂 bean org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean 有一个 setSocketConfigurator(com.rabbitmq.client.SocketConfigurator) 方法在这个简单的 SocketConfigurator
中使用 javax.net.ssl.HandshakeCompletedListener 配置 SSLSocket,如下所示
static class MySocketConfigurator implements SocketConfigurator {
private final String[] validHostnames;
public MySocketConfigurator(String[] validHostnames) {
this.validHostnames = validHostnames;
}
@Override
public void configure(final Socket socket) throws IOException {
if (socket instanceof SSLSocket) {
SSLSocket sslSocket = (SSLSocket) socket;
sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
@Override
public void handshakeCompleted(final HandshakeCompletedEvent event) {
try {
if (event.getPeerCertificates()[0] instanceof X509Certificate) {
X509Certificate x509Certificate = (X509Certificate) event.getPeerCertificates()[0];
boolean verified = verifyHost(validHostnames, x509Certificate);
if (!verified) {
event.getSocket().close();
}
} else {
event.getSocket().close();
}
} catch (SSLPeerUnverifiedException e) {
throw new RuntimeException(e);
} catch (CertificateParsingException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}
}
// verify that one of the validHostname items matches a host found in the broker certificate
boolean verifyHost(String[] validHostnames, X509Certificate serverCertificate) throws CertificateParsingException {
...
}
}
关闭 event.getSocket().close() 中引用的套接字似乎有点笨手笨脚,但如果应用程序认为证书中的主机名不够接近,则足以关闭连接比赛。我最初设想在确定主机名不匹配时抛出 RuntimeException,但看起来 Spring 堆栈吞噬了这些并且不会导致所需的应用程序引起的连接设置失败。
上面显示的 SocketConfigurator 方法,它直接调用 socket.close(),是在证书主机名被认为不匹配时使 TLS 连接设置失败的推荐方法吗?
我不知道你说的 "spring stack swallows the exception" 是什么意思;听起来不对;如果你能给我指出执行该操作的代码,我可以看一下。
spring 连接工厂只是委托给兔子连接工厂。
我不知道你关于最佳实践的基本问题的答案;你可能想要 ping rabbitmq-users google 组中的兔子家伙。