1 /*
   2  * Copyright (c) 2001, 2015, 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 
  27 package sun.net.www.protocol.https;
  28 
  29 import java.io.IOException;
  30 import java.io.UnsupportedEncodingException;
  31 import java.io.PrintStream;
  32 import java.io.BufferedOutputStream;
  33 import java.net.InetAddress;
  34 import java.net.Socket;
  35 import java.net.SocketException;
  36 import java.net.URL;
  37 import java.net.UnknownHostException;
  38 import java.net.InetSocketAddress;
  39 import java.net.Proxy;
  40 import java.security.Principal;
  41 import java.security.cert.*;
  42 import java.util.StringTokenizer;
  43 import java.util.Vector;
  44 
  45 import javax.security.auth.x500.X500Principal;
  46 
  47 import javax.net.ssl.*;
  48 import sun.net.www.http.HttpClient;
  49 import sun.net.www.protocol.http.HttpURLConnection;
  50 import sun.security.action.*;
  51 
  52 import sun.security.util.HostnameChecker;
  53 import sun.security.ssl.SSLSocketImpl;
  54 
  55 import sun.util.logging.PlatformLogger;
  56 import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
  57 
  58 
  59 /**
  60  * This class provides HTTPS client URL support, building on the standard
  61  * "sun.net.www" HTTP protocol handler.  HTTPS is the same protocol as HTTP,
  62  * but differs in the transport layer which it uses:  <UL>
  63  *
  64  *      <LI>There's a <em>Secure Sockets Layer</em> between TCP
  65  *      and the HTTP protocol code.
  66  *
  67  *      <LI>It uses a different default TCP port.
  68  *
  69  *      <LI>It doesn't use application level proxies, which can see and
  70  *      manipulate HTTP user level data, compromising privacy.  It uses
  71  *      low level tunneling instead, which hides HTTP protocol and data
  72  *      from all third parties.  (Traffic analysis is still possible).
  73  *
  74  *      <LI>It does basic server authentication, to protect
  75  *      against "URL spoofing" attacks.  This involves deciding
  76  *      whether the X.509 certificate chain identifying the server
  77  *      is trusted, and verifying that the name of the server is
  78  *      found in the certificate.  (The application may enable an
  79  *      anonymous SSL cipher suite, and such checks are not done
  80  *      for anonymous ciphers.)
  81  *
  82  *      <LI>It exposes key SSL session attributes, specifically the
  83  *      cipher suite in use and the server's X509 certificates, to
  84  *      application software which knows about this protocol handler.
  85  *
  86  *      </UL>
  87  *
  88  * <P> System properties used include:  <UL>
  89  *
  90  *      <LI><em>https.proxyHost</em> ... the host supporting SSL
  91  *      tunneling using the conventional CONNECT syntax
  92  *
  93  *      <LI><em>https.proxyPort</em> ... port to use on proxyHost
  94  *
  95  *      <LI><em>https.cipherSuites</em> ... comma separated list of
  96  *      SSL cipher suite names to enable.
  97  *
  98  *      <LI><em>http.nonProxyHosts</em> ...
  99  *
 100  *      </UL>
 101  *
 102  * @author David Brownell
 103  * @author Bill Foote
 104  */
 105 
 106 // final for export control reasons (access to APIs); remove with care
 107 final class HttpsClient extends HttpClient
 108     implements HandshakeCompletedListener
 109 {
 110     // STATIC STATE and ACCESSORS THERETO
 111 
 112     // HTTPS uses a different default port number than HTTP.
 113     private static final int    httpsPortNumber = 443;
 114 
 115     // default HostnameVerifier class canonical name
 116     private static final String defaultHVCanonicalName =
 117             "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";
 118 
 119     /** Returns the default HTTPS port (443) */
 120     @Override
 121     protected int getDefaultPort() { return httpsPortNumber; }
 122 
 123     private HostnameVerifier hv;
 124     private SSLSocketFactory sslSocketFactory;
 125 
 126     // HttpClient.proxyDisabled will always be false, because we don't
 127     // use an application-level HTTP proxy.  We might tunnel through
 128     // our http proxy, though.
 129 
 130 
 131     // INSTANCE DATA
 132 
 133     // last negotiated SSL session
 134     private SSLSession  session;
 135 
 136     private String [] getCipherSuites() {
 137         //
 138         // If ciphers are assigned, sort them into an array.
 139         //
 140         String ciphers [];
 141         String cipherString =
 142                 GetPropertyAction.getProperty("https.cipherSuites");
 143 
 144         if (cipherString == null || "".equals(cipherString)) {
 145             ciphers = null;
 146         } else {
 147             StringTokenizer     tokenizer;
 148             Vector<String>      v = new Vector<String>();
 149 
 150             tokenizer = new StringTokenizer(cipherString, ",");
 151             while (tokenizer.hasMoreTokens())
 152                 v.addElement(tokenizer.nextToken());
 153             ciphers = new String [v.size()];
 154             for (int i = 0; i < ciphers.length; i++)
 155                 ciphers [i] = v.elementAt(i);
 156         }
 157         return ciphers;
 158     }
 159 
 160     private String [] getProtocols() {
 161         //
 162         // If protocols are assigned, sort them into an array.
 163         //
 164         String protocols [];
 165         String protocolString =
 166                 GetPropertyAction.getProperty("https.protocols");
 167 
 168         if (protocolString == null || "".equals(protocolString)) {
 169             protocols = null;
 170         } else {
 171             StringTokenizer     tokenizer;
 172             Vector<String>      v = new Vector<String>();
 173 
 174             tokenizer = new StringTokenizer(protocolString, ",");
 175             while (tokenizer.hasMoreTokens())
 176                 v.addElement(tokenizer.nextToken());
 177             protocols = new String [v.size()];
 178             for (int i = 0; i < protocols.length; i++) {
 179                 protocols [i] = v.elementAt(i);
 180             }
 181         }
 182         return protocols;
 183     }
 184 
 185     private String getUserAgent() {
 186         String userAgent = GetPropertyAction.getProperty("https.agent");
 187         if (userAgent == null || userAgent.length() == 0) {
 188             userAgent = "JSSE";
 189         }
 190         return userAgent;
 191     }
 192 
 193     // CONSTRUCTOR, FACTORY
 194 
 195 
 196     /**
 197      * Create an HTTPS client URL.  Traffic will be tunneled through any
 198      * intermediate nodes rather than proxied, so that confidentiality
 199      * of data exchanged can be preserved.  However, note that all the
 200      * anonymous SSL flavors are subject to "person-in-the-middle"
 201      * attacks against confidentiality.  If you enable use of those
 202      * flavors, you may be giving up the protection you get through
 203      * SSL tunneling.
 204      *
 205      * Use New to get new HttpsClient. This constructor is meant to be
 206      * used only by New method. New properly checks for URL spoofing.
 207      *
 208      * @param URL https URL with which a connection must be established
 209      */
 210     private HttpsClient(SSLSocketFactory sf, URL url)
 211     throws IOException
 212     {
 213         // HttpClient-level proxying is always disabled,
 214         // because we override doConnect to do tunneling instead.
 215         this(sf, url, (String)null, -1);
 216     }
 217 
 218     /**
 219      *  Create an HTTPS client URL.  Traffic will be tunneled through
 220      * the specified proxy server.
 221      */
 222     HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)
 223         throws IOException {
 224         this(sf, url, proxyHost, proxyPort, -1);
 225     }
 226 
 227     /**
 228      *  Create an HTTPS client URL.  Traffic will be tunneled through
 229      * the specified proxy server, with a connect timeout
 230      */
 231     HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,
 232                 int connectTimeout)
 233         throws IOException {
 234         this(sf, url,
 235              (proxyHost == null? null:
 236                 HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),
 237                 connectTimeout);
 238     }
 239 
 240     /**
 241      *  Same as previous constructor except using a Proxy
 242      */
 243     HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,
 244                 int connectTimeout)
 245         throws IOException {
 246         PlatformLogger logger = HttpURLConnection.getHttpLogger();
 247         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 248              logger.finest("Creating new HttpsClient with url:" + url + " and proxy:" + proxy +
 249              " with connect timeout:" + connectTimeout);
 250         }
 251         this.proxy = proxy;
 252         setSSLSocketFactory(sf);
 253         this.proxyDisabled = true;
 254 
 255         this.host = url.getHost();
 256         this.url = url;
 257         port = url.getPort();
 258         if (port == -1) {
 259             port = getDefaultPort();
 260         }
 261         setConnectTimeout(connectTimeout);
 262         openServer();
 263     }
 264 
 265 
 266     // This code largely ripped off from HttpClient.New, and
 267     // it uses the same keepalive cache.
 268 
 269     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 270                           HttpURLConnection httpuc)
 271             throws IOException {
 272         return HttpsClient.New(sf, url, hv, true, httpuc);
 273     }
 274 
 275     /** See HttpClient for the model for this method. */
 276     static HttpClient New(SSLSocketFactory sf, URL url,
 277             HostnameVerifier hv, boolean useCache,
 278             HttpURLConnection httpuc) throws IOException {
 279         return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);
 280     }
 281 
 282     /**
 283      * Get a HTTPS client to the URL.  Traffic will be tunneled through
 284      * the specified proxy server.
 285      */
 286     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 287                            String proxyHost, int proxyPort,
 288                            HttpURLConnection httpuc) throws IOException {
 289         return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);
 290     }
 291 
 292     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 293                            String proxyHost, int proxyPort, boolean useCache,
 294                            HttpURLConnection httpuc)
 295         throws IOException {
 296         return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,
 297                                httpuc);
 298     }
 299 
 300     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 301                           String proxyHost, int proxyPort, boolean useCache,
 302                           int connectTimeout, HttpURLConnection httpuc)
 303         throws IOException {
 304 
 305         return HttpsClient.New(sf, url, hv,
 306                                (proxyHost == null? null :
 307                                 HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),
 308                                useCache, connectTimeout, httpuc);
 309     }
 310 
 311     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 312                           Proxy p, boolean useCache,
 313                           int connectTimeout, HttpURLConnection httpuc)
 314         throws IOException
 315     {
 316         if (p == null) {
 317             p = Proxy.NO_PROXY;
 318         }
 319         PlatformLogger logger = HttpURLConnection.getHttpLogger();
 320         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 321             logger.finest("Looking for HttpClient for URL " + url +
 322                 " and proxy value of " + p);
 323         }
 324         HttpsClient ret = null;
 325         if (useCache) {
 326             /* see if one's already around */
 327             ret = (HttpsClient) kac.get(url, sf);
 328             if (ret != null && httpuc != null &&
 329                 httpuc.streaming() &&
 330                 httpuc.getRequestMethod() == "POST") {
 331                 if (!ret.available())
 332                     ret = null;
 333             }
 334 
 335             if (ret != null) {
 336                 if ((ret.proxy != null && ret.proxy.equals(p)) ||
 337                     (ret.proxy == null && p == Proxy.NO_PROXY)) {
 338                     synchronized (ret) {
 339                         ret.cachedHttpClient = true;
 340                         assert ret.inCache;
 341                         ret.inCache = false;
 342                         if (httpuc != null && ret.needsTunneling())
 343                             httpuc.setTunnelState(TUNNELING);
 344                         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 345                             logger.finest("KeepAlive stream retrieved from the cache, " + ret);
 346                         }
 347                     }
 348                 } else {
 349                     // We cannot return this connection to the cache as it's
 350                     // KeepAliveTimeout will get reset. We simply close the connection.
 351                     // This should be fine as it is very rare that a connection
 352                     // to the same host will not use the same proxy.
 353                     synchronized(ret) {
 354                         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 355                             logger.finest("Not returning this connection to cache: " + ret);
 356                         }
 357                         ret.inCache = false;
 358                         ret.closeServer();
 359                     }
 360                     ret = null;
 361                 }
 362             }
 363         }
 364         if (ret == null) {
 365             ret = new HttpsClient(sf, url, p, connectTimeout);
 366         } else {
 367             SecurityManager security = System.getSecurityManager();
 368             if (security != null) {
 369                 if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
 370                     security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
 371                 } else {
 372                     security.checkConnect(url.getHost(), url.getPort());
 373                 }
 374             }
 375             ret.url = url;
 376         }
 377         ret.setHostnameVerifier(hv);
 378 
 379         return ret;
 380     }
 381 
 382     // METHODS
 383     void setHostnameVerifier(HostnameVerifier hv) {
 384         this.hv = hv;
 385     }
 386 
 387     void setSSLSocketFactory(SSLSocketFactory sf) {
 388         sslSocketFactory = sf;
 389     }
 390 
 391     SSLSocketFactory getSSLSocketFactory() {
 392         return sslSocketFactory;
 393     }
 394 
 395     /**
 396      * The following method, createSocket, is defined in NetworkClient
 397      * and overridden here so that the socket facroty is used to create
 398      * new sockets.
 399      */
 400     @Override
 401     protected Socket createSocket() throws IOException {
 402         try {
 403             return sslSocketFactory.createSocket();
 404         } catch (SocketException se) {
 405             //
 406             // bug 6771432
 407             // javax.net.SocketFactory throws a SocketException with an
 408             // UnsupportedOperationException as its cause to indicate that
 409             // unconnected sockets have not been implemented.
 410             //
 411             Throwable t = se.getCause();
 412             if (t != null && t instanceof UnsupportedOperationException) {
 413                 return super.createSocket();
 414             } else {
 415                 throw se;
 416             }
 417         }
 418     }
 419 
 420 
 421     @Override
 422     public boolean needsTunneling() {
 423         return (proxy != null && proxy.type() != Proxy.Type.DIRECT
 424                 && proxy.type() != Proxy.Type.SOCKS);
 425     }
 426 
 427     @Override
 428     public void afterConnect() throws IOException, UnknownHostException {
 429         if (!isCachedConnection()) {
 430             SSLSocket s = null;
 431             SSLSocketFactory factory = sslSocketFactory;
 432             try {
 433                 if (!(serverSocket instanceof SSLSocket)) {
 434                     s = (SSLSocket)factory.createSocket(serverSocket,
 435                                                         host, port, true);
 436                 } else {
 437                     s = (SSLSocket)serverSocket;
 438                     if (s instanceof SSLSocketImpl) {
 439                         ((SSLSocketImpl)s).setHost(host);
 440                     }
 441                 }
 442             } catch (IOException ex) {
 443                 // If we fail to connect through the tunnel, try it
 444                 // locally, as a last resort.  If this doesn't work,
 445                 // throw the original exception.
 446                 try {
 447                     s = (SSLSocket)factory.createSocket(host, port);
 448                 } catch (IOException ignored) {
 449                     throw ex;
 450                 }
 451             }
 452 
 453             //
 454             // Force handshaking, so that we get any authentication.
 455             // Register a handshake callback so our session state tracks any
 456             // later session renegotiations.
 457             //
 458             String [] protocols = getProtocols();
 459             String [] ciphers = getCipherSuites();
 460             if (protocols != null) {
 461                 s.setEnabledProtocols(protocols);
 462             }
 463             if (ciphers != null) {
 464                 s.setEnabledCipherSuites(ciphers);
 465             }
 466             s.addHandshakeCompletedListener(this);
 467 
 468             // We have two hostname verification approaches. One is in
 469             // SSL/TLS socket layer, where the algorithm is configured with
 470             // SSLParameters.setEndpointIdentificationAlgorithm(), and the
 471             // hostname verification is done by X509ExtendedTrustManager when
 472             // the algorithm is "HTTPS". The other one is in HTTPS layer,
 473             // where the algorithm is customized by
 474             // HttpsURLConnection.setHostnameVerifier(), and the hostname
 475             // verification is done by HostnameVerifier when the default
 476             // rules for hostname verification fail.
 477             //
 478             // The relationship between two hostname verification approaches
 479             // likes the following:
 480             //
 481             //               |             EIA algorithm
 482             //               +----------------------------------------------
 483             //               |     null      |   HTTPS    |   LDAP/other   |
 484             // -------------------------------------------------------------
 485             //     |         |1              |2           |3               |
 486             // HNV | default | Set HTTPS EIA | use EIA    | HTTPS          |
 487             //     |--------------------------------------------------------
 488             //     | non -   |4              |5           |6               |
 489             //     | default | HTTPS/HNV     | use EIA    | HTTPS/HNV      |
 490             // -------------------------------------------------------------
 491             //
 492             // Abbreviation:
 493             //     EIA: the endpoint identification algorithm in SSL/TLS
 494             //           socket layer
 495             //     HNV: the hostname verification object in HTTPS layer
 496             // Notes:
 497             //     case 1. default HNV and EIA is null
 498             //           Set EIA as HTTPS, hostname check done in SSL/TLS
 499             //           layer.
 500             //     case 2. default HNV and EIA is HTTPS
 501             //           Use existing EIA, hostname check done in SSL/TLS
 502             //           layer.
 503             //     case 3. default HNV and EIA is other than HTTPS
 504             //           Use existing EIA, EIA check done in SSL/TLS
 505             //           layer, then do HTTPS check in HTTPS layer.
 506             //     case 4. non-default HNV and EIA is null
 507             //           No EIA, no EIA check done in SSL/TLS layer, then do
 508             //           HTTPS check in HTTPS layer using HNV as override.
 509             //     case 5. non-default HNV and EIA is HTTPS
 510             //           Use existing EIA, hostname check done in SSL/TLS
 511             //           layer. No HNV override possible. We will review this
 512             //           decision and may update the architecture for JDK 7.
 513             //     case 6. non-default HNV and EIA is other than HTTPS
 514             //           Use existing EIA, EIA check done in SSL/TLS layer,
 515             //           then do HTTPS check in HTTPS layer as override.
 516             boolean needToCheckSpoofing = true;
 517             String identification =
 518                 s.getSSLParameters().getEndpointIdentificationAlgorithm();
 519             if (identification != null && identification.length() != 0) {
 520                 if (identification.equalsIgnoreCase("HTTPS")) {
 521                     // Do not check server identity again out of SSLSocket,
 522                     // the endpoint will be identified during TLS handshaking
 523                     // in SSLSocket.
 524                     needToCheckSpoofing = false;
 525                 }   // else, we don't understand the identification algorithm,
 526                     // need to check URL spoofing here.
 527             } else {
 528                 boolean isDefaultHostnameVerifier = false;
 529 
 530                 // We prefer to let the SSLSocket do the spoof checks, but if
 531                 // the application has specified a HostnameVerifier (HNV),
 532                 // we will always use that.
 533                 if (hv != null) {
 534                     String canonicalName = hv.getClass().getCanonicalName();
 535                     if (canonicalName != null &&
 536                     canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) {
 537                         isDefaultHostnameVerifier = true;
 538                     }
 539                 } else {
 540                     // Unlikely to happen! As the behavior is the same as the
 541                     // default hostname verifier, so we prefer to let the
 542                     // SSLSocket do the spoof checks.
 543                     isDefaultHostnameVerifier = true;
 544                 }
 545 
 546                 if (isDefaultHostnameVerifier) {
 547                     // If the HNV is the default from HttpsURLConnection, we
 548                     // will do the spoof checks in SSLSocket.
 549                     SSLParameters paramaters = s.getSSLParameters();
 550                     paramaters.setEndpointIdentificationAlgorithm("HTTPS");
 551                     s.setSSLParameters(paramaters);
 552 
 553                     needToCheckSpoofing = false;
 554                 }
 555             }
 556 
 557             s.startHandshake();
 558             session = s.getSession();
 559             // change the serverSocket and serverOutput
 560             serverSocket = s;
 561             try {
 562                 serverOutput = new PrintStream(
 563                     new BufferedOutputStream(serverSocket.getOutputStream()),
 564                     false, encoding);
 565             } catch (UnsupportedEncodingException e) {
 566                 throw new InternalError(encoding+" encoding not found");
 567             }
 568 
 569             // check URL spoofing if it has not been checked under handshaking
 570             if (needToCheckSpoofing) {
 571                 checkURLSpoofing(hv);
 572             }
 573         } else {
 574             // if we are reusing a cached https session,
 575             // we don't need to do handshaking etc. But we do need to
 576             // set the ssl session
 577             session = ((SSLSocket)serverSocket).getSession();
 578         }
 579     }
 580 
 581     // Server identity checking is done according to RFC 2818: HTTP over TLS
 582     // Section 3.1 Server Identity
 583     private void checkURLSpoofing(HostnameVerifier hostnameVerifier)
 584             throws IOException {
 585         //
 586         // Get authenticated server name, if any
 587         //
 588         String host = url.getHost();
 589 
 590         // if IPv6 strip off the "[]"
 591         if (host != null && host.startsWith("[") && host.endsWith("]")) {
 592             host = host.substring(1, host.length()-1);
 593         }
 594 
 595         Certificate[] peerCerts = null;
 596         String cipher = session.getCipherSuite();
 597         try {
 598             HostnameChecker checker = HostnameChecker.getInstance(
 599                                                 HostnameChecker.TYPE_TLS);
 600 
 601             // Use ciphersuite to determine whether Kerberos is present.
 602             if (cipher.startsWith("TLS_KRB5")) {
 603                 if (!HostnameChecker.match(host, getPeerPrincipal())) {
 604                     throw new SSLPeerUnverifiedException("Hostname checker" +
 605                                 " failed for Kerberos");
 606                 }
 607             } else { // X.509
 608 
 609                 // get the subject's certificate
 610                 peerCerts = session.getPeerCertificates();
 611 
 612                 X509Certificate peerCert;
 613                 if (peerCerts[0] instanceof
 614                         java.security.cert.X509Certificate) {
 615                     peerCert = (java.security.cert.X509Certificate)peerCerts[0];
 616                 } else {
 617                     throw new SSLPeerUnverifiedException("");
 618                 }
 619                 checker.match(host, peerCert);
 620             }
 621 
 622             // if it doesn't throw an exception, we passed. Return.
 623             return;
 624 
 625         } catch (SSLPeerUnverifiedException e) {
 626 
 627             //
 628             // client explicitly changed default policy and enabled
 629             // anonymous ciphers; we can't check the standard policy
 630             //
 631             // ignore
 632         } catch (java.security.cert.CertificateException cpe) {
 633             // ignore
 634         }
 635 
 636         if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
 637             return;
 638         } else if ((hostnameVerifier != null) &&
 639                    (hostnameVerifier.verify(host, session))) {
 640             return;
 641         }
 642 
 643         serverSocket.close();
 644         session.invalidate();
 645 
 646         throw new IOException("HTTPS hostname wrong:  should be <"
 647                               + url.getHost() + ">");
 648     }
 649 
 650     @Override
 651     protected void putInKeepAliveCache() {
 652         if (inCache) {
 653             assert false : "Duplicate put to keep alive cache";
 654             return;
 655         }
 656         inCache = true;
 657         kac.put(url, sslSocketFactory, this);
 658     }
 659 
 660     /*
 661      * Close an idle connection to this URL (if it exists in the cache).
 662      */
 663     @Override
 664     public void closeIdleConnection() {
 665         HttpClient http = kac.get(url, sslSocketFactory);
 666         if (http != null) {
 667             http.closeServer();
 668         }
 669     }
 670 
 671     /**
 672      * Returns the cipher suite in use on this connection.
 673      */
 674     String getCipherSuite() {
 675         return session.getCipherSuite();
 676     }
 677 
 678     /**
 679      * Returns the certificate chain the client sent to the
 680      * server, or null if the client did not authenticate.
 681      */
 682     public java.security.cert.Certificate [] getLocalCertificates() {
 683         return session.getLocalCertificates();
 684     }
 685 
 686     /**
 687      * Returns the certificate chain with which the server
 688      * authenticated itself, or throw a SSLPeerUnverifiedException
 689      * if the server did not authenticate.
 690      */
 691     java.security.cert.Certificate [] getServerCertificates()
 692             throws SSLPeerUnverifiedException
 693     {
 694         return session.getPeerCertificates();
 695     }
 696 
 697     /**
 698      * Returns the principal with which the server authenticated
 699      * itself, or throw a SSLPeerUnverifiedException if the
 700      * server did not authenticate.
 701      */
 702     Principal getPeerPrincipal()
 703             throws SSLPeerUnverifiedException
 704     {
 705         Principal principal;
 706         try {
 707             principal = session.getPeerPrincipal();
 708         } catch (AbstractMethodError e) {
 709             // if the provider does not support it, fallback to peer certs.
 710             // return the X500Principal of the end-entity cert.
 711             java.security.cert.Certificate[] certs =
 712                         session.getPeerCertificates();
 713             principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
 714         }
 715         return principal;
 716     }
 717 
 718     /**
 719      * Returns the principal the client sent to the
 720      * server, or null if the client did not authenticate.
 721      */
 722     Principal getLocalPrincipal()
 723     {
 724         Principal principal;
 725         try {
 726             principal = session.getLocalPrincipal();
 727         } catch (AbstractMethodError e) {
 728             principal = null;
 729             // if the provider does not support it, fallback to local certs.
 730             // return the X500Principal of the end-entity cert.
 731             java.security.cert.Certificate[] certs =
 732                         session.getLocalCertificates();
 733             if (certs != null) {
 734                 principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
 735             }
 736         }
 737         return principal;
 738     }
 739 
 740     /**
 741      * This method implements the SSL HandshakeCompleted callback,
 742      * remembering the resulting session so that it may be queried
 743      * for the current cipher suite and peer certificates.  Servers
 744      * sometimes re-initiate handshaking, so the session in use on
 745      * a given connection may change.  When sessions change, so may
 746      * peer identities and cipher suites.
 747      */
 748     public void handshakeCompleted(HandshakeCompletedEvent event)
 749     {
 750         session = event.getSession();
 751     }
 752 
 753     /**
 754      * @return the proxy host being used for this client, or null
 755      *          if we're not going through a proxy
 756      */
 757     @Override
 758     public String getProxyHostUsed() {
 759         if (!needsTunneling()) {
 760             return null;
 761         } else {
 762             return super.getProxyHostUsed();
 763         }
 764     }
 765 
 766     /**
 767      * @return the proxy port being used for this client.  Meaningless
 768      *          if getProxyHostUsed() gives null.
 769      */
 770     @Override
 771     public int getProxyPortUsed() {
 772         return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||
 773                 proxy.type() == Proxy.Type.SOCKS)? -1:
 774             ((InetSocketAddress)proxy.address()).getPort();
 775     }
 776 }