VYPR
Moderate severityNVD Advisory· Published Aug 21, 2014· Updated May 6, 2026

CVE-2014-3577

CVE-2014-3577

Description

org.apache.http.conn.ssl.AbstractVerifier in Apache HttpComponents HttpClient before 4.3.5 and HttpAsyncClient before 4.0.2 does not properly verify that the server hostname matches a domain name in the subject's Common Name (CN) or subjectAltName field of the X.509 certificate, which allows man-in-the-middle attackers to spoof SSL servers via a "CN=" string in a field in the distinguished name (DN) of a certificate, as demonstrated by the "foo,CN=www.apache.org" string in the O field.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.httpcomponents:httpclientMaven
< 4.3.54.3.5

Affected products

2

Patches

1
51cc67567765

Improved cert subject parsing

https://github.com/apache/httpcomponents-clientOleg KalnichevskiJul 28, 2014via ghsa
2 files changed · +58 50
  • httpclient/src/main/java/org/apache/http/conn/ssl/AbstractVerifier.java+37 38 modified
    @@ -34,14 +34,20 @@
     import java.security.cert.Certificate;
     import java.security.cert.CertificateParsingException;
     import java.security.cert.X509Certificate;
    +import java.util.ArrayList;
     import java.util.Arrays;
     import java.util.Collection;
     import java.util.Iterator;
     import java.util.LinkedList;
     import java.util.List;
     import java.util.Locale;
    -import java.util.StringTokenizer;
     
    +import javax.naming.InvalidNameException;
    +import javax.naming.NamingException;
    +import javax.naming.directory.Attribute;
    +import javax.naming.directory.Attributes;
    +import javax.naming.ldap.LdapName;
    +import javax.naming.ldap.Rdn;
     import javax.net.ssl.SSLException;
     import javax.net.ssl.SSLSession;
     import javax.net.ssl.SSLSocket;
    @@ -253,47 +259,40 @@ boolean validCountryWildcard(final String cn) {
         }
     
         public static String[] getCNs(final X509Certificate cert) {
    -        final LinkedList<String> cnList = new LinkedList<String>();
    -        /*
    -          Sebastian Hauer's original StrictSSLProtocolSocketFactory used
    -          getName() and had the following comment:
    -
    -                Parses a X.500 distinguished name for the value of the
    -                "Common Name" field.  This is done a bit sloppy right
    -                 now and should probably be done a bit more according to
    -                <code>RFC 2253</code>.
    -
    -           I've noticed that toString() seems to do a better job than
    -           getName() on these X500Principal objects, so I'm hoping that
    -           addresses Sebastian's concern.
    -
    -           For example, getName() gives me this:
    -           1.2.840.113549.1.9.1=#16166a756c6975736461766965734063756362632e636f6d
    -
    -           whereas toString() gives me this:
    -           EMAILADDRESS=juliusdavies@cucbc.com
    -
    -           Looks like toString() even works with non-ascii domain names!
    -           I tested it with "&#x82b1;&#x5b50;.co.jp" and it worked fine.
    -        */
    -
             final String subjectPrincipal = cert.getSubjectX500Principal().toString();
    -        final StringTokenizer st = new StringTokenizer(subjectPrincipal, ",+");
    -        while(st.hasMoreTokens()) {
    -            final String tok = st.nextToken().trim();
    -            if (tok.length() > 3) {
    -                if (tok.substring(0, 3).equalsIgnoreCase("CN=")) {
    -                    cnList.add(tok.substring(3));
    -                }
    -            }
    +        try {
    +            return extractCNs(subjectPrincipal);
    +        } catch (SSLException ex) {
    +            return null;
             }
    -        if(!cnList.isEmpty()) {
    -            final String[] cns = new String[cnList.size()];
    -            cnList.toArray(cns);
    -            return cns;
    -        } else {
    +    }
    +
    +    static String[] extractCNs(final String subjectPrincipal) throws SSLException {
    +        if (subjectPrincipal == null) {
                 return null;
             }
    +        final List<String> cns = new ArrayList<String>();
    +        try {
    +            final LdapName subjectDN = new LdapName(subjectPrincipal);
    +            final List<Rdn> rdns = subjectDN.getRdns();
    +            for (int i = rdns.size() - 1; i >= 0; i--) {
    +                final Rdn rds = rdns.get(i);
    +                final Attributes attributes = rds.toAttributes();
    +                final Attribute cn = attributes.get("cn");
    +                if (cn != null) {
    +                    try {
    +                        final Object value = cn.get();
    +                        if (value != null) {
    +                            cns.add(value.toString());
    +                        }
    +                    } catch (NamingException ignore) {
    +                    }
    +                }
    +            }
    +        } catch (InvalidNameException e) {
    +            throw new SSLException(subjectPrincipal + " is not a valid X500 distinguished name");
    +        }
    +        return cns.isEmpty() ? null : cns.toArray(new String[ cns.size() ]);
         }
     
         /**
    
  • httpclient/src/test/java/org/apache/http/conn/ssl/TestHostnameVerifier.java+21 12 modified
    @@ -29,7 +29,6 @@
     
     import java.io.ByteArrayInputStream;
     import java.io.InputStream;
    -import java.security.Principal;
     import java.security.cert.CertificateFactory;
     import java.security.cert.X509Certificate;
     import java.util.Arrays;
    @@ -38,7 +37,6 @@
     
     import org.junit.Assert;
     import org.junit.Test;
    -import org.mockito.Mockito;
     
     /**
      * Unit tests for {@link X509HostnameVerifier}.
    @@ -349,16 +347,27 @@ public void testHTTPCLIENT_1255() {
             checkMatching(shv, "mail.a.b.c.com", cns, alt, false); // OK
         }
     
    -    public void testGetCNs() {
    -        final Principal principal = Mockito.mock(Principal.class);
    -        final X509Certificate cert = Mockito.mock(X509Certificate.class);
    -        Mockito.when(cert.getSubjectDN()).thenReturn(principal);
    -        Mockito.when(principal.toString()).thenReturn("bla,  bla, blah");
    -        Assert.assertArrayEquals(new String[] {}, AbstractVerifier.getCNs(cert));
    -        Mockito.when(principal.toString()).thenReturn("Cn=,  Cn=  , CN, OU=CN=");
    -        Assert.assertArrayEquals(new String[] {}, AbstractVerifier.getCNs(cert));
    -        Mockito.when(principal.toString()).thenReturn("  Cn=blah,  CN= blah , OU=CN=yada");
    -        Assert.assertArrayEquals(new String[] {"blah", " blah"}, AbstractVerifier.getCNs(cert));
    +    @Test
    +    public void testExtractCN() throws Exception {
    +        Assert.assertArrayEquals(new String[] {"blah"}, AbstractVerifier.extractCNs("cn=blah, ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {"blah", "yada", "booh"}, AbstractVerifier.extractCNs("cn=blah, cn=yada, cn=booh"));
    +        Assert.assertArrayEquals(new String[] {"blah"}, AbstractVerifier.extractCNs("c = pampa ,  cn  =    blah    , ou = blah , o = blah"));
    +        Assert.assertArrayEquals(new String[] {"blah"}, AbstractVerifier.extractCNs("cn=\"blah\", ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {"blah  blah"}, AbstractVerifier.extractCNs("cn=\"blah  blah\", ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {"blah, blah"}, AbstractVerifier.extractCNs("cn=\"blah, blah\", ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {"blah, blah"}, AbstractVerifier.extractCNs("cn=blah\\, blah, ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {"blah"}, AbstractVerifier.extractCNs("c = cn=uuh, cn=blah, ou=blah, o=blah"));
    +        Assert.assertArrayEquals(new String[] {""}, AbstractVerifier.extractCNs("cn=   , ou=blah, o=blah"));
    +    }
    +
    +    @Test(expected = SSLException.class)
    +    public void testExtractCNInvalid1() throws Exception {
    +        AbstractVerifier.extractCNs("blah,blah");
    +    }
    +
    +    @Test(expected = SSLException.class)
    +    public void testExtractCNInvalid2() throws Exception {
    +        AbstractVerifier.extractCNs("cn,o=blah");
         }
     
     }
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

61

News mentions

0

No linked articles in our index yet.