你还没有发布任何代码,所以我不能确定你到底做了什么。但是,我假设您SSLContext
只使用自定义X509TrustManager
子类来设置。这很好,但是您可以做的是让您的自定义信任管理器实现也链接到内置的信任管理器。您可以在设置信任管理器时执行此操作;像这样的东西应该工作:
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()
依次调用每个信任管理器来检查每个信任管理器。如果他们都不信任证书,您可以应用自己的证书检查。如果通过,您可以正常返回。如果没有,只需像其他方式一样抛出 a 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
内容,因此在这种情况下,您需要做的不仅仅是简单的等号匹配。