您尚未发布任何代码,因此我无法确定您实际做了什么。但是,我假设您只使用自定义的X509TrustManager 子类来设置SSLContext。这很好,但您可以做的是让您的自定义信任管理器实现也链接到内置的信任管理器。您可以在设置信任管理器时执行此操作;像这样的东西应该可以工作:
private List<X509TrustManager> trustManagers = new ArrayList<X509TrustManager>();
public MyCustomTrustManager() {
TrustManagerFactory tmFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmFactory.init((KeyStore)null);
for (TrustManager tm : tmFactory.getTrustManagers()) {
if (tm instanceof X509TrustManager)
trustManagers.add((X509TrustManager)tm);
}
}
因此,现在您的自定义信任管理器拥有所有内置信任管理器的列表。在覆盖checkServerTrusted() 时,您需要遍历内置的信任管理器并依次调用checkServerTrusted() 来检查每个信任管理器。如果他们都不信任证书,您可以应用自己的证书检查。如果通过,您可以正常返回。如果没有,只需像其他方式一样抛出CertificateException。
编辑:添加以下有关执行主机名验证等操作的内容。
您还可以验证证书中的主机名是否符合您的预期。您需要在构造函数中为自定义信任管理器传递有效的主机名,并将其存储在类中。您的checkServerTrusted() 方法将获得X509Certificate 的数组。许多“链”将仅包含一个证书,但其他“链”将包含多个,具体取决于 cA 如何签署您的证书。无论哪种方式,数组中的第一个证书应该是您想要比较的“您的”证书。
使用信任管理器检查基本证书有效性后,您需要执行以下操作:
Principal subjectDN = chain[0].getSubjectDN();
String subjectCN = parseDN(subjectDN.getName(), "CN");
if (this.allowedCN.equals(subjectCN)) {
// certificate is good
}
parseDN() 的实现由您决定。 subjectDN.getName() 将返回一个逗号分隔的键值对列表(由= 分隔),类似于C=US,ST=California,L=Mountain View,O=Google Inc,CN=www.google.com。您需要用于主机名比较的 CN(“通用名称”)值。请注意,如果您有通配符证书,它将被列为类似 *.example.com 的内容,因此在这种情况下,您需要做的不仅仅是简单的等号匹配。