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