1 /*
   2  * Copyright (c) 2001, 2012, 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     // should remove once HttpClient.newHttpProxy is putback
 196     private static Proxy newHttpProxy(String proxyHost, int proxyPort) {
 197         InetSocketAddress saddr = null;
 198         final String phost = proxyHost;
 199         final int pport = proxyPort < 0 ? httpsPortNumber : proxyPort;
 200         try {
 201             saddr = java.security.AccessController.doPrivileged(new
 202                 java.security.PrivilegedExceptionAction<InetSocketAddress>() {
 203                 public InetSocketAddress run() {
 204                     return new InetSocketAddress(phost, pport);
 205                 }});
 206         } catch (java.security.PrivilegedActionException pae) {
 207         }
 208         return new Proxy(Proxy.Type.HTTP, saddr);
 209     }
 210 
 211     // CONSTRUCTOR, FACTORY
 212 
 213 
 214     /**
 215      * Create an HTTPS client URL.  Traffic will be tunneled through any
 216      * intermediate nodes rather than proxied, so that confidentiality
 217      * of data exchanged can be preserved.  However, note that all the
 218      * anonymous SSL flavors are subject to "person-in-the-middle"
 219      * attacks against confidentiality.  If you enable use of those
 220      * flavors, you may be giving up the protection you get through
 221      * SSL tunneling.
 222      *
 223      * Use New to get new HttpsClient. This constructor is meant to be
 224      * used only by New method. New properly checks for URL spoofing.
 225      *
 226      * @param URL https URL with which a connection must be established
 227      */
 228     private HttpsClient(SSLSocketFactory sf, URL url)
 229     throws IOException
 230     {
 231         // HttpClient-level proxying is always disabled,
 232         // because we override doConnect to do tunneling instead.
 233         this(sf, url, (String)null, -1);
 234     }
 235 
 236     /**
 237      *  Create an HTTPS client URL.  Traffic will be tunneled through
 238      * the specified proxy server.
 239      */
 240     HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)
 241         throws IOException {
 242         this(sf, url, proxyHost, proxyPort, -1);
 243     }
 244 
 245     /**
 246      *  Create an HTTPS client URL.  Traffic will be tunneled through
 247      * the specified proxy server, with a connect timeout
 248      */
 249     HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,
 250                 int connectTimeout)
 251         throws IOException {
 252         this(sf, url,
 253              (proxyHost == null? null:
 254                 HttpsClient.newHttpProxy(proxyHost, proxyPort)),
 255                 connectTimeout);
 256     }
 257 
 258     /**
 259      *  Same as previous constructor except using a Proxy
 260      */
 261     HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,
 262                 int connectTimeout)
 263         throws IOException {
 264         this.proxy = proxy;
 265         setSSLSocketFactory(sf);
 266         this.proxyDisabled = true;
 267 
 268         this.host = url.getHost();
 269         this.url = url;
 270         port = url.getPort();
 271         if (port == -1) {
 272             port = getDefaultPort();
 273         }
 274         setConnectTimeout(connectTimeout);
 275         openServer();
 276     }
 277 
 278 
 279     // This code largely ripped off from HttpClient.New, and
 280     // it uses the same keepalive cache.
 281 
 282     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 283                           HttpURLConnection httpuc)
 284             throws IOException {
 285         return HttpsClient.New(sf, url, hv, true, httpuc);
 286     }
 287 
 288     /** See HttpClient for the model for this method. */
 289     static HttpClient New(SSLSocketFactory sf, URL url,
 290             HostnameVerifier hv, boolean useCache,
 291             HttpURLConnection httpuc) throws IOException {
 292         return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);
 293     }
 294 
 295     /**
 296      * Get a HTTPS client to the URL.  Traffic will be tunneled through
 297      * the specified proxy server.
 298      */
 299     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 300                            String proxyHost, int proxyPort,
 301                            HttpURLConnection httpuc) throws IOException {
 302         return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);
 303     }
 304 
 305     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 306                            String proxyHost, int proxyPort, boolean useCache,
 307                            HttpURLConnection httpuc)
 308         throws IOException {
 309         return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,
 310                                httpuc);
 311     }
 312 
 313     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 314                           String proxyHost, int proxyPort, boolean useCache,
 315                           int connectTimeout, HttpURLConnection httpuc)
 316         throws IOException {
 317 
 318         return HttpsClient.New(sf, url, hv,
 319                                (proxyHost == null? null :
 320                                 HttpsClient.newHttpProxy(proxyHost, proxyPort)),
 321                                useCache, connectTimeout, httpuc);
 322     }
 323 
 324     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
 325                           Proxy p, boolean useCache,
 326                           int connectTimeout, HttpURLConnection httpuc)
 327         throws IOException
 328     {
 329         if (p == null) {
 330             p = Proxy.NO_PROXY;
 331         }
 332         HttpsClient ret = null;
 333         if (useCache) {
 334             /* see if one's already around */
 335             ret = (HttpsClient) kac.get(url, sf);
 336             if (ret != null && httpuc != null &&
 337                 httpuc.streaming() &&
 338                 httpuc.getRequestMethod() == "POST") {
 339                 if (!ret.available())
 340                     ret = null;
 341             }
 342 
 343             if (ret != null) {
 344                 if ((ret.proxy != null && ret.proxy.equals(p)) ||
 345                     (ret.proxy == null && p == null)) {
 346                     synchronized (ret) {
 347                         ret.cachedHttpClient = true;
 348                         assert ret.inCache;
 349                         ret.inCache = false;
 350                         if (httpuc != null && ret.needsTunneling())
 351                             httpuc.setTunnelState(TUNNELING);
 352                         PlatformLogger logger = HttpURLConnection.getHttpLogger();
 353                         if (logger.isLoggable(PlatformLogger.Level.FINEST)) {
 354                             logger.finest("KeepAlive stream retrieved from the cache, " + ret);
 355                         }
 356                     }
 357                 } else {
 358                     // We cannot return this connection to the cache as it's
 359                     // KeepAliveTimeout will get reset. We simply close the connection.
 360                     // This should be fine as it is very rare that a connection
 361                     // to the same host will not use the same proxy.
 362                     synchronized(ret) {
 363                         ret.inCache = false;
 364                         ret.closeServer();
 365                     }
 366                     ret = null;
 367                 }
 368             }
 369         }
 370         if (ret == null) {
 371             ret = new HttpsClient(sf, url, p, connectTimeout);
 372         } else {
 373             SecurityManager security = System.getSecurityManager();
 374             if (security != null) {
 375                 if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
 376                     security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
 377                 } else {
 378                     security.checkConnect(url.getHost(), url.getPort());
 379                 }
 380             }
 381             ret.url = url;
 382         }
 383         ret.setHostnameVerifier(hv);
 384 
 385         return ret;
 386     }
 387 
 388     // METHODS
 389     void setHostnameVerifier(HostnameVerifier hv) {
 390         this.hv = hv;
 391     }
 392 
 393     void setSSLSocketFactory(SSLSocketFactory sf) {
 394         sslSocketFactory = sf;
 395     }
 396 
 397     SSLSocketFactory getSSLSocketFactory() {
 398         return sslSocketFactory;
 399     }
 400 
 401     /**
 402      * The following method, createSocket, is defined in NetworkClient
 403      * and overridden here so that the socket facroty is used to create
 404      * new sockets.
 405      */
 406     @Override
 407     protected Socket createSocket() throws IOException {
 408         try {
 409             return sslSocketFactory.createSocket();
 410         } catch (SocketException se) {
 411             //
 412             // bug 6771432
 413             // javax.net.SocketFactory throws a SocketException with an
 414             // UnsupportedOperationException as its cause to indicate that
 415             // unconnected sockets have not been implemented.
 416             //
 417             Throwable t = se.getCause();
 418             if (t != null && t instanceof UnsupportedOperationException) {
 419                 return super.createSocket();
 420             } else {
 421                 throw se;
 422             }
 423         }
 424     }
 425 
 426 
 427     @Override
 428     public boolean needsTunneling() {
 429         return (proxy != null && proxy.type() != Proxy.Type.DIRECT
 430                 && proxy.type() != Proxy.Type.SOCKS);
 431     }
 432 
 433     @Override
 434     public void afterConnect() throws IOException, UnknownHostException {
 435         if (!isCachedConnection()) {
 436             SSLSocket s = null;
 437             SSLSocketFactory factory = sslSocketFactory;
 438             try {
 439                 if (!(serverSocket instanceof SSLSocket)) {
 440                     s = (SSLSocket)factory.createSocket(serverSocket,
 441                                                         host, port, true);
 442                 } else {
 443                     s = (SSLSocket)serverSocket;
 444                     if (s instanceof SSLSocketImpl) {
 445                         ((SSLSocketImpl)s).setHost(host);
 446                     }
 447                 }
 448             } catch (IOException ex) {
 449                 // If we fail to connect through the tunnel, try it
 450                 // locally, as a last resort.  If this doesn't work,
 451                 // throw the original exception.
 452                 try {
 453                     s = (SSLSocket)factory.createSocket(host, port);
 454                 } catch (IOException ignored) {
 455                     throw ex;
 456                 }
 457             }
 458 
 459             //
 460             // Force handshaking, so that we get any authentication.
 461             // Register a handshake callback so our session state tracks any
 462             // later session renegotiations.
 463             //
 464             String [] protocols = getProtocols();
 465             String [] ciphers = getCipherSuites();
 466             if (protocols != null) {
 467                 s.setEnabledProtocols(protocols);
 468             }
 469             if (ciphers != null) {
 470                 s.setEnabledCipherSuites(ciphers);
 471             }
 472             s.addHandshakeCompletedListener(this);
 473 
 474             // We have two hostname verification approaches. One is in
 475             // SSL/TLS socket layer, where the algorithm is configured with
 476             // SSLParameters.setEndpointIdentificationAlgorithm(), and the
 477             // hostname verification is done by X509ExtendedTrustManager when
 478             // the algorithm is "HTTPS". The other one is in HTTPS layer,
 479             // where the algorithm is customized by
 480             // HttpsURLConnection.setHostnameVerifier(), and the hostname
 481             // verification is done by HostnameVerifier when the default
 482             // rules for hostname verification fail.
 483             //
 484             // The relationship between two hostname verification approaches
 485             // likes the following:
 486             //
 487             //               |             EIA algorithm
 488             //               +----------------------------------------------
 489             //               |     null      |   HTTPS    |   LDAP/other   |
 490             // -------------------------------------------------------------
 491             //     |         |1              |2           |3               |
 492             // HNV | default | Set HTTPS EIA | use EIA    | HTTPS          |
 493             //     |--------------------------------------------------------
 494             //     | non -   |4              |5           |6               |
 495             //     | default | HTTPS/HNV     | use EIA    | HTTPS/HNV      |
 496             // -------------------------------------------------------------
 497             //
 498             // Abbreviation:
 499             //     EIA: the endpoint identification algorithm in SSL/TLS
 500             //           socket layer
 501             //     HNV: the hostname verification object in HTTPS layer
 502             // Notes:
 503             //     case 1. default HNV and EIA is null
 504             //           Set EIA as HTTPS, hostname check done in SSL/TLS
 505             //           layer.
 506             //     case 2. default HNV and EIA is HTTPS
 507             //           Use existing EIA, hostname check done in SSL/TLS
 508             //           layer.
 509             //     case 3. default HNV and EIA is other than HTTPS
 510             //           Use existing EIA, EIA check done in SSL/TLS
 511             //           layer, then do HTTPS check in HTTPS layer.
 512             //     case 4. non-default HNV and EIA is null
 513             //           No EIA, no EIA check done in SSL/TLS layer, then do
 514             //           HTTPS check in HTTPS layer using HNV as override.
 515             //     case 5. non-default HNV and EIA is HTTPS
 516             //           Use existing EIA, hostname check done in SSL/TLS
 517             //           layer. No HNV override possible. We will review this
 518             //           decision and may update the architecture for JDK 7.
 519             //     case 6. non-default HNV and EIA is other than HTTPS
 520             //           Use existing EIA, EIA check done in SSL/TLS layer,
 521             //           then do HTTPS check in HTTPS layer as override.
 522             boolean needToCheckSpoofing = true;
 523             String identification =
 524                 s.getSSLParameters().getEndpointIdentificationAlgorithm();
 525             if (identification != null && identification.length() != 0) {
 526                 if (identification.equalsIgnoreCase("HTTPS")) {
 527                     // Do not check server identity again out of SSLSocket,
 528                     // the endpoint will be identified during TLS handshaking
 529                     // in SSLSocket.
 530                     needToCheckSpoofing = false;
 531                 }   // else, we don't understand the identification algorithm,
 532                     // need to check URL spoofing here.
 533             } else {
 534                 boolean isDefaultHostnameVerifier = false;
 535 
 536                 // We prefer to let the SSLSocket do the spoof checks, but if
 537                 // the application has specified a HostnameVerifier (HNV),
 538                 // we will always use that.
 539                 if (hv != null) {
 540                     String canonicalName = hv.getClass().getCanonicalName();
 541                     if (canonicalName != null &&
 542                     canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) {
 543                         isDefaultHostnameVerifier = true;
 544                     }
 545                 } else {
 546                     // Unlikely to happen! As the behavior is the same as the
 547                     // default hostname verifier, so we prefer to let the
 548                     // SSLSocket do the spoof checks.
 549                     isDefaultHostnameVerifier = true;
 550                 }
 551 
 552                 if (isDefaultHostnameVerifier) {
 553                     // If the HNV is the default from HttpsURLConnection, we
 554                     // will do the spoof checks in SSLSocket.
 555                     SSLParameters paramaters = s.getSSLParameters();
 556                     paramaters.setEndpointIdentificationAlgorithm("HTTPS");
 557                     s.setSSLParameters(paramaters);
 558 
 559                     needToCheckSpoofing = false;
 560                 }
 561             }
 562 
 563             s.startHandshake();
 564             session = s.getSession();
 565             // change the serverSocket and serverOutput
 566             serverSocket = s;
 567             try {
 568                 serverOutput = new PrintStream(
 569                     new BufferedOutputStream(serverSocket.getOutputStream()),
 570                     false, encoding);
 571             } catch (UnsupportedEncodingException e) {
 572                 throw new InternalError(encoding+" encoding not found");
 573             }
 574 
 575             // check URL spoofing if it has not been checked under handshaking
 576             if (needToCheckSpoofing) {
 577                 checkURLSpoofing(hv);
 578             }
 579         } else {
 580             // if we are reusing a cached https session,
 581             // we don't need to do handshaking etc. But we do need to
 582             // set the ssl session
 583             session = ((SSLSocket)serverSocket).getSession();
 584         }
 585     }
 586 
 587     // Server identity checking is done according to RFC 2818: HTTP over TLS
 588     // Section 3.1 Server Identity
 589     private void checkURLSpoofing(HostnameVerifier hostnameVerifier)
 590             throws IOException {
 591         //
 592         // Get authenticated server name, if any
 593         //
 594         String host = url.getHost();
 595 
 596         // if IPv6 strip off the "[]"
 597         if (host != null && host.startsWith("[") && host.endsWith("]")) {
 598             host = host.substring(1, host.length()-1);
 599         }
 600 
 601         Certificate[] peerCerts = null;
 602         String cipher = session.getCipherSuite();
 603         try {
 604             HostnameChecker checker = HostnameChecker.getInstance(
 605                                                 HostnameChecker.TYPE_TLS);
 606 
 607             // Use ciphersuite to determine whether Kerberos is present.
 608             if (cipher.startsWith("TLS_KRB5")) {
 609                 if (!HostnameChecker.match(host, getPeerPrincipal())) {
 610                     throw new SSLPeerUnverifiedException("Hostname checker" +
 611                                 " failed for Kerberos");
 612                 }
 613             } else { // X.509
 614 
 615                 // get the subject's certificate
 616                 peerCerts = session.getPeerCertificates();
 617 
 618                 X509Certificate peerCert;
 619                 if (peerCerts[0] instanceof
 620                         java.security.cert.X509Certificate) {
 621                     peerCert = (java.security.cert.X509Certificate)peerCerts[0];
 622                 } else {
 623                     throw new SSLPeerUnverifiedException("");
 624                 }
 625                 checker.match(host, peerCert);
 626             }
 627 
 628             // if it doesn't throw an exception, we passed. Return.
 629             return;
 630 
 631         } catch (SSLPeerUnverifiedException e) {
 632 
 633             //
 634             // client explicitly changed default policy and enabled
 635             // anonymous ciphers; we can't check the standard policy
 636             //
 637             // ignore
 638         } catch (java.security.cert.CertificateException cpe) {
 639             // ignore
 640         }
 641 
 642         if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
 643             return;
 644         } else if ((hostnameVerifier != null) &&
 645                    (hostnameVerifier.verify(host, session))) {
 646             return;
 647         }
 648 
 649         serverSocket.close();
 650         session.invalidate();
 651 
 652         throw new IOException("HTTPS hostname wrong:  should be <"
 653                               + url.getHost() + ">");
 654     }
 655 
 656     @Override
 657     protected void putInKeepAliveCache() {
 658         if (inCache) {
 659             assert false : "Duplicate put to keep alive cache";
 660             return;
 661         }
 662         inCache = true;
 663         kac.put(url, sslSocketFactory, this);
 664     }
 665 
 666     /*
 667      * Close an idle connection to this URL (if it exists in the cache).
 668      */
 669     @Override
 670     public void closeIdleConnection() {
 671         HttpClient http = kac.get(url, sslSocketFactory);
 672         if (http != null) {
 673             http.closeServer();
 674         }
 675     }
 676 
 677     /**
 678      * Returns the cipher suite in use on this connection.
 679      */
 680     String getCipherSuite() {
 681         return session.getCipherSuite();
 682     }
 683 
 684     /**
 685      * Returns the certificate chain the client sent to the
 686      * server, or null if the client did not authenticate.
 687      */
 688     public java.security.cert.Certificate [] getLocalCertificates() {
 689         return session.getLocalCertificates();
 690     }
 691 
 692     /**
 693      * Returns the certificate chain with which the server
 694      * authenticated itself, or throw a SSLPeerUnverifiedException
 695      * if the server did not authenticate.
 696      */
 697     java.security.cert.Certificate [] getServerCertificates()
 698             throws SSLPeerUnverifiedException
 699     {
 700         return session.getPeerCertificates();
 701     }
 702 
 703     /**
 704      * Returns the X.509 certificate chain with which the server
 705      * authenticated itself, or null if the server did not authenticate.
 706      */
 707     javax.security.cert.X509Certificate [] getServerCertificateChain()
 708             throws SSLPeerUnverifiedException
 709     {
 710         return session.getPeerCertificateChain();
 711     }
 712 
 713     /**
 714      * Returns the principal with which the server authenticated
 715      * itself, or throw a SSLPeerUnverifiedException if the
 716      * server did not authenticate.
 717      */
 718     Principal getPeerPrincipal()
 719             throws SSLPeerUnverifiedException
 720     {
 721         Principal principal;
 722         try {
 723             principal = session.getPeerPrincipal();
 724         } catch (AbstractMethodError e) {
 725             // if the provider does not support it, fallback to peer certs.
 726             // return the X500Principal of the end-entity cert.
 727             java.security.cert.Certificate[] certs =
 728                         session.getPeerCertificates();
 729             principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
 730         }
 731         return principal;
 732     }
 733 
 734     /**
 735      * Returns the principal the client sent to the
 736      * server, or null if the client did not authenticate.
 737      */
 738     Principal getLocalPrincipal()
 739     {
 740         Principal principal;
 741         try {
 742             principal = session.getLocalPrincipal();
 743         } catch (AbstractMethodError e) {
 744             principal = null;
 745             // if the provider does not support it, fallback to local certs.
 746             // return the X500Principal of the end-entity cert.
 747             java.security.cert.Certificate[] certs =
 748                         session.getLocalCertificates();
 749             if (certs != null) {
 750                 principal = ((X509Certificate)certs[0]).getSubjectX500Principal();
 751             }
 752         }
 753         return principal;
 754     }
 755 
 756     /**
 757      * This method implements the SSL HandshakeCompleted callback,
 758      * remembering the resulting session so that it may be queried
 759      * for the current cipher suite and peer certificates.  Servers
 760      * sometimes re-initiate handshaking, so the session in use on
 761      * a given connection may change.  When sessions change, so may
 762      * peer identities and cipher suites.
 763      */
 764     public void handshakeCompleted(HandshakeCompletedEvent event)
 765     {
 766         session = event.getSession();
 767     }
 768 
 769     /**
 770      * @return the proxy host being used for this client, or null
 771      *          if we're not going through a proxy
 772      */
 773     @Override
 774     public String getProxyHostUsed() {
 775         if (!needsTunneling()) {
 776             return null;
 777         } else {
 778             return super.getProxyHostUsed();
 779         }
 780     }
 781 
 782     /**
 783      * @return the proxy port being used for this client.  Meaningless
 784      *          if getProxyHostUsed() gives null.
 785      */
 786     @Override
 787     public int getProxyPortUsed() {
 788         return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||
 789                 proxy.type() == Proxy.Type.SOCKS)? -1:
 790             ((InetSocketAddress)proxy.address()).getPort();
 791     }
 792 }