使用 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 组中的兔子家伙。