1 /*
2  * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.net.ssl.internal.www.protocol.https;
27 
28 import java.net.URL;
29 import java.net.Proxy;
30 import java.io.IOException;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Iterator;
34 
35 import java.security.Principal;
36 import java.security.cert.*;
37 
38 import javax.security.auth.x500.X500Principal;
39 
40 import sun.security.util.HostnameChecker;
41 import sun.security.util.DerValue;
42 import sun.security.x509.X500Name;
43 
44 import sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection;
45 
46 /**
47  * This class was introduced to provide an additional level of
48  * abstraction between javax.net.ssl.HttpURLConnection and
49  * com.sun.net.ssl.HttpURLConnection objects. <p>
50  *
51  * javax.net.ssl.HttpURLConnection is used in the new sun.net version
52  * of protocol implementation (this one)
53  * com.sun.net.ssl.HttpURLConnection is used in the com.sun version.
54  *
55  */
56 public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection {
57 
58     // we need a reference to the HttpsURLConnection to get
59     // the properties set there
60     // we also need it to be public so that it can be referenced
61     // from sun.net.www.protocol.http.HttpURLConnection
62     // this is for ResponseCache.put(URI, URLConnection)
63     // second parameter needs to be cast to javax.net.ssl.HttpsURLConnection
64     // instead of AbstractDelegateHttpsURLConnection
65     public com.sun.net.ssl.HttpsURLConnection httpsURLConnection;
66 
DelegateHttpsURLConnection(URL url, sun.net.www.protocol.http.Handler handler, com.sun.net.ssl.HttpsURLConnection httpsURLConnection)67     DelegateHttpsURLConnection(URL url,
68             sun.net.www.protocol.http.Handler handler,
69             com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
70             throws IOException {
71         this(url, null, handler, httpsURLConnection);
72     }
73 
DelegateHttpsURLConnection(URL url, Proxy p, sun.net.www.protocol.http.Handler handler, com.sun.net.ssl.HttpsURLConnection httpsURLConnection)74     DelegateHttpsURLConnection(URL url, Proxy p,
75             sun.net.www.protocol.http.Handler handler,
76             com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
77             throws IOException {
78         super(url, p, handler);
79         this.httpsURLConnection = httpsURLConnection;
80     }
81 
getSSLSocketFactory()82     protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() {
83         return httpsURLConnection.getSSLSocketFactory();
84     }
85 
getHostnameVerifier()86     protected javax.net.ssl.HostnameVerifier getHostnameVerifier() {
87         // note: getHostnameVerifier() never returns null
88         return new VerifierWrapper(httpsURLConnection.getHostnameVerifier());
89     }
90 
91     /*
92      * Called by layered delegator's finalize() method to handle closing
93      * the underlying object.
94      */
dispose()95     protected void dispose() throws Throwable {
96         super.finalize();
97     }
98 }
99 
100 class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
101 
102     private com.sun.net.ssl.HostnameVerifier verifier;
103 
VerifierWrapper(com.sun.net.ssl.HostnameVerifier verifier)104     VerifierWrapper(com.sun.net.ssl.HostnameVerifier verifier) {
105         this.verifier = verifier;
106     }
107 
108     /*
109      * In com.sun.net.ssl.HostnameVerifier the method is defined
110      * as verify(String urlHostname, String certHostname).
111      * This means we need to extract the hostname from the X.509 certificate
112      * or from the Kerberos principal name, in this wrapper.
113      */
verify(String hostname, javax.net.ssl.SSLSession session)114     public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
115         try {
116             String serverName;
117             // Use ciphersuite to determine whether Kerberos is active.
118             if (session.getCipherSuite().startsWith("TLS_KRB5")) {
119                 serverName =
120                     HostnameChecker.getServerName(getPeerPrincipal(session));
121 
122             } else { // X.509
123                 Certificate[] serverChain = session.getPeerCertificates();
124                 if ((serverChain == null) || (serverChain.length == 0)) {
125                     return false;
126                 }
127                 if (serverChain[0] instanceof X509Certificate == false) {
128                     return false;
129                 }
130                 X509Certificate serverCert = (X509Certificate)serverChain[0];
131                 serverName = getServername(serverCert);
132             }
133             if (serverName == null) {
134                 return false;
135             }
136             return verifier.verify(hostname, serverName);
137         } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
138             return false;
139         }
140     }
141 
142     /*
143      * Get the peer principal from the session
144      */
getPeerPrincipal(javax.net.ssl.SSLSession session)145     private Principal getPeerPrincipal(javax.net.ssl.SSLSession session)
146         throws javax.net.ssl.SSLPeerUnverifiedException
147     {
148         Principal principal;
149         try {
150             principal = session.getPeerPrincipal();
151         } catch (AbstractMethodError e) {
152             // if the provider does not support it, return null, since
153             // we need it only for Kerberos.
154             principal = null;
155         }
156         return principal;
157     }
158 
159     /*
160      * Extract the name of the SSL server from the certificate.
161      *
162      * Note this code is essentially a subset of the hostname extraction
163      * code in HostnameChecker.
164      */
getServername(X509Certificate peerCert)165     private static String getServername(X509Certificate peerCert) {
166         try {
167             // compare to subjectAltNames if dnsName is present
168             Collection<List<?>> subjAltNames = peerCert.getSubjectAlternativeNames();
169             if (subjAltNames != null) {
170                 for (Iterator<List<?>> itr = subjAltNames.iterator(); itr.hasNext(); ) {
171                     List<?> next = itr.next();
172                     if (((Integer)next.get(0)).intValue() == 2) {
173                         // compare dNSName with host in url
174                         String dnsName = ((String)next.get(1));
175                         return dnsName;
176                     }
177                 }
178             }
179 
180             // else check against common name in the subject field
181             X500Name subject = HostnameChecker.getSubjectX500Name(peerCert);
182 
183             DerValue derValue = subject.findMostSpecificAttribute
184                                                 (X500Name.commonName_oid);
185             if (derValue != null) {
186                 try {
187                     String name = derValue.getAsString();
188                     return name;
189                 } catch (IOException e) {
190                     // ignore
191                 }
192             }
193         } catch (java.security.cert.CertificateException e) {
194             // ignore
195         }
196         return null;
197     }
198 
199 }
200