0N/A/*
3002N/A * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A
0N/Apackage sun.net.www.protocol.https;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.UnsupportedEncodingException;
0N/Aimport java.io.PrintStream;
0N/Aimport java.io.BufferedOutputStream;
5843N/Aimport java.net.InetAddress;
0N/Aimport java.net.Socket;
2241N/Aimport java.net.SocketException;
0N/Aimport java.net.URL;
0N/Aimport java.net.UnknownHostException;
0N/Aimport java.net.InetSocketAddress;
0N/Aimport java.net.Proxy;
0N/Aimport java.security.Principal;
0N/Aimport java.security.cert.*;
0N/Aimport java.util.StringTokenizer;
0N/Aimport java.util.Vector;
0N/Aimport java.security.AccessController;
0N/A
0N/Aimport javax.security.auth.x500.X500Principal;
0N/A
0N/Aimport javax.net.ssl.*;
0N/Aimport sun.net.www.http.HttpClient;
5843N/Aimport sun.net.www.protocol.http.HttpURLConnection;
0N/Aimport sun.security.action.*;
0N/A
0N/Aimport sun.security.util.HostnameChecker;
0N/Aimport sun.security.ssl.SSLSocketImpl;
0N/A
5843N/Aimport sun.util.logging.PlatformLogger;
5843N/Aimport static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;
5843N/A
0N/A
0N/A/**
0N/A * This class provides HTTPS client URL support, building on the standard
0N/A * "sun.net.www" HTTP protocol handler. HTTPS is the same protocol as HTTP,
0N/A * but differs in the transport layer which it uses: <UL>
0N/A *
0N/A * <LI>There's a <em>Secure Sockets Layer</em> between TCP
0N/A * and the HTTP protocol code.
0N/A *
0N/A * <LI>It uses a different default TCP port.
0N/A *
0N/A * <LI>It doesn't use application level proxies, which can see and
0N/A * manipulate HTTP user level data, compromising privacy. It uses
0N/A * low level tunneling instead, which hides HTTP protocol and data
0N/A * from all third parties. (Traffic analysis is still possible).
0N/A *
0N/A * <LI>It does basic server authentication, to protect
0N/A * against "URL spoofing" attacks. This involves deciding
0N/A * whether the X.509 certificate chain identifying the server
0N/A * is trusted, and verifying that the name of the server is
0N/A * found in the certificate. (The application may enable an
0N/A * anonymous SSL cipher suite, and such checks are not done
0N/A * for anonymous ciphers.)
0N/A *
0N/A * <LI>It exposes key SSL session attributes, specifically the
0N/A * cipher suite in use and the server's X509 certificates, to
0N/A * application software which knows about this protocol handler.
0N/A *
0N/A * </UL>
0N/A *
0N/A * <P> System properties used include: <UL>
0N/A *
0N/A * <LI><em>https.proxyHost</em> ... the host supporting SSL
0N/A * tunneling using the conventional CONNECT syntax
0N/A *
0N/A * <LI><em>https.proxyPort</em> ... port to use on proxyHost
0N/A *
0N/A * <LI><em>https.cipherSuites</em> ... comma separated list of
0N/A * SSL cipher suite names to enable.
0N/A *
0N/A * <LI><em>http.nonProxyHosts</em> ...
0N/A *
0N/A * </UL>
0N/A *
0N/A * @author David Brownell
0N/A * @author Bill Foote
0N/A */
0N/A
0N/A// final for export control reasons (access to APIs); remove with care
0N/Afinal class HttpsClient extends HttpClient
0N/A implements HandshakeCompletedListener
0N/A{
0N/A // STATIC STATE and ACCESSORS THERETO
0N/A
0N/A // HTTPS uses a different default port number than HTTP.
0N/A private static final int httpsPortNumber = 443;
0N/A
3002N/A // default HostnameVerifier class canonical name
3002N/A private static final String defaultHVCanonicalName =
3002N/A "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";
3002N/A
0N/A /** Returns the default HTTPS port (443) */
2241N/A @Override
0N/A protected int getDefaultPort() { return httpsPortNumber; }
0N/A
0N/A private HostnameVerifier hv;
0N/A private SSLSocketFactory sslSocketFactory;
0N/A
0N/A // HttpClient.proxyDisabled will always be false, because we don't
0N/A // use an application-level HTTP proxy. We might tunnel through
0N/A // our http proxy, though.
0N/A
0N/A
0N/A // INSTANCE DATA
0N/A
0N/A // last negotiated SSL session
0N/A private SSLSession session;
0N/A
0N/A private String [] getCipherSuites() {
0N/A //
0N/A // If ciphers are assigned, sort them into an array.
0N/A //
0N/A String ciphers [];
0N/A String cipherString = AccessController.doPrivileged(
0N/A new GetPropertyAction("https.cipherSuites"));
0N/A
0N/A if (cipherString == null || "".equals(cipherString)) {
0N/A ciphers = null;
0N/A } else {
0N/A StringTokenizer tokenizer;
0N/A Vector<String> v = new Vector<String>();
0N/A
0N/A tokenizer = new StringTokenizer(cipherString, ",");
0N/A while (tokenizer.hasMoreTokens())
0N/A v.addElement(tokenizer.nextToken());
0N/A ciphers = new String [v.size()];
0N/A for (int i = 0; i < ciphers.length; i++)
0N/A ciphers [i] = v.elementAt(i);
0N/A }
0N/A return ciphers;
0N/A }
0N/A
0N/A private String [] getProtocols() {
0N/A //
0N/A // If protocols are assigned, sort them into an array.
0N/A //
0N/A String protocols [];
0N/A String protocolString = AccessController.doPrivileged(
0N/A new GetPropertyAction("https.protocols"));
0N/A
0N/A if (protocolString == null || "".equals(protocolString)) {
0N/A protocols = null;
0N/A } else {
0N/A StringTokenizer tokenizer;
0N/A Vector<String> v = new Vector<String>();
0N/A
0N/A tokenizer = new StringTokenizer(protocolString, ",");
0N/A while (tokenizer.hasMoreTokens())
0N/A v.addElement(tokenizer.nextToken());
0N/A protocols = new String [v.size()];
0N/A for (int i = 0; i < protocols.length; i++) {
0N/A protocols [i] = v.elementAt(i);
0N/A }
0N/A }
0N/A return protocols;
0N/A }
0N/A
0N/A private String getUserAgent() {
0N/A String userAgent = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetPropertyAction("https.agent"));
0N/A if (userAgent == null || userAgent.length() == 0) {
0N/A userAgent = "JSSE";
0N/A }
0N/A return userAgent;
0N/A }
0N/A
0N/A // should remove once HttpClient.newHttpProxy is putback
0N/A private static Proxy newHttpProxy(String proxyHost, int proxyPort) {
0N/A InetSocketAddress saddr = null;
0N/A final String phost = proxyHost;
0N/A final int pport = proxyPort < 0 ? httpsPortNumber : proxyPort;
0N/A try {
0N/A saddr = java.security.AccessController.doPrivileged(new
0N/A java.security.PrivilegedExceptionAction<InetSocketAddress>() {
0N/A public InetSocketAddress run() {
0N/A return new InetSocketAddress(phost, pport);
0N/A }});
0N/A } catch (java.security.PrivilegedActionException pae) {
0N/A }
0N/A return new Proxy(Proxy.Type.HTTP, saddr);
0N/A }
0N/A
0N/A // CONSTRUCTOR, FACTORY
0N/A
0N/A
0N/A /**
0N/A * Create an HTTPS client URL. Traffic will be tunneled through any
0N/A * intermediate nodes rather than proxied, so that confidentiality
0N/A * of data exchanged can be preserved. However, note that all the
0N/A * anonymous SSL flavors are subject to "person-in-the-middle"
0N/A * attacks against confidentiality. If you enable use of those
0N/A * flavors, you may be giving up the protection you get through
0N/A * SSL tunneling.
0N/A *
0N/A * Use New to get new HttpsClient. This constructor is meant to be
0N/A * used only by New method. New properly checks for URL spoofing.
0N/A *
0N/A * @param URL https URL with which a connection must be established
0N/A */
0N/A private HttpsClient(SSLSocketFactory sf, URL url)
0N/A throws IOException
0N/A {
0N/A // HttpClient-level proxying is always disabled,
0N/A // because we override doConnect to do tunneling instead.
0N/A this(sf, url, (String)null, -1);
0N/A }
0N/A
0N/A /**
0N/A * Create an HTTPS client URL. Traffic will be tunneled through
0N/A * the specified proxy server.
0N/A */
0N/A HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)
0N/A throws IOException {
0N/A this(sf, url, proxyHost, proxyPort, -1);
0N/A }
0N/A
0N/A /**
0N/A * Create an HTTPS client URL. Traffic will be tunneled through
0N/A * the specified proxy server, with a connect timeout
0N/A */
0N/A HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,
0N/A int connectTimeout)
0N/A throws IOException {
0N/A this(sf, url,
0N/A (proxyHost == null? null:
0N/A HttpsClient.newHttpProxy(proxyHost, proxyPort)),
0N/A connectTimeout);
0N/A }
0N/A
0N/A /**
0N/A * Same as previous constructor except using a Proxy
0N/A */
0N/A HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,
0N/A int connectTimeout)
0N/A throws IOException {
0N/A this.proxy = proxy;
0N/A setSSLSocketFactory(sf);
0N/A this.proxyDisabled = true;
0N/A
0N/A this.host = url.getHost();
0N/A this.url = url;
0N/A port = url.getPort();
0N/A if (port == -1) {
0N/A port = getDefaultPort();
0N/A }
0N/A setConnectTimeout(connectTimeout);
0N/A openServer();
0N/A }
0N/A
0N/A
0N/A // This code largely ripped off from HttpClient.New, and
0N/A // it uses the same keepalive cache.
0N/A
5843N/A static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
5843N/A HttpURLConnection httpuc)
0N/A throws IOException {
5843N/A return HttpsClient.New(sf, url, hv, true, httpuc);
0N/A }
0N/A
0N/A /** See HttpClient for the model for this method. */
0N/A static HttpClient New(SSLSocketFactory sf, URL url,
5843N/A HostnameVerifier hv, boolean useCache,
5843N/A HttpURLConnection httpuc) throws IOException {
5843N/A return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);
0N/A }
0N/A
0N/A /**
0N/A * Get a HTTPS client to the URL. Traffic will be tunneled through
0N/A * the specified proxy server.
0N/A */
0N/A static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
5843N/A String proxyHost, int proxyPort,
5843N/A HttpURLConnection httpuc) throws IOException {
5843N/A return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);
0N/A }
0N/A
0N/A static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
5843N/A String proxyHost, int proxyPort, boolean useCache,
5843N/A HttpURLConnection httpuc)
0N/A throws IOException {
5843N/A return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,
5843N/A httpuc);
0N/A }
0N/A
0N/A static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
0N/A String proxyHost, int proxyPort, boolean useCache,
5843N/A int connectTimeout, HttpURLConnection httpuc)
0N/A throws IOException {
0N/A
0N/A return HttpsClient.New(sf, url, hv,
0N/A (proxyHost == null? null :
0N/A HttpsClient.newHttpProxy(proxyHost, proxyPort)),
5843N/A useCache, connectTimeout, httpuc);
0N/A }
0N/A
0N/A static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,
0N/A Proxy p, boolean useCache,
5843N/A int connectTimeout, HttpURLConnection httpuc)
5843N/A throws IOException
5843N/A {
5843N/A if (p == null) {
5843N/A p = Proxy.NO_PROXY;
5843N/A }
0N/A HttpsClient ret = null;
0N/A if (useCache) {
0N/A /* see if one's already around */
0N/A ret = (HttpsClient) kac.get(url, sf);
5843N/A if (ret != null && httpuc != null &&
5843N/A httpuc.streaming() &&
5843N/A httpuc.getRequestMethod() == "POST") {
5843N/A if (!ret.available())
5843N/A ret = null;
5843N/A }
5843N/A
0N/A if (ret != null) {
5843N/A if ((ret.proxy != null && ret.proxy.equals(p)) ||
5843N/A (ret.proxy == null && p == null)) {
5843N/A synchronized (ret) {
5843N/A ret.cachedHttpClient = true;
5843N/A assert ret.inCache;
5843N/A ret.inCache = false;
5843N/A if (httpuc != null && ret.needsTunneling())
5843N/A httpuc.setTunnelState(TUNNELING);
5843N/A PlatformLogger logger = HttpURLConnection.getHttpLogger();
5843N/A if (logger.isLoggable(PlatformLogger.FINEST)) {
5843N/A logger.finest("KeepAlive stream retrieved from the cache, " + ret);
5843N/A }
5843N/A }
5843N/A } else {
5843N/A // We cannot return this connection to the cache as it's
5843N/A // KeepAliveTimeout will get reset. We simply close the connection.
5843N/A // This should be fine as it is very rare that a connection
5843N/A // to the same host will not use the same proxy.
5843N/A synchronized(ret) {
5843N/A ret.inCache = false;
5843N/A ret.closeServer();
5843N/A }
5843N/A ret = null;
5843N/A }
0N/A }
0N/A }
0N/A if (ret == null) {
0N/A ret = new HttpsClient(sf, url, p, connectTimeout);
0N/A } else {
0N/A SecurityManager security = System.getSecurityManager();
0N/A if (security != null) {
5843N/A if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) {
5843N/A security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());
5843N/A } else {
5843N/A security.checkConnect(url.getHost(), url.getPort());
5843N/A }
0N/A }
0N/A ret.url = url;
0N/A }
0N/A ret.setHostnameVerifier(hv);
0N/A
0N/A return ret;
0N/A }
0N/A
0N/A // METHODS
0N/A void setHostnameVerifier(HostnameVerifier hv) {
0N/A this.hv = hv;
0N/A }
0N/A
0N/A void setSSLSocketFactory(SSLSocketFactory sf) {
0N/A sslSocketFactory = sf;
0N/A }
0N/A
0N/A SSLSocketFactory getSSLSocketFactory() {
0N/A return sslSocketFactory;
0N/A }
0N/A
2241N/A /**
2241N/A * The following method, createSocket, is defined in NetworkClient
2241N/A * and overridden here so that the socket facroty is used to create
2241N/A * new sockets.
2241N/A */
2241N/A @Override
2241N/A protected Socket createSocket() throws IOException {
2241N/A try {
2241N/A return sslSocketFactory.createSocket();
2241N/A } catch (SocketException se) {
2241N/A //
2241N/A // bug 6771432
2241N/A // javax.net.SocketFactory throws a SocketException with an
2241N/A // UnsupportedOperationException as its cause to indicate that
2241N/A // unconnected sockets have not been implemented.
2241N/A //
2241N/A Throwable t = se.getCause();
2241N/A if (t != null && t instanceof UnsupportedOperationException) {
2241N/A return super.createSocket();
2241N/A } else {
2241N/A throw se;
2241N/A }
2241N/A }
2241N/A }
2241N/A
2241N/A
2241N/A @Override
0N/A public boolean needsTunneling() {
0N/A return (proxy != null && proxy.type() != Proxy.Type.DIRECT
0N/A && proxy.type() != Proxy.Type.SOCKS);
0N/A }
0N/A
2241N/A @Override
0N/A public void afterConnect() throws IOException, UnknownHostException {
0N/A if (!isCachedConnection()) {
0N/A SSLSocket s = null;
0N/A SSLSocketFactory factory = sslSocketFactory;
0N/A try {
0N/A if (!(serverSocket instanceof SSLSocket)) {
0N/A s = (SSLSocket)factory.createSocket(serverSocket,
0N/A host, port, true);
0N/A } else {
0N/A s = (SSLSocket)serverSocket;
2241N/A if (s instanceof SSLSocketImpl) {
2241N/A ((SSLSocketImpl)s).setHost(host);
2241N/A }
0N/A }
0N/A } catch (IOException ex) {
0N/A // If we fail to connect through the tunnel, try it
0N/A // locally, as a last resort. If this doesn't work,
0N/A // throw the original exception.
0N/A try {
0N/A s = (SSLSocket)factory.createSocket(host, port);
0N/A } catch (IOException ignored) {
0N/A throw ex;
0N/A }
0N/A }
0N/A
0N/A //
0N/A // Force handshaking, so that we get any authentication.
0N/A // Register a handshake callback so our session state tracks any
0N/A // later session renegotiations.
0N/A //
0N/A String [] protocols = getProtocols();
0N/A String [] ciphers = getCipherSuites();
0N/A if (protocols != null) {
0N/A s.setEnabledProtocols(protocols);
0N/A }
0N/A if (ciphers != null) {
0N/A s.setEnabledCipherSuites(ciphers);
0N/A }
0N/A s.addHandshakeCompletedListener(this);
0N/A
3002N/A // We have two hostname verification approaches. One is in
3002N/A // SSL/TLS socket layer, where the algorithm is configured with
3002N/A // SSLParameters.setEndpointIdentificationAlgorithm(), and the
3002N/A // hostname verification is done by X509ExtendedTrustManager when
3002N/A // the algorithm is "HTTPS". The other one is in HTTPS layer,
3002N/A // where the algorithm is customized by
3002N/A // HttpsURLConnection.setHostnameVerifier(), and the hostname
3002N/A // verification is done by HostnameVerifier when the default
3002N/A // rules for hostname verification fail.
3002N/A //
3002N/A // The relationship between two hostname verification approaches
3002N/A // likes the following:
3002N/A //
3002N/A // | EIA algorithm
3002N/A // +----------------------------------------------
3002N/A // | null | HTTPS | LDAP/other |
3002N/A // -------------------------------------------------------------
3002N/A // | |1 |2 |3 |
3002N/A // HNV | default | Set HTTPS EIA | use EIA | HTTPS |
3002N/A // |--------------------------------------------------------
3002N/A // | non - |4 |5 |6 |
3002N/A // | default | HTTPS/HNV | use EIA | HTTPS/HNV |
3002N/A // -------------------------------------------------------------
3002N/A //
3002N/A // Abbreviation:
3002N/A // EIA: the endpoint identification algorithm in SSL/TLS
3002N/A // socket layer
3002N/A // HNV: the hostname verification object in HTTPS layer
3002N/A // Notes:
3002N/A // case 1. default HNV and EIA is null
3002N/A // Set EIA as HTTPS, hostname check done in SSL/TLS
3002N/A // layer.
3002N/A // case 2. default HNV and EIA is HTTPS
3002N/A // Use existing EIA, hostname check done in SSL/TLS
3002N/A // layer.
3002N/A // case 3. default HNV and EIA is other than HTTPS
3002N/A // Use existing EIA, EIA check done in SSL/TLS
3002N/A // layer, then do HTTPS check in HTTPS layer.
3002N/A // case 4. non-default HNV and EIA is null
3002N/A // No EIA, no EIA check done in SSL/TLS layer, then do
3002N/A // HTTPS check in HTTPS layer using HNV as override.
3002N/A // case 5. non-default HNV and EIA is HTTPS
3002N/A // Use existing EIA, hostname check done in SSL/TLS
3002N/A // layer. No HNV override possible. We will review this
3002N/A // decision and may update the architecture for JDK 7.
3002N/A // case 6. non-default HNV and EIA is other than HTTPS
3002N/A // Use existing EIA, EIA check done in SSL/TLS layer,
3002N/A // then do HTTPS check in HTTPS layer as override.
3002N/A boolean needToCheckSpoofing = true;
3002N/A String identification =
3002N/A s.getSSLParameters().getEndpointIdentificationAlgorithm();
3002N/A if (identification != null && identification.length() != 0) {
3002N/A if (identification.equalsIgnoreCase("HTTPS")) {
3002N/A // Do not check server identity again out of SSLSocket,
3002N/A // the endpoint will be identified during TLS handshaking
3002N/A // in SSLSocket.
3002N/A needToCheckSpoofing = false;
3002N/A } // else, we don't understand the identification algorithm,
3002N/A // need to check URL spoofing here.
3002N/A } else {
3002N/A boolean isDefaultHostnameVerifier = false;
3002N/A
3002N/A // We prefer to let the SSLSocket do the spoof checks, but if
3002N/A // the application has specified a HostnameVerifier (HNV),
3002N/A // we will always use that.
3002N/A if (hv != null) {
3002N/A String canonicalName = hv.getClass().getCanonicalName();
3002N/A if (canonicalName != null &&
3002N/A canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) {
3002N/A isDefaultHostnameVerifier = true;
3002N/A }
3002N/A } else {
3002N/A // Unlikely to happen! As the behavior is the same as the
3002N/A // default hostname verifier, so we prefer to let the
3002N/A // SSLSocket do the spoof checks.
3002N/A isDefaultHostnameVerifier = true;
3002N/A }
3002N/A
3002N/A if (isDefaultHostnameVerifier) {
3002N/A // If the HNV is the default from HttpsURLConnection, we
3002N/A // will do the spoof checks in SSLSocket.
3002N/A SSLParameters paramaters = s.getSSLParameters();
3002N/A paramaters.setEndpointIdentificationAlgorithm("HTTPS");
3002N/A s.setSSLParameters(paramaters);
3002N/A
3002N/A needToCheckSpoofing = false;
3002N/A }
0N/A }
0N/A
0N/A s.startHandshake();
0N/A session = s.getSession();
0N/A // change the serverSocket and serverOutput
0N/A serverSocket = s;
0N/A try {
0N/A serverOutput = new PrintStream(
0N/A new BufferedOutputStream(serverSocket.getOutputStream()),
0N/A false, encoding);
0N/A } catch (UnsupportedEncodingException e) {
0N/A throw new InternalError(encoding+" encoding not found");
0N/A }
0N/A
0N/A // check URL spoofing if it has not been checked under handshaking
3002N/A if (needToCheckSpoofing) {
0N/A checkURLSpoofing(hv);
0N/A }
0N/A } else {
0N/A // if we are reusing a cached https session,
0N/A // we don't need to do handshaking etc. But we do need to
0N/A // set the ssl session
0N/A session = ((SSLSocket)serverSocket).getSession();
0N/A }
0N/A }
0N/A
0N/A // Server identity checking is done according to RFC 2818: HTTP over TLS
0N/A // Section 3.1 Server Identity
0N/A private void checkURLSpoofing(HostnameVerifier hostnameVerifier)
3002N/A throws IOException {
0N/A //
0N/A // Get authenticated server name, if any
0N/A //
0N/A String host = url.getHost();
0N/A
0N/A // if IPv6 strip off the "[]"
0N/A if (host != null && host.startsWith("[") && host.endsWith("]")) {
0N/A host = host.substring(1, host.length()-1);
0N/A }
0N/A
0N/A Certificate[] peerCerts = null;
1715N/A String cipher = session.getCipherSuite();
0N/A try {
0N/A HostnameChecker checker = HostnameChecker.getInstance(
0N/A HostnameChecker.TYPE_TLS);
0N/A
1870N/A // Use ciphersuite to determine whether Kerberos is present.
1870N/A if (cipher.startsWith("TLS_KRB5")) {
2241N/A if (!HostnameChecker.match(host, getPeerPrincipal())) {
0N/A throw new SSLPeerUnverifiedException("Hostname checker" +
0N/A " failed for Kerberos");
0N/A }
1870N/A } else { // X.509
1870N/A
0N/A // get the subject's certificate
0N/A peerCerts = session.getPeerCertificates();
0N/A
0N/A X509Certificate peerCert;
0N/A if (peerCerts[0] instanceof
0N/A java.security.cert.X509Certificate) {
0N/A peerCert = (java.security.cert.X509Certificate)peerCerts[0];
0N/A } else {
0N/A throw new SSLPeerUnverifiedException("");
0N/A }
0N/A checker.match(host, peerCert);
0N/A }
0N/A
0N/A // if it doesn't throw an exception, we passed. Return.
0N/A return;
0N/A
0N/A } catch (SSLPeerUnverifiedException e) {
0N/A
0N/A //
0N/A // client explicitly changed default policy and enabled
0N/A // anonymous ciphers; we can't check the standard policy
0N/A //
0N/A // ignore
0N/A } catch (java.security.cert.CertificateException cpe) {
0N/A // ignore
0N/A }
0N/A
0N/A if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
0N/A return;
0N/A } else if ((hostnameVerifier != null) &&
0N/A (hostnameVerifier.verify(host, session))) {
0N/A return;
0N/A }
0N/A
0N/A serverSocket.close();
0N/A session.invalidate();
0N/A
0N/A throw new IOException("HTTPS hostname wrong: should be <"
0N/A + url.getHost() + ">");
0N/A }
0N/A
2241N/A @Override
0N/A protected void putInKeepAliveCache() {
5843N/A if (inCache) {
5843N/A assert false : "Duplicate put to keep alive cache";
5843N/A return;
5843N/A }
5843N/A inCache = true;
0N/A kac.put(url, sslSocketFactory, this);
0N/A }
0N/A
75N/A /*
75N/A * Close an idle connection to this URL (if it exists in the cache).
75N/A */
2241N/A @Override
75N/A public void closeIdleConnection() {
75N/A HttpClient http = (HttpClient) kac.get(url, sslSocketFactory);
75N/A if (http != null) {
75N/A http.closeServer();
75N/A }
75N/A }
75N/A
0N/A /**
0N/A * Returns the cipher suite in use on this connection.
0N/A */
0N/A String getCipherSuite() {
0N/A return session.getCipherSuite();
0N/A }
0N/A
0N/A /**
0N/A * Returns the certificate chain the client sent to the
0N/A * server, or null if the client did not authenticate.
0N/A */
0N/A public java.security.cert.Certificate [] getLocalCertificates() {
0N/A return session.getLocalCertificates();
0N/A }
0N/A
0N/A /**
0N/A * Returns the certificate chain with which the server
0N/A * authenticated itself, or throw a SSLPeerUnverifiedException
0N/A * if the server did not authenticate.
0N/A */
0N/A java.security.cert.Certificate [] getServerCertificates()
0N/A throws SSLPeerUnverifiedException
0N/A {
0N/A return session.getPeerCertificates();
0N/A }
0N/A
0N/A /**
0N/A * Returns the X.509 certificate chain with which the server
0N/A * authenticated itself, or null if the server did not authenticate.
0N/A */
0N/A javax.security.cert.X509Certificate [] getServerCertificateChain()
0N/A throws SSLPeerUnverifiedException
0N/A {
0N/A return session.getPeerCertificateChain();
0N/A }
0N/A
0N/A /**
0N/A * Returns the principal with which the server authenticated
0N/A * itself, or throw a SSLPeerUnverifiedException if the
0N/A * server did not authenticate.
0N/A */
0N/A Principal getPeerPrincipal()
0N/A throws SSLPeerUnverifiedException
0N/A {
0N/A Principal principal;
0N/A try {
0N/A principal = session.getPeerPrincipal();
0N/A } catch (AbstractMethodError e) {
0N/A // if the provider does not support it, fallback to peer certs.
0N/A // return the X500Principal of the end-entity cert.
0N/A java.security.cert.Certificate[] certs =
0N/A session.getPeerCertificates();
0N/A principal = (X500Principal)
0N/A ((X509Certificate)certs[0]).getSubjectX500Principal();
0N/A }
0N/A return principal;
0N/A }
0N/A
0N/A /**
0N/A * Returns the principal the client sent to the
0N/A * server, or null if the client did not authenticate.
0N/A */
0N/A Principal getLocalPrincipal()
0N/A {
0N/A Principal principal;
0N/A try {
0N/A principal = session.getLocalPrincipal();
0N/A } catch (AbstractMethodError e) {
0N/A principal = null;
0N/A // if the provider does not support it, fallback to local certs.
0N/A // return the X500Principal of the end-entity cert.
0N/A java.security.cert.Certificate[] certs =
0N/A session.getLocalCertificates();
0N/A if (certs != null) {
0N/A principal = (X500Principal)
0N/A ((X509Certificate)certs[0]).getSubjectX500Principal();
0N/A }
0N/A }
0N/A return principal;
0N/A }
0N/A
0N/A /**
0N/A * This method implements the SSL HandshakeCompleted callback,
0N/A * remembering the resulting session so that it may be queried
0N/A * for the current cipher suite and peer certificates. Servers
0N/A * sometimes re-initiate handshaking, so the session in use on
0N/A * a given connection may change. When sessions change, so may
0N/A * peer identities and cipher suites.
0N/A */
0N/A public void handshakeCompleted(HandshakeCompletedEvent event)
0N/A {
0N/A session = event.getSession();
0N/A }
0N/A
0N/A /**
0N/A * @return the proxy host being used for this client, or null
0N/A * if we're not going through a proxy
0N/A */
2241N/A @Override
0N/A public String getProxyHostUsed() {
0N/A if (!needsTunneling()) {
0N/A return null;
0N/A } else {
2239N/A return super.getProxyHostUsed();
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * @return the proxy port being used for this client. Meaningless
0N/A * if getProxyHostUsed() gives null.
0N/A */
2241N/A @Override
0N/A public int getProxyPortUsed() {
0N/A return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||
0N/A proxy.type() == Proxy.Type.SOCKS)? -1:
0N/A ((InetSocketAddress)proxy.address()).getPort();
0N/A }
0N/A}