是否可以向 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();
证书验证有两个方面(一般):
- 验证证书是否真实并由您信任的人颁发(这是 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
。逻辑是一样的,但是你需要return true/false 而不是抛出异常。
在这里,我假设您已经从具有该特定证书的参考位置加载了 referenceCertificate
。例如,您可以从已知的密钥库加载它,或者使用应用程序中配置的参考 PEM 文件中的 CertificateFactory
加载它。
与 TrustStrategy
有两个主要区别,您将 isTrusted(final X509Certificate[] chain, final String authType)
实现为 return "nice guy".equalsIgnoreCase(issuerDN.getName());
:
- 您实际上只是为那个证书创建了这个例外情况,而不是为任何其他恰好也与您之后的名称有问题的证书。
- 它只会影响您希望连接到该特定主机(而不是其他主机)的连接。您确实可以访问
HostnameVerifier.verify(...)
的第一个 String
参数,这是您 打算 连接的主机名。至少你可以用它来与你获得的证书进行比较,这是你用 TrustStrategy
. 无法获得的东西
我们正在使用 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
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();
证书验证有两个方面(一般):
- 验证证书是否真实并由您信任的人颁发(这是 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
。逻辑是一样的,但是你需要return true/false 而不是抛出异常。
在这里,我假设您已经从具有该特定证书的参考位置加载了 referenceCertificate
。例如,您可以从已知的密钥库加载它,或者使用应用程序中配置的参考 PEM 文件中的 CertificateFactory
加载它。
与 TrustStrategy
有两个主要区别,您将 isTrusted(final X509Certificate[] chain, final String authType)
实现为 return "nice guy".equalsIgnoreCase(issuerDN.getName());
:
- 您实际上只是为那个证书创建了这个例外情况,而不是为任何其他恰好也与您之后的名称有问题的证书。
- 它只会影响您希望连接到该特定主机(而不是其他主机)的连接。您确实可以访问
HostnameVerifier.verify(...)
的第一个String
参数,这是您 打算 连接的主机名。至少你可以用它来与你获得的证书进行比较,这是你用TrustStrategy
. 无法获得的东西