You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2021/04/10 09:48:29 UTC

[httpcomponents-client] 02/03: Don't match CN for IP addresses

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit 1bee47d2997c00d1508bd7b425ae2402214d850b
Author: Peter Dettman <pe...@bouncycastle.org>
AuthorDate: Sat Apr 10 11:27:17 2021 +0700

    Don't match CN for IP addresses
---
 .../client5/http/ssl/DefaultHostnameVerifier.java  | 80 +++++++++++++---------
 .../http/ssl/TestDefaultHostnameVerifier.java      | 25 +++----
 2 files changed, 56 insertions(+), 49 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java
index 73ca314..ec640c9 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ssl/DefaultHostnameVerifier.java
@@ -101,28 +101,24 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
     }
 
     @Override
-    public void verify(
-            final String host, final X509Certificate cert) throws SSLException {
+    public void verify(final String host, final X509Certificate cert) throws SSLException {
         final HostNameType hostType = determineHostFormat(host);
-        final List<SubjectName> subjectAlts = getSubjectAltNames(cert);
-        if (subjectAlts != null && !subjectAlts.isEmpty()) {
-            switch (hostType) {
-                case IPv4:
-                    matchIPAddress(host, subjectAlts);
-                    break;
-                case IPv6:
-                    matchIPv6Address(host, subjectAlts);
-                    break;
-                default:
-                    // In case there are no SubjectName.DNS entries, fallback to CN matching
-                    if (!matchDNSName(host, subjectAlts, this.publicSuffixMatcher)) {
-                        matchCN(host, cert, this.publicSuffixMatcher);
-                    }
+        switch (hostType) {
+        case IPv4:
+            matchIPAddress(host, getSubjectAltNames(cert, SubjectName.IP));
+            break;
+        case IPv6:
+            matchIPv6Address(host, getSubjectAltNames(cert, SubjectName.IP));
+            break;
+        default:
+            final List<SubjectName> subjectAlts = getSubjectAltNames(cert, SubjectName.DNS);
+            if (subjectAlts.isEmpty()) {
+                // 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 of type SubjectName.DNS are available
-            matchCN(host, cert, this.publicSuffixMatcher);
         }
     }
 
@@ -154,23 +150,18 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
                 "of the subject alternative names: " + subjectAlts);
     }
 
-    static boolean matchDNSName(final String host, final List<SubjectName> subjectAlts,
-                                final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
+    static void matchDNSName(final String host, final List<SubjectName> subjectAlts,
+                             final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
         final String normalizedHost = DnsUtils.normalize(host);
-        boolean foundAnyDNS = false;
         for (int i = 0; i < subjectAlts.size(); i++) {
             final SubjectName subjectAlt = subjectAlts.get(i);
             if (subjectAlt.getType() == SubjectName.DNS) {
-                foundAnyDNS = true;
                 final String normalizedSubjectAlt = DnsUtils.normalize(subjectAlt.getValue());
                 if (matchIdentityStrict(normalizedHost, normalizedSubjectAlt, publicSuffixMatcher)) {
-                    return true;
+                    return;
                 }
             }
         }
-        if (!foundAnyDNS) {
-            return false;
-        }
         throw new SSLPeerUnverifiedException("Certificate for <" + host + "> doesn't match any " +
                 "of the subject alternative names: " + subjectAlts);
     }
@@ -180,14 +171,9 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
         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 " +
+            throw new SSLPeerUnverifiedException("Certificate subject for <" + host + "> doesn't contain " +
                     "a common name and does not have alternative names");
         }
-        matchCN(host, cn, publicSuffixMatcher);
-    }
-
-    static void matchCN(final String host, final String cn,
-                        final PublicSuffixMatcher publicSuffixMatcher) throws SSLException {
         final String normalizedHost = DnsUtils.normalize(host);
         final String normalizedCn = DnsUtils.normalize(cn);
         if (!matchIdentityStrict(normalizedHost, normalizedCn, publicSuffixMatcher)) {
@@ -326,6 +312,32 @@ public final class DefaultHostnameVerifier implements HttpClientHostnameVerifier
         }
     }
 
+    static List<SubjectName> getSubjectAltNames(final X509Certificate cert, final int subjectName) {
+        try {
+            final Collection<List<?>> entries = cert.getSubjectAlternativeNames();
+            if (entries == null) {
+                return Collections.emptyList();
+            }
+            final List<SubjectName> result = new ArrayList<>();
+            for (final List<?> entry : entries) {
+                final Integer type = entry.size() >= 2 ? (Integer) entry.get(0) : null;
+                if (type != null) {
+                    if (type == subjectName) {
+                        final Object o = entry.get(1);
+                        if (o instanceof String) {
+                            result.add(new SubjectName((String) o, type));
+                        } else if (o instanceof byte[]) {
+                            // TODO ASN.1 DER encoded form
+                        }
+                    }
+                }
+            }
+            return result;
+        } catch (final CertificateParsingException ignore) {
+            return Collections.emptyList();
+        }
+    }
+
     /*
      * Normalize IPv6 or DNS name.
      */
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java
index 3ec90e8..88487ff 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/ssl/TestDefaultHostnameVerifier.java
@@ -432,31 +432,26 @@ public class TestDefaultHostnameVerifier {
 
     @Test
     public void testMatchDNSName() throws Exception {
-        Assert.assertTrue(DefaultHostnameVerifier.matchDNSName(
+        DefaultHostnameVerifier.matchDNSName(
                 "host.domain.com",
                 Collections.singletonList(SubjectName.DNS("*.domain.com")),
-                publicSuffixMatcher));
-        Assert.assertTrue(DefaultHostnameVerifier.matchDNSName(
+                publicSuffixMatcher);
+        DefaultHostnameVerifier.matchDNSName(
                 "host.xx",
                 Collections.singletonList(SubjectName.DNS("*.xx")),
-                publicSuffixMatcher));
-        Assert.assertTrue(DefaultHostnameVerifier.matchDNSName(
+                publicSuffixMatcher);
+        DefaultHostnameVerifier.matchDNSName(
                 "host.appspot.com",
                 Collections.singletonList(SubjectName.DNS("*.appspot.com")),
-                publicSuffixMatcher));
-        Assert.assertTrue(DefaultHostnameVerifier.matchDNSName(
+                publicSuffixMatcher);
+        DefaultHostnameVerifier.matchDNSName(
                 "demo-s3-bucket.s3.eu-central-1.amazonaws.com",
                 Collections.singletonList(SubjectName.DNS("*.s3.eu-central-1.amazonaws.com")),
-                publicSuffixMatcher));
-        Assert.assertTrue(DefaultHostnameVerifier.matchDNSName(
+                publicSuffixMatcher);
+        DefaultHostnameVerifier.matchDNSName(
                 "hostname-workspace-1.local",
                 Collections.singletonList(SubjectName.DNS("hostname-workspace-1.local")),
-                publicSuffixMatcher));
-
-        Assert.assertFalse(DefaultHostnameVerifier.matchDNSName(
-            "host.domain.com",
-            Collections.singletonList(SubjectName.IP("1.1.1.1")),
-            publicSuffixMatcher));
+                publicSuffixMatcher);
 
         try {
             DefaultHostnameVerifier.matchDNSName(