【问题标题】:SSLContext with hostname verification带有主机名验证的 SSLContext
【发布时间】:2016-04-11 12:30:13
【问题描述】:

在测试我的客户端-服务器分布式系统时,起初我很惊讶地发现 TLS 的默认 JSSE 实现不进行主机名验证。我在this 问题中尝试了接受的答案,但我的用例有点不同。我使用 RabbitMQ 的连接工厂,它抽象了 SSLSocket 的构造。我只是为连接工厂提供一个 SSLContext。我确实找到了很多关于 HTTPS 甚至一些其他协议的信息,但不是可以始终使用的通用内容,即使使用自定义协议也是如此。

除了使用X509ExtendedTrustManager 之外,关于创建域验证 SSLContext 并没有太多的发现。在调试时,我可以看到

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init((KeyStore) null);
tmf.getTrustManagers();

返回一个TrustManager,一个X509TrustManagerImpl,根据this页面扩展X509ExtendedTrustManager。但是,此证书不会拒绝错误的证书(我所说的“错误”是指证书与服务器的主机名不匹配)。

然后我求助于编写自己的X509ExtendedTrustManager,它从我的TrustManagerFactory 委托给信托经理:

final TrustManager[] trustManagers = tmf.getTrustManagers();
X509ExtendedTrustManager x509ExtendedTrustManager = new X509ExtendedTrustManager() {
    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
        checkClientTrusted(x509Certificates, s);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
        checkServerTrusted(x509Certificates, s);
    }

    @Override
    public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
        checkClientTrusted(x509Certificates, s);
    }

    @Override
    public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
        checkServerTrusted(x509Certificates, s);
    }

    public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        for (TrustManager trustManager : trustManagers)
            ((X509TrustManager)trustManager).checkClientTrusted(x509Certificates, s);
    }

    public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
        for (TrustManager trustManager : trustManagers)
            ((X509TrustManager)trustManager).checkServerTrusted(x509Certificates, s);

        for (X509Certificate x509Certificate : x509Certificates) {
            Collection<List<?>> alternativeNameCollection = x509Certificate.getSubjectAlternativeNames();
            if (alternativeNameCollection != null) {
                for (List alternativeNames : alternativeNameCollection) {
                    if (alternativeNames.get(1).equals(host))
                        return;
                }
            }
        }

        throw new CertificateException("Certificate hostname and requested hostname don't match");
    }

    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
};

这终于奏效了。但我不敢相信没有更清洁的方法可以做到这一点。基本上我正在寻找的是标准的东西,因为我认为我正在做的事情也很标准。我读到了HostnameVerifier,但是有没有办法在不使用 HTTPS 的情况下将它与 SSLContext 一起使用?在某处是否有X509ExtendedTrustManager 的主机名验证实现?我确信我正在重新发明已经写好的东西。

编辑: this 是一个很好的工作示例。不过仍然是自定义代码。

编辑 2: RabbitMQ 仍然存在问题,因为 RabbitMQ 解析 DNS 并将 IP 地址传递给验证者,这当然总是失败。所以X509ExtendedTrustManager 似乎还是要走的路。

【问题讨论】:

  • 我很想建议这可能是 this question 的副本(即,您确实从 Java 7 开始在 JSSE 中开箱即用)。我不确定您的 RabbitMQ 库是如何实现主机名验证的,但如果没有,那就是那里的错误。
  • 我同意这些问题非常相似,它们都是关于主机名验证的。 IMO 的不同之处在于该问题及其答案都是关于 HTTPS 的。我正在寻找一个通用的解决方案,基本上是为任何协议添加主机名验证功能,我只是发现自己编写这样的代码是危险且不可维护的(目前,这就是我解决它的方法)。我宁愿依赖一个由对该主题有更多了解的团队实施的库。
  • @delucasvb 请参阅tersesystems.com/2014/03/23/fixing-hostname-verification 了解更多信息
  • 我使用 Netty 进行网络通信。它默认进行主机名验证,包括原始 TLS 套接字。

标签: java security ssl


【解决方案1】:

主机名验证不是 SSL 的一部分。它是 HTTPS 的一部分。您正在调查错误的层和错误的 API。您应该查看HttpURLConnectionHostnameVerifier

【讨论】:

  • 我正在使用除 HTTPS 之外的其他协议,其中一些是自定义的。
  • 这不会以任何方式改变我的答案。
  • 我编辑了我的问题。我尝试使用主机名验证程序,但这并不总是有效,因为它并不总是收到主机名。一些库或框架(如 RabbitMQ)会向它发送一个 IP 地址。
  • 这也不会改变我的答案。
  • @EJP docs.oracle.com/javase/7/docs/api/javax/net/ssl/… 你是对的,主机名验证是在 HTTPS 而不是在 SSL/TLS 层完成的,但是上面的类确实存在用于通过一个 API 调用进行证书链检查和主机名验证。文档说“这个类允许在一次调用这个类时完成检查。”
猜你喜欢
  • 1970-01-01
  • 2012-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-25
  • 2020-11-15
  • 2013-01-15
  • 1970-01-01
相关资源
最近更新 更多