证书验证有两个方面(一般来说):
- 验证证书是否真实且由您信任的人颁发(这是 PKI 方面)。
- 验证它是否属于您要连接的主机名(即主机名验证)。
(如果您需要类比,也许this question, about libcurl 可能会感兴趣。)
根据您的说法,该特定证书对于其他主机名是可信且有效的。因此,它将通过 PKI 验证(TrustStrategy 实现的内容)。
您需要构建一个例外情况,仅针对该特定证书,用于主机名验证方面。
我不记得它是如何与 Apache HTTP Client 4.4 一起使用的,但您应该使用自己的验证器而不是 DefaultHostnameVerifier。
实现的方法有verify(String hostYouAreAfter, SSLSession sessionYouActuallyGet)和verify(String hostYouAreAfter, X509Certificate certYouActuallyGet)。
您可以按照以下方式提供自己的实现:
public verify(String hostYouAreAfter, X509Certificate certYouActuallyGet) {
if (certYouActuallyGet.equals(referenceCertificate)) {
if ("server4.company.com".equalsIgnoreCase(hostYouAreAfter)) {
// All good, don't fail and throw an exception.
} else {
super.verify(hostYouAreAfter, certYouActuallyGet);
}
} else {
super.verify(hostYouAreAfter, certYouActuallyGet);
}
}
您可以对verify(String,SSLSession) 执行相同操作,并从SSLSession 的对等链(位置0)获取X509Certificate。逻辑是一样的,只是需要返回true/false,而不是抛出异常。
在这里,我假设您已从具有该特定证书的参考位置加载 referenceCertificate。例如,您可以从已知的密钥库中加载它,或者使用应用程序中配置的参考 PEM 文件中的 CertificateFactory 加载它。
与 TrustStrategy 有两个关键区别,您可以将 isTrusted(final X509Certificate[] chain, final String authType) 实现为 return "nice guy".equalsIgnoreCase(issuerDN.getName());:
- 实际上,您只是为该证书创建此例外情况,而不是为任何其他可能与您所追求的名称发生问题的证书。
- 它只影响您希望连接到该特定主机(而不是其他主机)的连接。您确实可以访问
HostnameVerifier.verify(...) 的第一个String 参数,这是您打算连接到的主机名。至少您可以将其用于与您获得的证书进行比较,这是 TrustStrategy 所没有的。