【问题标题】:Implementing X509TrustManager实现 X509TrustManager
【发布时间】:2019-02-28 10:26:06
【问题描述】:

我目前正在尝试通过 Java 中的 SSL/TLS 通过 Internet 传输数据,并且我希望双方都进行身份验证。我自己实现了 KeyManager 来加载密钥对并向对方提供适当的证书。

现在,我正在尝试检查证书,我正在通过实施我自己的 TrustManager 来做到这一点(双方都持有对方的证书,一切都是自签名的)。但是,getAcceptedIssuers 并没有像我希望的那样工作,因为即使我没有返回任何内容,连接仍然可以毫无问题地建立。

为什么证书没有被拒绝?

protected static class SelectingTrustManager implements X509TrustManager{
    final X509TrustManager delegate;

    private String[] trustedAliases;
    private final KeyStore keystore;

    public SelectingTrustManager(X509TrustManager delegate, KeyStore keystore, String[] trustedAliases) {
        this.trustedAliases = trustedAliases;
        this.keystore = keystore;
        this.delegate = delegate;
    }

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{
        delegate.checkClientTrusted(chain, authType);
    }

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{
        delegate.checkServerTrusted(chain, authType);
    }

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

}

【问题讨论】:

  • 实际的握手会发生在其他地方吗?

标签: java ssl x509trustmanager


【解决方案1】:

你不清楚你的代码是客户端还是服务器,所以我两个都回答,尽管它只对服务器很重要。

尽管 javadoc 不是特定的,X509TM.getAcceptedIssuers 不用于决定是否信任接收到的证书或链;这仅由客户端中的checkServerTrusted 或服务器中的checkClientTrusted 完成。

getAcceptedIssuers 的值仅用于并影响两件事:

  • 在服务器中,(仅)如果启用了客户端身份验证(通过调用needClientAuth(true)wantClientAuth(true)),则其元素中的主题名称用于在server's CertificateRequest message 中创建CA 列表。这并不强制与将用作客户端证书链的信任锚的 CA 列表相同(如果收到的话);事实上,信任管理器实际上不需要使用信任锚列表甚至standard validation algorithm 的其余部分——尽管如果您的“代表”是使用标准X509[Extended]TrustManager 的标准CertPathValidator 确实如此。但是,如果您告诉客户使用来自某些 CA 的证书,然后不接受来自这些 CA 的有效证书链,您可能会有不满意的客户来追随您各种沉重、尖锐和/或其他令人不快的物体。

no 'CAs'(长度为 0 的数组,如您所愿)的特定情况下,客户端可以从它选择的任何 CA 发送证书,服务器将根据情况接受它仅限checkClientTrusted

为了完整起见,RFC 为客户端定义了一个扩展,以指定它希望服务器证书使用的 CA,但我不知道任何支持此扩展的实现,Java/JSSE 肯定不支持,所以在实践中,服务器要么只有一个证书(每个算法),要么根据 SNI 进行选择(仅此而已),如果客户端不信任该证书太糟糕。

  • 如果您有有效的算法约束(现在您通常默认执行,即使您没有明确设置一些),它们不会在链中的最后一个证书上强制执行(假定的锚点) ) 如果它在getAcceptedIssuers 返回的证书中,这些证书被假定为(实际)锚。换句话说,如果证书是信任锚,则用户可能已决定信任它,即使它可能使用不符合当前标准的算法(如 MD5 或小于 1024 的 RSA)。

将证书放入信任库或以其他方式使其成为锚点的人是否真正正确地评估了其安全性是 Java 不会尝试回答的另一个问题。即使是 Stackexchange 也可能无法做到这一点,尽管我相信这里会有人乐于尝试。 (我不承诺我是否会成为他们中的一员。)

【讨论】:

  • 感谢您的宝贵时间和回答!我的设置是,客户端和服务器都有一个密钥库,里面有自己的密钥对和对方的自签名证书。两者都使用相同的 KeyManager 和 TrustManager - 自定义 KeyManager 返回自己的密钥,TrustManager 返回对方的证书。我启用了needClientAuth(在服务器上)并在客户端上使用了默认的 SSL 实现,并且连接仍然有效(我不想要 - 只有我为对方保存在密钥库中的证书应该被接受)。我还做错了什么?
  • 我刚刚发现,当我从服务器的密钥库中删除客户端的证书时,如果设置了needClientAuth,则握手失败。所以,这行得通,只是getAcceptedIssuers 没有按应有的方式工作,或者我仍然不明白,因为显然,委托(默认X509[Extended]TrustManager)接受客户的证书,尽管它是自签名的而不是在getAcceptedIssuers.
  • 正如我所说,getAcceptedIssuers 不用于决定是否信任证书。 JSSE 调用 .check{Client,Server}Trusted,如果您使用从密钥库文件初始化的标准信任管理器,该调用将信任任何正确链接到给定密钥库中的锚点的证书(如果启用该选项,则不会过期或撤销,并且服务器证书仅在启用该选项时与预期的主机名匹配),否则不会。由于您在信任管理器中有客户端证书,checkClientTrusted 确实接受它,getAcceptedIssuers 无关紧要。
  • 另外需要明确的是,keystore 和 truststore 是不同的概念,虽然两者都通常存储在 keystore 格式中并且可以在同一个 keystore 文件中.服务器的 keystore 是它发送给客户端并用于验证自身(服务器)的私钥和证书/链组合;服务器的 truststore 包含证书而不是私钥,它们将或可用于验证客户端用来验证 自身 的证书和密钥(客户端)。
  • @dave_thompson_085 如何向信任锚和接受的颁发者提供不同的列表,类似于 SSLCADNRequestFile 和 SSLCACertificateFile?
猜你喜欢
  • 2014-04-23
  • 2021-01-27
  • 2019-01-15
  • 1970-01-01
  • 1970-01-01
  • 2015-12-08
  • 1970-01-01
  • 1970-01-01
  • 2013-10-01
相关资源
最近更新 更多