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