United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6766775 X509 certificate hostname checking is broken in JDK1.6.0_10
JDK-6766775 : X509 certificate hostname checking is broken in JDK1.6.0_10

Details
Type:
Bug
Submit Date:
2008-11-03
Status:
Resolved
Updated Date:
2010-09-29
Project Name:
JDK
Resolved Date:
2008-12-19
Component:
security-libs
OS:
solaris_nevada
Sub-Component:
javax.net.ssl
CPU:
generic
Priority:
P2
Resolution:
Fixed
Affected Versions:
solaris_11
Fixed Versions:
6u12 (b03)

Related Reports
Backport:
Backport:
Relates:
Relates:
Relates:
Relates:
Relates:
Relates:
Relates:

Sub Tasks

Description
An application that uses SSL runs fine under JDK 1.6.0_07 fails on 1.6.0_10, with the error "java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found".  As far as I can tell, this means certificate client hostname checking in _10 is broken.

I've subclassed X509ExtendedTrustManager and overridden the following method and added code to print out the "hostname" parameter:

    public void checkClientTrusted(X509Certificate chain[],
      String authType, String hostname, String algorithm)

The SSL debugging trace for _10 is below, the first line is from the 'println' described above.

On _07 this prints "localhost", on _10 it prints "127.0.0.1".  The certificate contains a Subject Alternative Name DNS entry for "localhost", but not an IP entry, so the verification fails on _10 because the "hostname" parameter is being passed incorrectly as the IP address.

----------

hostname: 127.0.0.1

Found trusted certificate:
[
[
  Version: V3
  Subject: CN=localhost, OU=localhost, O=localhost, L=localhost, ST=localhost, C=LH
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  SunPKCS11-Solaris RSA public key, 1024 bits (id 141872840, session object)
  modulus: 109777218937388701784579687142656553341258321638585627485175050314459183201332477916460154772991397177884180777647990110004231594570592744683000156387973919408665778569055470325764510541090702915175442409277089172259451334766955930833463434056218955943049848174218012553219322334089538187179486760641341890673
  public exponent: 65537
  Validity: [From: Wed Dec 12 21:23:40 GMT 2007,
               To: Tue Dec 11 21:23:40 GMT 2012]
  Issuer: CN=localhost, OU=localhost, O=localhost, L=localhost, ST=localhost, C=LH
  SerialNumber: [    d790f9d4 fda5a41f]

Certificate Extensions: 9
[1]: ObjectId: 2.5.29.17 Criticality=false
SubjectAlternativeName [
  DNSName: localhost
  RFC822Name: root@localhost
]

[2]: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 21 A5 8E AF 58 A0 CF A1   49 43 67 5B AD CB F8 EF  !...X...ICg[....
0010: 3E 6F CC 43                                        >o.C
]

[CN=localhost, OU=localhost, O=localhost, L=localhost, ST=localhost, C=LH]
SerialNumber: [    d790f9d4 fda5a41f]
]

[3]: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 21 A5 8E AF 58 A0 CF A1   49 43 67 5B AD CB F8 EF  !...X...ICg[....
0010: 3E 6F CC 43                                        >o.C
]
]

[4]: ObjectId: 2.16.840.1.113730.1.13 Criticality=false
Extension unknown: DER encoded OCTET string =
0000: 04 24 16 22 4F 70 65 6E   53 6F 6C 61 72 69 73 2E  .$."OpenSolaris.
0010: 6F 72 67 20 43 6C 69 65   6E 74 20 43 65 72 74 69  org Client Certi
0020: 66 69 63 61 74 65                                  ficate


[5]: ObjectId: 2.5.29.18 Criticality=false
IssuerAlternativeName [
  DNSName: localhost
  RFC822Name: root@localhost
]

[6]: ObjectId: 2.16.840.1.113730.1.1 Criticality=false
NetscapeCertType [
   SSL client
]

[7]: ObjectId: 2.5.29.19 Criticality=true
BasicConstraints:[
  CA:false
  PathLen: undefined
]

[8]: ObjectId: 2.5.29.37 Criticality=true
ExtendedKeyUsages [
  clientAuth
]

[9]: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
  DigitalSignature
  Key_Encipherment
]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 3B E1 09 78 7A FB 9E D8   17 B9 0C E0 FE 59 E6 5E  ;..xz........Y.^
0010: 35 70 85 CE B6 1F AD DD   17 13 60 CC BE 9F D6 53  5p........`....S
0020: 33 B1 1E 43 A5 9E 21 A0   52 21 A1 C2 DA 32 75 94  3..C..!.R!...2u.
0030: 78 A3 7C 07 85 8C 80 AA   AD 08 6E B5 56 47 4B EF  x.........n.VGK.
0040: D3 68 19 57 2E C6 71 3F   3A FE EA AA D0 7E BF 84  .h.W..q?:.......
0050: 00 2F D1 1E 48 A2 46 92   EA 63 1F F7 77 92 BB 23  ./..H.F..c..w..#
0060: 92 A0 12 24 5D EE 8A B4   D6 26 D4 98 84 36 F9 09  ...$]....&...6..
0070: FA 3D D3 73 E0 CE 58 9B   E6 64 8E FC DE 8F 27 5F  .=.s..X..d....'_

]
XML-RPC Weblistener, SEND TLSv1 ALERT:  fatal, description = certificate_unknown
XML-RPC Weblistener, WRITE: TLSv1 Alert, length = 2
XML-RPC Weblistener, called closeSocket()
XML-RPC Weblistener, handling exception: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found
XML-RPC Weblistener, called close()
XML-RPC Weblistener, called closeInternal(true)
03 Nov 2008 12:52:39,421 ERROR java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found
        at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1591)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:187)
        at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:181)
        at com.sun.net.ssl.internal.ssl.ServerHandshaker.clientCertificate(ServerHandshaker.java:1253)
        at com.sun.net.ssl.internal.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:148)
        at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:516)
        at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:454)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:884)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1096)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readDataRecord(SSLSocketImpl.java:744)
        at com.sun.net.ssl.internal.ssl.AppInputStream.read(AppInputStream.java:75)
        at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
        at java.io.BufferedInputStream.read(BufferedInputStream.java:237)
        at org.apache.xmlrpc.webserver.HttpServletRequestImpl$2.read(HttpServletRequestImpl.java:91)
        at javax.servlet.ServletInputStream.readLine(ServletInputStream.java:94)
        at org.apache.xmlrpc.webserver.HttpServletRequestImpl.readLine(HttpServletRequestImpl.java:167)
        at org.apache.xmlrpc.webserver.HttpServletRequestImpl.<init>(HttpServletRequestImpl.java:103)
        at org.apache.xmlrpc.webserver.ServletConnection.<init>(ServletConnection.java:47)
        at org.apache.xmlrpc.webserver.ServletWebServer.newTask(ServletWebServer.java:142)
        at org.apache.xmlrpc.webserver.WebServer.run(WebServer.java:326)
        at java.lang.Thread.run(Thread.java:619)
Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address 127.0.0.1 found
        at sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:155)
        at sun.security.util.HostnameChecker.match(HostnameChecker.java:75)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkIdentity(X509TrustManagerImpl.java:264)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkClientTrusted(X509TrustManagerImpl.java:238)
        at org.opensolaris.auth.ssl.SSLSocketManager$HostnameCheckTrustManager.checkClientTrusted(SSLSocketManager.java:279)
        at com.sun.net.ssl.internal.ssl.ServerHandshaker.clientCertificate(ServerHandshaker.java:1232)
        ... 17 more

                                    

Comments
PUBLIC COMMENTS

From email conversation with Xuelei Fan:

Xuelei Fan wrote:

> As the bug description, the SSL server side want to check the client 
> hostname against the client certificate(I call this as client identity 
> checking), and broken at 6u10. However, by default, the SSL server side 
> does not enable the client identity checking, so I think we need more 
> information about the user's application code, I suppose the user call 
> sun internal API, and enabled the client identity checking.

No, it calls setNeedClientAuth(true) on the SSLServerSockets it creates by subclassing X509ExtendedTrustManager.  The code in question can be seen at http://auth.opensolaris.org/svn/showfile.svn?path=/trunk/AuthSSL/src/org/opensolaris/auth/ssl/SSLSocketManager.java&revision=HEAD&name=auth

I raised a number of bugs against this area last year, for reference they are:

6586258 X509ExtendedTrustManager is in the wrong namespace       
http://monaco.sfbay/detail.jsf?cr=6586258
                                           
6586274 SSLSocketFactory and SSLServerSocketFactory can't be configured                                     
http://monaco.sfbay/detail.jsf?cr=6586274

6586276 SSLSockets and SSLEngines need a switch to enable hostname validation
http://monaco.sfbay/detail.jsf?cr=6586276

> I don't know whether or not you have approach to the application 
> programmer, I want to know whether or not their codes call the sun 
> internal API, SSLEngineImpl.trySetHostnameVerification() or 
> SSLSocketImpl.trySetHostnameVerification(), at the server accepted 
> sockets. If it is an essential call, I would like to know why, and would 
> try to make improvement.

No, neither of those APIs are called.

> At 6u10, we made a few changes on the way to get a ssl socket hostname 
> (fixed for bug 6614957). Before 6u10, the socket name will be resolved 
> from domain name services, from 6u10, the socket name will not be 
> resolved any more. For this case, the SSL server accepted socket's 
> hostname will be resolved from 127.0.0.1 to "localhost" before 6u10, but 
> it will not changed at 6u10. So, even the application enabled the client 
> identity checking, the checking will pass at 6u7, for "localhost" is an 
> subject alternative name of the client certificate.

The problem is that whatever you did for that fix has broken another part of the JDK, this from the stack trace I included in the original bug report:

/sun/security/util/HostnameChecker.java

    /**
     * Perform the check.
     *
     * @exception CertificateException if the name does not match any of
     * the names specified in the certificate
     */
    public void match(String expectedName, X509Certificate cert)
            throws CertificateException {
        if (isIpAddress(expectedName)) {
           matchIP(expectedName, cert);
        } else {
           matchDNS(expectedName, cert);
        }
    }

Now, because expectedName is *always* an IP address in 1.6.1_10, the first branch of the check will *always* be taken, and unless there is a Subject Alternative Name IP entry in the cert, the the check will *always* fail.  If you've removed the reverse DNS lookup elsewhere then you've broken this method as a result.  I can't drill down any further into the stack trace because I don't have access to the source of the com.sun.net.ssl.internal.ssl.* classes that call HostnameChecker.match.

Note that in general Subject Alternative Names use the DNS name rather than IP addresses, because it means the certificate is valid even if the service is moved to another IP or DNS round-robin'ed across several IPs.

> Typically, the server has no external knowledge of what the client's 
> identity ought to be and so checks are not possible. However, if the 
> server understand  how to check the client's identity, a proper 
> workaround is to customize the X509ExtendedTrustManager.checkClientTrust().

I disagree, client authentication is a defined part of the specification, and is often used where the communicating parties are both applications.

Also, I assume you mean 'X509ExtendedTrustManager.checkClientTrusted()'.  Overriding checkClientTrusted is exactly what the code in question does, and that's exactly what is now broken in 1.6.0_10.
                                     
2008-11-04
PUBLIC COMMENTS

The HTTPS spec said, "Typically, the server has no external knowledge of what the client's identity ought to be and so checks (other than that the client has a certificate chain rooted in an appropriate CA) are not possible. If a server has such knowledge (typically from some source external to HTTP or TLS) it SHOULD check the identity as described above<server identity>." I think maybe the DNS could be considered as an external source to TLS.
                                     
2008-11-05
EVALUATION

The application enable client identity checking during extending X509ExtendedTrustManager with a fixed identification algorithm, "https", and the bug fix of 6614957 changes the behaviors of how to get a ssl socket host name, cause known.
                                     
2008-11-05
PUBLIC COMMENTS

Xuelei Fan wrote:

> Cause known, the application enable client identity checking during 
> extending X509ExtendedTrustManager with a fixed identification 
> algorithm, "https", and the bug fix of 6614957 changes the behaviors of 
> how to get a ssl socket host name.

Yes, the fix for 6614957 has clearly caused a regression.

> Yes, we need to find a general way for client identity. The HTTPS spec 
> said, "Typically, the server has no external knowledge of what the 
> client's identity ought to be and so checks (other than that the client 
> has a certificate chain rooted in an appropriate CA) are not possible. 
> If a server has such knowledge (typically from some source external to 
> HTTP or TLS) it SHOULD check the identity as described above<server 
> identity>." I think maybe the DNS could be considered as an external 
> source to TLS.

Yes, it may not be commonly-used, but it *is* supposed to work, and the spec says that if such a mechanism exists, it should be used.  That's the case here.

> I don't think I have enough time to fix it into 6u12 b01, I'd like have 
> a try.

Do you have a pointer to the build and release schedules?

> Because 6u10 is a major upgrade, I think you maybe need a workaround, I 
> would suggest always convert the hostname into a resolved hostname 
> before call the default checkClientTrusted in the customized trust manager,
> _hostname_ = InetAddress.getByName(hostname).getHostName();
> Or
> _hostname_ = InetAddress.getByName(hostname).getCanonicalHostName();

That should work, I'll check it out.

However I'm concerned by your assertion that the move from 1.6.0_07 to 1.6.0_10 "is a major upgrade".  According to http://java.sun.com/j2se/versioning_naming.html, the switch from _07 to _10 designates an "update release" which "contains customer focused bug fixes".  As far as I'm aware, that means that we shouldn't be introducing any major changes in existing functionality in such a release - something that has clearly happened in this case.  This bug is also directly affecting the development of opensolaris.org.
                                     
2008-11-05
WORK AROUND

As a workaround, I would suggest always convert the hostname into a resolved hostname before call the default checkClientTrusted in the customized trust manager,
_hostname_ = InetAddress.getByName(hostname).getHostName();
Or
_hostname_ = InetAddress.getByName(hostname).getCanonicalHostName();
                                     
2008-12-09
SUGGESTED FIX

The behaviors change back for better backwards compatibilities.
                                     
2008-12-12



Hardware and Software, Engineered to Work Together