HTTPCLIENT-2149: When no dNSName, match against CN
This commit is contained in:
parent
8f31e6339d
commit
58a17cc549
|
@ -101,31 +101,24 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void verify(
|
public void verify(final String host, final X509Certificate cert) throws SSLException {
|
||||||
final String host, final X509Certificate cert) throws SSLException {
|
|
||||||
final HostNameType hostType = determineHostFormat(host);
|
final HostNameType hostType = determineHostFormat(host);
|
||||||
final List<SubjectName> subjectAlts = getSubjectAltNames(cert);
|
switch (hostType) {
|
||||||
if (subjectAlts != null && !subjectAlts.isEmpty()) {
|
case IPv4:
|
||||||
switch (hostType) {
|
matchIPAddress(host, getSubjectAltNames(cert, SubjectName.IP));
|
||||||
case IPv4:
|
break;
|
||||||
matchIPAddress(host, subjectAlts);
|
case IPv6:
|
||||||
break;
|
matchIPv6Address(host, getSubjectAltNames(cert, SubjectName.IP));
|
||||||
case IPv6:
|
break;
|
||||||
matchIPv6Address(host, subjectAlts);
|
default:
|
||||||
break;
|
final List<SubjectName> subjectAlts = getSubjectAltNames(cert, SubjectName.DNS);
|
||||||
default:
|
if (subjectAlts.isEmpty()) {
|
||||||
matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
|
// CN matching has been deprecated by rfc2818 and can be used
|
||||||
|
// as fallback only when no subjectAlts of type SubjectName.DNS are available
|
||||||
|
matchCN(host, cert, this.publicSuffixMatcher);
|
||||||
|
} else {
|
||||||
|
matchDNSName(host, subjectAlts, this.publicSuffixMatcher);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// CN matching has been deprecated by rfc2818 and can be used
|
|
||||||
// as fallback only when no subjectAlts are available
|
|
||||||
final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
|
|
||||||
final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
|
|
||||||
if (cn == null) {
|
|
||||||
throw new SSLException("Certificate subject for <" + host + "> doesn't contain " +
|
|
||||||
"a common name and does not have alternative names");
|
|
||||||
}
|
|
||||||
matchCN(host, cn, this.publicSuffixMatcher);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +166,14 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
|
||||||
"of the subject alternative names: " + subjectAlts);
|
"of the subject alternative names: " + subjectAlts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void matchCN(final String host, final String cn,
|
static void matchCN(final String host, final X509Certificate cert,
|
||||||
final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
|
final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
|
||||||
|
final X500Principal subjectPrincipal = cert.getSubjectX500Principal();
|
||||||
|
final String cn = extractCN(subjectPrincipal.getName(X500Principal.RFC2253));
|
||||||
|
if (cn == null) {
|
||||||
|
throw new SSLPeerUnverifiedException("Certificate subject for <" + host + "> doesn't contain " +
|
||||||
|
"a common name and does not have alternative names");
|
||||||
|
}
|
||||||
final String normalizedHost = DnsUtils.normalize(host);
|
final String normalizedHost = DnsUtils.normalize(host);
|
||||||
final String normalizedCn = DnsUtils.normalize(cn);
|
final String normalizedCn = DnsUtils.normalize(cn);
|
||||||
if (!matchIdentityStrict(normalizedHost, normalizedCn, publicSuffixMatcher)) {
|
if (!matchIdentityStrict(normalizedHost, normalizedCn, publicSuffixMatcher)) {
|
||||||
|
@ -288,6 +287,10 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<SubjectName> getSubjectAltNames(final X509Certificate cert) {
|
static List<SubjectName> getSubjectAltNames(final X509Certificate cert) {
|
||||||
|
return getSubjectAltNames(cert, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SubjectName> getSubjectAltNames(final X509Certificate cert, final int subjectName) {
|
||||||
try {
|
try {
|
||||||
final Collection<List<?>> entries = cert.getSubjectAlternativeNames();
|
final Collection<List<?>> entries = cert.getSubjectAlternativeNames();
|
||||||
if (entries == null) {
|
if (entries == null) {
|
||||||
|
@ -297,7 +300,7 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
|
||||||
for (final List<?> entry : entries) {
|
for (final List<?> entry : entries) {
|
||||||
final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null;
|
final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null;
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
if (type == SubjectName.DNS || type == SubjectName.IP) {
|
if (type == subjectName || -1 == subjectName) {
|
||||||
final Object o = entry.get(1);
|
final Object o = entry.get(1);
|
||||||
if (o instanceof String) {
|
if (o instanceof String) {
|
||||||
result.add(new SubjectName((String) o, type));
|
result.add(new SubjectName((String) o, type));
|
||||||
|
|
|
@ -575,4 +575,18 @@ public class CertificatesToPlayWith {
|
||||||
"-----END CERTIFICATE-----"
|
"-----END CERTIFICATE-----"
|
||||||
).getBytes();
|
).getBytes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subject CN=www.foo.com, subjectAlt=IP Address:127.0.0.1
|
||||||
|
*/
|
||||||
|
public final static byte[] SUBJECT_ALT_IP_ONLY = (
|
||||||
|
"-----BEGIN CERTIFICATE-----\n" +
|
||||||
|
"MIIBQjCB6qADAgECAgYBeLZWSL0wCgYIKoZIzj0EAwIwFjEUMBIGA1UEAwwLd3d3\n" +
|
||||||
|
"LmZvby5jb20wHhcNMjEwNDA5MTExMzI2WhcNMjEwNDA5MTE0MzMxWjAWMRQwEgYD\n" +
|
||||||
|
"VQQDDAt3d3cuZm9vLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPC+8O/v\n" +
|
||||||
|
"IPWSC/iPdPAgpgzpyLKZevNH8ENOb6PaJRDyNHdd1MbJvurKtJ+HP6UYV3keNHUk\n" +
|
||||||
|
"r657s2JjufiTmuSjJDAiMAwGA1UdEwQFMAMBAf8wEgYDVR0RAQH/BAgwBocEfwAA\n" +
|
||||||
|
"ATAKBggqhkjOPQQDAgNHADBEAiA2svKw50Mr5nnF4TXyFcvzhJWkC+7m46JROMiy\n" +
|
||||||
|
"TMt3BQIgK5IHScVH6Cbi106y+BILx4U0Ygt5IFNnMx/K+Jusuls=\n" +
|
||||||
|
"-----END CERTIFICATE-----"
|
||||||
|
).getBytes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,9 +176,10 @@ public class TestDefaultHostnameVerifier {
|
||||||
in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1);
|
in = new ByteArrayInputStream(CertificatesToPlayWith.IP_1_1_1_1);
|
||||||
x509 = (X509Certificate) cf.generateCertificate(in);
|
x509 = (X509Certificate) cf.generateCertificate(in);
|
||||||
impl.verify("1.1.1.1", x509);
|
impl.verify("1.1.1.1", x509);
|
||||||
|
impl.verify("dummy-value.com", x509);
|
||||||
|
|
||||||
exceptionPlease(impl, "1.1.1.2", x509);
|
exceptionPlease(impl, "1.1.1.2", x509);
|
||||||
exceptionPlease(impl, "dummy-value.com", x509);
|
exceptionPlease(impl, "not-the-cn.com", x509);
|
||||||
|
|
||||||
in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
|
in = new ByteArrayInputStream(CertificatesToPlayWith.EMAIL_ALT_SUBJECT_NAME);
|
||||||
x509 = (X509Certificate) cf.generateCertificate(in);
|
x509 = (X509Certificate) cf.generateCertificate(in);
|
||||||
|
@ -392,6 +393,21 @@ public class TestDefaultHostnameVerifier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHTTPCLIENT_2149() throws Exception {
|
||||||
|
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||||
|
final InputStream in = new ByteArrayInputStream(CertificatesToPlayWith.SUBJECT_ALT_IP_ONLY);
|
||||||
|
final X509Certificate x509 = (X509Certificate) cf.generateCertificate(in);
|
||||||
|
|
||||||
|
Assert.assertEquals("CN=www.foo.com", x509.getSubjectDN().getName());
|
||||||
|
|
||||||
|
impl.verify("127.0.0.1", x509);
|
||||||
|
impl.verify("www.foo.com", x509);
|
||||||
|
|
||||||
|
exceptionPlease(impl, "127.0.0.2", x509);
|
||||||
|
exceptionPlease(impl, "www.bar.com", x509);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExtractCN() throws Exception {
|
public void testExtractCN() throws Exception {
|
||||||
Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
|
Assert.assertEquals("blah", DefaultHostnameVerifier.extractCN("cn=blah, ou=blah, o=blah"));
|
||||||
|
@ -436,6 +452,16 @@ public class TestDefaultHostnameVerifier {
|
||||||
"hostname-workspace-1.local",
|
"hostname-workspace-1.local",
|
||||||
Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
|
Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
|
||||||
publicSuffixMatcher);
|
publicSuffixMatcher);
|
||||||
|
|
||||||
|
try {
|
||||||
|
DefaultHostnameVerifier.matchDNSName(
|
||||||
|
"host.domain.com",
|
||||||
|
Collections.singletonList(SubjectName.DNS("some.other.com")),
|
||||||
|
publicSuffixMatcher);
|
||||||
|
Assert.fail("SSLException should have been thrown");
|
||||||
|
} catch (final SSLException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue