是否可以向 HttpClient 4.4 的现有 SSL 证书添加额外的子域?

Is it possible to add an additional subdomain to a existing SSL certificate for HttpClient 4.4?

我们正在使用 HttpClient 4.4 与一些外部服务器(server1.company.com、server2.company.com、server3.company.com)进行通信。他们最近添加了一个额外的环境(server4.company.com),它使用与其他 3 个相同的证书。有问题的证书将前 3 个服务器列为 "Certificate Subject Alternative Names",但未提及服务器 4。

我可以告诉 keytool 该证书对其他 SAN 有效吗?或者有没有其他方法告诉 HttpClient 'trust' 这个证书用于一些额外的域?还有其他选择吗?还是我必须返回 company.com 并要求他们获得新证书?

可以通过使用自定义 TrustStrategy

来信任某些 select 证书
SSLContext sslcontext = SSLContexts.custom()
  .loadTrustMaterial(new TrustStrategy() {
    @Override
    public boolean isTrusted(final X509Certificate[] chain, final String authType) 
        throws CertificateException {
      X509Certificate x509Certificate = chain[0];
      Principal issuerDN = x509Certificate.getIssuerDN();
      return "nice guy".equalsIgnoreCase(issuerDN.getName());
    }
  }).build();
CloseableHttpClient client = HttpClients.custom()
  .setSslcontext(sslcontext)
  .build();

证书验证有两个方面(一般):

  1. 验证证书是否真实并由您信任的人颁发(这是 PKI 方面)。
  2. 验证它是否属于您要连接的主机名(即主机名验证)。

(如果您需要类比,也许 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。逻辑是一样的,但是你需要return true/false 而不是抛出异常。

在这里,我假设您已经从具有该特定证书的参考位置加载了 referenceCertificate。例如,您可以从已知的密钥库加载它,或者使用应用程序中配置的参考 PEM 文件中的 CertificateFactory 加载它。


TrustStrategy 有两个主要区别,您将 isTrusted(final X509Certificate[] chain, final String authType) 实现为 return "nice guy".equalsIgnoreCase(issuerDN.getName());

  • 您实际上只是为那个证书创建了这个例外情况,而不是为任何其他恰好也与您之后的名称有问题的证书。
  • 它只会影响您希望连接到该特定主机(而不是其他主机)的连接。您确实可以访问 HostnameVerifier.verify(...) 的第一个 String 参数,这是您 打算 连接的主机名。至少你可以用它来与你获得的证书进行比较,这是你用 TrustStrategy.
  • 无法获得的东西