实施 X509TrustManager

Implementing X509TrustManager

我目前正在尝试通过 SSL/TLS 在 java 中通过互联网传输数据,我希望双方都对自己进行身份验证。我自己实现了KeyManager来加载密钥对并向对方出示相应的证书。

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

为什么证书没有被拒绝?

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];
    }

}

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

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

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

  • 在服务器中,(仅)如果启用客户端身份验证(通过调用 needClientAuth(true)wantClientAuth(true)),其元素中的主题名称用于在 server's CertificateRequest message. This is not forced to be the same as the list of CAs that will be used as trust anchors for the client cert chain, if one is received; in fact, the trustmanager isn't actually required to use a list of trust anchors or even the rest of the standard validation algorithm -- 尽管如果您的 'delegate' 是使用标准 CertPathValidator 的标准 X509[Extended]TrustManager,那么 就是这样。但是,如果您告诉客户使用来自某些 CA 的证书,然后不接受来自这些 CA 的有效证书链,您可能会有不满意的客户,他们可能会来找您各种重的、尖锐的 and/or 其他不愉快的物体。

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

接受它

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

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

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