Java 实现X509TrustManager

Java 实现X509TrustManager,java,ssl,x509trustmanager,Java,Ssl,X509trustmanager,我目前正试图通过java中的SSL/TLS在internet上传输数据,我希望双方都进行身份验证。我自己实现了KeyManager来加载密钥对并向另一方提供适当的证书 现在,我正在尝试检查证书,并通过实现我自己的TrustManager来检查证书(双方都持有另一方的证书,所有内容都是自签名的)。然而,getAcceptedAssuers不能像我希望的那样工作,因为即使我返回none,连接仍然可以毫无问题地建立起来 为什么证书没有被拒绝 protected static class Selecti

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

现在,我正在尝试检查证书,并通过实现我自己的TrustManager来检查证书(双方都持有另一方的证书,所有内容都是自签名的)。然而,getAcceptedAssuers不能像我希望的那样工作,因为即使我返回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)
    ),则其元素中的主题名称将用于在中创建CA列表。如果收到CA列表,则不会强制该列表与将用作客户端证书链信任锚的CA列表相同;事实上,trustmanager实际上并不需要使用信任锚列表,甚至不需要使用信任锚列表的其余部分——尽管如果您的“委托”是标准的
    X509[Extended]trustmanager
    ,它使用的是标准的
    CertPathValidator
    。但是,如果您告诉客户使用来自某些CA的证书,然后不接受来自这些CA的有效证书链,则您可能会遇到不高兴的客户,他们可能会带着各种沉重、尖锐和/或其他不愉快的物品追上您

    在没有“CA”(长度为0的数组,如您所知)的特定情况下,客户端可以从其选择的任何CA发送证书,服务器将仅根据
    checkClientTrusted
    接受证书

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

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

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


真正的握手可能发生在其他地方吗?感谢您抽出时间和回答!我的设置是,客户端和服务器都有一个密钥库,其中包含自己的密钥对和另一方的自签名证书。两者都使用相同的密钥管理器和信任管理器-自定义密钥管理器返回它们自己的密钥,信任管理器返回另一方的证书。我启用了
needClientAuth
(在服务器上)并在客户机上使用了默认的SSL实现,并且连接仍然有效(我不希望这样-只接受我为另一方保存在密钥库中的证书)。我还做错了什么?我刚刚发现,当我从服务器的密钥库中删除客户端的证书时,如果设置了
needClientAuth
,握手就会失败。所以,这是可行的,只是
GetAcceptedAssuers
没有像它应该的那样工作,或者我仍然不理解它,因为显然,委托(默认
X509[Extended]TrustManager
)接受客户机的证书,尽管它是自签名的,而不是在
GetAcceptedAssuers
中,
getAcceptedIssuers
不用于决定是否信任证书。JSSE调用
。请检查{Client,Server}Trusted
,如果使用从密钥库文件初始化的标准trustmanager,则该调用将信任正确链接到给定密钥库中锚的任何证书(如果启用该选项,则未过期或吊销,如果启用该选项,则for server cert仅与预期主机名匹配)而不是其他。由于您在trustmanager中拥有客户端证书,
checkClientTrusted
确实接受它,并且
getAcceptedIssuers
是不相关的。同样要明确的是,密钥库和信任库是不同的概念,尽管它们通常以密钥库格式存储,并且可以位于同一密钥库文件中。服务器的密钥库e是私钥和证书/链的组合,它发送给客户端并用于对自身(服务器)进行身份验证;服务器的信任库包含证书而不是私钥,这些证书和密钥将或可用于验证客户端用于对自身(客户端)进行身份验证的证书和密钥@dave_thompson_085我如何向信任锚和接受的发行人提供不同的列表,类似于SSLCADNRequestFile和SSLCACertificateFile?