There are two aspects of certificate verifications (in general):
- Verifying that the certificate is genuine and issued by someone you trust (that's the PKI aspect).
- Verifying that it belongs to the host name you want to connect to (that's the host name verification).
(Perhaps this question, about libcurl might be of interest if you need analogies.)
According to what you are saying, that particular certificate is trusted and valid for other host names. Hence, it will pass the PKI verification (what the TrustStrategy
implements).
What you need is to make build an exceptional case, only for that particular certificate, for the host name verification aspect.
I can't remember off the top of my head how it is used with Apache HTTP Client 4.4, but you should use your own verifier instead of the DefaultHostnameVerifier
.
The methods to implement are verify(String hostYouAreAfter, SSLSession sessionYouActuallyGet)
and verify(String hostYouAreAfter, X509Certificate certYouActuallyGet)
.
You can provide your own implementation along these lines:
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);
}
}
You can do the same with verify(String,SSLSession)
and get the X509Certificate
from the SSLSession
's peer chain (position 0). The logic is the same, but you need to return true/false instead of throwing exceptions.
Here, I'm assuming that you've loaded referenceCertificate
from a place of reference where have that particular certificate. You could for example load it from a known keystore, or load it with a CertificateFactory
from a reference PEM file configured in your application.
There are two key differences with a TrustStrategy
where you'd implement isTrusted(final X509Certificate[] chain, final String authType)
as return "nice guy".equalsIgnoreCase(issuerDN.getName());
:
- You're actually making this exceptional case only for that very certificate, not for any other certificate that would also happen to be issue with the name you're after.
- It only affects the connections where you expect to connect to that particular host (not other hosts). You indeed have access to the first
String
parameter of HostnameVerifier.verify(...)
, which is the host name you intend to connect to. At least you have it to use for comparison with the certificate you get, which is something you don't get with a TrustStrategy
.