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 @SuppressWarnings("deprecation") // HttpsURLConnection is deprecated
  57 public class DelegateHttpsURLConnection extends AbstractDelegateHttpsURLConnection {
  58 
  59     // we need a reference to the HttpsURLConnection to get
  60     // the properties set there
  61     // we also need it to be public so that it can be referenced
  62     // from sun.net.www.protocol.http.HttpURLConnection
  63     // this is for ResponseCache.put(URI, URLConnection)
  64     // second parameter needs to be cast to javax.net.ssl.HttpsURLConnection
  65     // instead of AbstractDelegateHttpsURLConnection
  66     
  67     public com.sun.net.ssl.HttpsURLConnection httpsURLConnection;
  68 
  69     DelegateHttpsURLConnection(URL url,
  70             sun.net.www.protocol.http.Handler handler,
  71             com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
  72             throws IOException {
  73         this(url, null, handler, httpsURLConnection);
  74     }
  75 
  76     DelegateHttpsURLConnection(URL url, Proxy p,
  77             sun.net.www.protocol.http.Handler handler,
  78             com.sun.net.ssl.HttpsURLConnection httpsURLConnection)
  79             throws IOException {
  80         super(url, p, handler);
  81         this.httpsURLConnection = httpsURLConnection;
  82     }
  83 
  84     protected javax.net.ssl.SSLSocketFactory getSSLSocketFactory() {
  85         return httpsURLConnection.getSSLSocketFactory();
  86     }
  87 
  88     protected javax.net.ssl.HostnameVerifier getHostnameVerifier() {
  89         // note: getHostnameVerifier() never returns null
  90         return new VerifierWrapper(httpsURLConnection.getHostnameVerifier());
  91     }
  92 
  93     /*
  94      * Called by layered delegator's finalize() method to handle closing
  95      * the underlying object.
  96      */
  97     protected void dispose() throws Throwable {
  98         super.finalize();
  99     }
 100 }
 101 
 102 class VerifierWrapper implements javax.net.ssl.HostnameVerifier {
 103     @SuppressWarnings("deprecation")
 104     private com.sun.net.ssl.HostnameVerifier verifier;
 105 
 106     @SuppressWarnings("deprecation")
 107     VerifierWrapper(com.sun.net.ssl.HostnameVerifier verifier) {
 108         this.verifier = verifier;
 109     }
 110 
 111     /*
 112      * In com.sun.net.ssl.HostnameVerifier the method is defined
 113      * as verify(String urlHostname, String certHostname).
 114      * This means we need to extract the hostname from the X.509 certificate
 115      * or from the Kerberos principal name, in this wrapper.
 116      */
 117     public boolean verify(String hostname, javax.net.ssl.SSLSession session) {
 118         try {
 119             String serverName;
 120             // Use ciphersuite to determine whether Kerberos is active.
 121             if (session.getCipherSuite().startsWith("TLS_KRB5")) {
 122                 serverName =
 123                     HostnameChecker.getServerName(getPeerPrincipal(session));
 124 
 125             } else { // X.509
 126                 Certificate[] serverChain = session.getPeerCertificates();
 127                 if ((serverChain == null) || (serverChain.length == 0)) {
 128                     return false;
 129                 }
 130                 if (serverChain[0] instanceof X509Certificate == false) {
 131                     return false;
 132                 }
 133                 X509Certificate serverCert = (X509Certificate)serverChain[0];
 134                 serverName = getServername(serverCert);
 135             }
 136             if (serverName == null) {
 137                 return false;
 138             }
 139             return verifier.verify(hostname, serverName);
 140         } catch (javax.net.ssl.SSLPeerUnverifiedException e) {
 141             return false;
 142         }
 143     }
 144 
 145     /*
 146      * Get the peer principal from the session
 147      */
 148     private Principal getPeerPrincipal(javax.net.ssl.SSLSession session)
 149         throws javax.net.ssl.SSLPeerUnverifiedException
 150     {
 151         Principal principal;
 152         try {
 153             principal = session.getPeerPrincipal();
 154         } catch (AbstractMethodError e) {
 155             // if the provider does not support it, return null, since
 156             // we need it only for Kerberos.
 157             principal = null;
 158         }
 159         return principal;
 160     }
 161 
 162     /*
 163      * Extract the name of the SSL server from the certificate.
 164      *
 165      * Note this code is essentially a subset of the hostname extraction
 166      * code in HostnameChecker.
 167      */
 168     private static String getServername(X509Certificate peerCert) {
 169         try {
 170             // compare to subjectAltNames if dnsName is present
 171             Collection<List<?>> subjAltNames = peerCert.getSubjectAlternativeNames();
 172             if (subjAltNames != null) {
 173                 for (Iterator<List<?>> itr = subjAltNames.iterator(); itr.hasNext(); ) {
 174                     List<?> next = itr.next();
 175                     if (((Integer)next.get(0)).intValue() == 2) {
 176                         // compare dNSName with host in url
 177                         String dnsName = ((String)next.get(1));
 178                         return dnsName;
 179                     }
 180                 }
 181             }
 182 
 183             // else check against common name in the subject field
 184             X500Name subject = HostnameChecker.getSubjectX500Name(peerCert);
 185 
 186             DerValue derValue = subject.findMostSpecificAttribute
 187                                                 (X500Name.commonName_oid);
 188             if (derValue != null) {
 189                 try {
 190                     String name = derValue.getAsString();
 191                     return name;
 192                 } catch (IOException e) {
 193                     // ignore
 194                 }
 195             }
 196         } catch (java.security.cert.CertificateException e) {
 197             // ignore
 198         }
 199         return null;
 200     }
 201 
 202 }