| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
package sun.net.www.protocol.https;  | 
 | 
 | 
 | 
import java.io.IOException;  | 
 | 
import java.io.UnsupportedEncodingException;  | 
 | 
import java.io.PrintStream;  | 
 | 
import java.io.BufferedOutputStream;  | 
 | 
import java.net.InetAddress;  | 
 | 
import java.net.Socket;  | 
 | 
import java.net.SocketException;  | 
 | 
import java.net.URL;  | 
 | 
import java.net.UnknownHostException;  | 
 | 
import java.net.InetSocketAddress;  | 
 | 
import java.net.Proxy;  | 
 | 
import java.security.Principal;  | 
 | 
import java.security.cert.*;  | 
 | 
import java.util.StringTokenizer;  | 
 | 
import java.util.Vector;  | 
 | 
import java.security.AccessController;  | 
 | 
 | 
 | 
import javax.security.auth.x500.X500Principal;  | 
 | 
 | 
 | 
import javax.net.ssl.*;  | 
 | 
import sun.net.www.http.HttpClient;  | 
 | 
import sun.net.www.protocol.http.HttpURLConnection;  | 
 | 
import sun.security.action.*;  | 
 | 
 | 
 | 
import sun.security.util.HostnameChecker;  | 
 | 
import sun.security.ssl.SSLSocketImpl;  | 
 | 
 | 
 | 
import sun.util.logging.PlatformLogger;  | 
 | 
import static sun.net.www.protocol.http.HttpURLConnection.TunnelState.*;  | 
 | 
 | 
 | 
 | 
 | 
/**  | 
 | 
 * This class provides HTTPS client URL support, building on the standard  | 
 | 
 * "sun.net.www" HTTP protocol handler.  HTTPS is the same protocol as HTTP,  | 
 | 
 * but differs in the transport layer which it uses:  <UL>  | 
 | 
 *  | 
 | 
 *      <LI>There's a <em>Secure Sockets Layer</em> between TCP  | 
 | 
 *      and the HTTP protocol code.  | 
 | 
 *  | 
 | 
 *      <LI>It uses a different default TCP port.  | 
 | 
 *  | 
 | 
 *      <LI>It doesn't use application level proxies, which can see and  | 
 | 
 *      manipulate HTTP user level data, compromising privacy.  It uses  | 
 | 
 *      low level tunneling instead, which hides HTTP protocol and data  | 
 | 
 *      from all third parties.  (Traffic analysis is still possible).  | 
 | 
 *  | 
 | 
 *      <LI>It does basic server authentication, to protect  | 
 | 
 *      against "URL spoofing" attacks.  This involves deciding  | 
 | 
 *      whether the X.509 certificate chain identifying the server  | 
 | 
 *      is trusted, and verifying that the name of the server is  | 
 | 
 *      found in the certificate.  (The application may enable an  | 
 | 
 *      anonymous SSL cipher suite, and such checks are not done  | 
 | 
 *      for anonymous ciphers.)  | 
 | 
 *  | 
 | 
 *      <LI>It exposes key SSL session attributes, specifically the  | 
 | 
 *      cipher suite in use and the server's X509 certificates, to  | 
 | 
 *      application software which knows about this protocol handler.  | 
 | 
 *  | 
 | 
 *      </UL>  | 
 | 
 *  | 
 | 
 * <P> System properties used include:  <UL>  | 
 | 
 *  | 
 | 
 *      <LI><em>https.proxyHost</em> ... the host supporting SSL  | 
 | 
 *      tunneling using the conventional CONNECT syntax  | 
 | 
 *  | 
 | 
 *      <LI><em>https.proxyPort</em> ... port to use on proxyHost  | 
 | 
 *  | 
 | 
 *      <LI><em>https.cipherSuites</em> ... comma separated list of  | 
 | 
 *      SSL cipher suite names to enable.  | 
 | 
 *  | 
 | 
 *      <LI><em>http.nonProxyHosts</em> ...  | 
 | 
 *  | 
 | 
 *      </UL>  | 
 | 
 *  | 
 | 
 * @author David Brownell  | 
 | 
 * @author Bill Foote  | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
final class HttpsClient extends HttpClient  | 
 | 
    implements HandshakeCompletedListener  | 
 | 
{ | 
 | 
    // STATIC STATE and ACCESSORS THERETO  | 
 | 
 | 
 | 
      | 
 | 
    private static final int    httpsPortNumber = 443;  | 
 | 
 | 
 | 
      | 
 | 
    private static final String defaultHVCanonicalName =  | 
 | 
            "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";  | 
 | 
 | 
 | 
      | 
 | 
    @Override  | 
 | 
    protected int getDefaultPort() { return httpsPortNumber; } | 
 | 
 | 
 | 
    private HostnameVerifier hv;  | 
 | 
    private SSLSocketFactory sslSocketFactory;  | 
 | 
 | 
 | 
    // HttpClient.proxyDisabled will always be false, because we don't  | 
 | 
    // use an application-level HTTP proxy.  We might tunnel through  | 
 | 
    // our http proxy, though.  | 
 | 
 | 
 | 
 | 
 | 
    // INSTANCE DATA  | 
 | 
 | 
 | 
      | 
 | 
    private SSLSession  session;  | 
 | 
 | 
 | 
    private String [] getCipherSuites() { | 
 | 
        //  | 
 | 
        // If ciphers are assigned, sort them into an array.  | 
 | 
          | 
 | 
        String ciphers [];  | 
 | 
        String cipherString = AccessController.doPrivileged(  | 
 | 
                new GetPropertyAction("https.cipherSuites")); | 
 | 
 | 
 | 
        if (cipherString == null || "".equals(cipherString)) { | 
 | 
            ciphers = null;  | 
 | 
        } else { | 
 | 
            StringTokenizer     tokenizer;  | 
 | 
            Vector<String>      v = new Vector<String>();  | 
 | 
 | 
 | 
            tokenizer = new StringTokenizer(cipherString, ",");  | 
 | 
            while (tokenizer.hasMoreTokens())  | 
 | 
                v.addElement(tokenizer.nextToken());  | 
 | 
            ciphers = new String [v.size()];  | 
 | 
            for (int i = 0; i < ciphers.length; i++)  | 
 | 
                ciphers [i] = v.elementAt(i);  | 
 | 
        }  | 
 | 
        return ciphers;  | 
 | 
    }  | 
 | 
 | 
 | 
    private String [] getProtocols() { | 
 | 
        //  | 
 | 
        // If protocols are assigned, sort them into an array.  | 
 | 
          | 
 | 
        String protocols [];  | 
 | 
        String protocolString = AccessController.doPrivileged(  | 
 | 
                new GetPropertyAction("https.protocols")); | 
 | 
 | 
 | 
        if (protocolString == null || "".equals(protocolString)) { | 
 | 
            protocols = null;  | 
 | 
        } else { | 
 | 
            StringTokenizer     tokenizer;  | 
 | 
            Vector<String>      v = new Vector<String>();  | 
 | 
 | 
 | 
            tokenizer = new StringTokenizer(protocolString, ",");  | 
 | 
            while (tokenizer.hasMoreTokens())  | 
 | 
                v.addElement(tokenizer.nextToken());  | 
 | 
            protocols = new String [v.size()];  | 
 | 
            for (int i = 0; i < protocols.length; i++) { | 
 | 
                protocols [i] = v.elementAt(i);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return protocols;  | 
 | 
    }  | 
 | 
 | 
 | 
    private String getUserAgent() { | 
 | 
        String userAgent = java.security.AccessController.doPrivileged(  | 
 | 
                new sun.security.action.GetPropertyAction("https.agent")); | 
 | 
        if (userAgent == null || userAgent.length() == 0) { | 
 | 
            userAgent = "JSSE";  | 
 | 
        }  | 
 | 
        return userAgent;  | 
 | 
    }  | 
 | 
 | 
 | 
    // CONSTRUCTOR, FACTORY  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private HttpsClient(SSLSocketFactory sf, URL url)  | 
 | 
    throws IOException  | 
 | 
    { | 
 | 
        // HttpClient-level proxying is always disabled,  | 
 | 
          | 
 | 
        this(sf, url, (String)null, -1);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)  | 
 | 
        throws IOException { | 
 | 
        this(sf, url, proxyHost, proxyPort, -1);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,  | 
 | 
                int connectTimeout)  | 
 | 
        throws IOException { | 
 | 
        this(sf, url,  | 
 | 
             (proxyHost == null? null:  | 
 | 
                HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),  | 
 | 
                connectTimeout);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,  | 
 | 
                int connectTimeout)  | 
 | 
        throws IOException { | 
 | 
        PlatformLogger logger = HttpURLConnection.getHttpLogger();  | 
 | 
        if (logger.isLoggable(PlatformLogger.Level.FINEST)) { | 
 | 
             logger.finest("Creating new HttpsClient with url:" + url + " and proxy:" + proxy + | 
 | 
             " with connect timeout:" + connectTimeout);  | 
 | 
        }  | 
 | 
        this.proxy = proxy;  | 
 | 
        setSSLSocketFactory(sf);  | 
 | 
        this.proxyDisabled = true;  | 
 | 
 | 
 | 
        this.host = url.getHost();  | 
 | 
        this.url = url;  | 
 | 
        port = url.getPort();  | 
 | 
        if (port == -1) { | 
 | 
            port = getDefaultPort();  | 
 | 
        }  | 
 | 
        setConnectTimeout(connectTimeout);  | 
 | 
        openServer();  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    // This code largely ripped off from HttpClient.New, and  | 
 | 
    // it uses the same keepalive cache.  | 
 | 
 | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,  | 
 | 
                          HttpURLConnection httpuc)  | 
 | 
            throws IOException { | 
 | 
        return HttpsClient.New(sf, url, hv, true, httpuc);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url,  | 
 | 
            HostnameVerifier hv, boolean useCache,  | 
 | 
            HttpURLConnection httpuc) throws IOException { | 
 | 
        return HttpsClient.New(sf, url, hv, (String)null, -1, useCache, httpuc);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,  | 
 | 
                           String proxyHost, int proxyPort,  | 
 | 
                           HttpURLConnection httpuc) throws IOException { | 
 | 
        return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true, httpuc);  | 
 | 
    }  | 
 | 
 | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,  | 
 | 
                           String proxyHost, int proxyPort, boolean useCache,  | 
 | 
                           HttpURLConnection httpuc)  | 
 | 
        throws IOException { | 
 | 
        return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1,  | 
 | 
                               httpuc);  | 
 | 
    }  | 
 | 
 | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,  | 
 | 
                          String proxyHost, int proxyPort, boolean useCache,  | 
 | 
                          int connectTimeout, HttpURLConnection httpuc)  | 
 | 
        throws IOException { | 
 | 
 | 
 | 
        return HttpsClient.New(sf, url, hv,  | 
 | 
                               (proxyHost == null? null :  | 
 | 
                                HttpClient.newHttpProxy(proxyHost, proxyPort, "https")),  | 
 | 
                               useCache, connectTimeout, httpuc);  | 
 | 
    }  | 
 | 
 | 
 | 
    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,  | 
 | 
                          Proxy p, boolean useCache,  | 
 | 
                          int connectTimeout, HttpURLConnection httpuc)  | 
 | 
        throws IOException  | 
 | 
    { | 
 | 
        if (p == null) { | 
 | 
            p = Proxy.NO_PROXY;  | 
 | 
        }  | 
 | 
        PlatformLogger logger = HttpURLConnection.getHttpLogger();  | 
 | 
        if (logger.isLoggable(PlatformLogger.Level.FINEST)) { | 
 | 
            logger.finest("Looking for HttpClient for URL " + url + | 
 | 
                " and proxy value of " + p);  | 
 | 
        }  | 
 | 
        HttpsClient ret = null;  | 
 | 
        if (useCache) { | 
 | 
              | 
 | 
            ret = (HttpsClient) kac.get(url, sf);  | 
 | 
            if (ret != null && httpuc != null &&  | 
 | 
                httpuc.streaming() &&  | 
 | 
                httpuc.getRequestMethod() == "POST") { | 
 | 
                if (!ret.available())  | 
 | 
                    ret = null;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (ret != null) { | 
 | 
                if ((ret.proxy != null && ret.proxy.equals(p)) ||  | 
 | 
                    (ret.proxy == null && p == Proxy.NO_PROXY)) { | 
 | 
                    synchronized (ret) { | 
 | 
                        ret.cachedHttpClient = true;  | 
 | 
                        assert ret.inCache;  | 
 | 
                        ret.inCache = false;  | 
 | 
                        if (httpuc != null && ret.needsTunneling())  | 
 | 
                            httpuc.setTunnelState(TUNNELING);  | 
 | 
                        if (logger.isLoggable(PlatformLogger.Level.FINEST)) { | 
 | 
                            logger.finest("KeepAlive stream retrieved from the cache, " + ret); | 
 | 
                        }  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // We cannot return this connection to the cache as it's  | 
 | 
                    // KeepAliveTimeout will get reset. We simply close the connection.  | 
 | 
                    // This should be fine as it is very rare that a connection  | 
 | 
                      | 
 | 
                    synchronized(ret) { | 
 | 
                        if (logger.isLoggable(PlatformLogger.Level.FINEST)) { | 
 | 
                            logger.finest("Not returning this connection to cache: " + ret); | 
 | 
                        }  | 
 | 
                        ret.inCache = false;  | 
 | 
                        ret.closeServer();  | 
 | 
                    }  | 
 | 
                    ret = null;  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (ret == null) { | 
 | 
            ret = new HttpsClient(sf, url, p, connectTimeout);  | 
 | 
        } else { | 
 | 
            SecurityManager security = System.getSecurityManager();  | 
 | 
            if (security != null) { | 
 | 
                if (ret.proxy == Proxy.NO_PROXY || ret.proxy == null) { | 
 | 
                    security.checkConnect(InetAddress.getByName(url.getHost()).getHostAddress(), url.getPort());  | 
 | 
                } else { | 
 | 
                    security.checkConnect(url.getHost(), url.getPort());  | 
 | 
                }  | 
 | 
            }  | 
 | 
            ret.url = url;  | 
 | 
        }  | 
 | 
        ret.setHostnameVerifier(hv);  | 
 | 
 | 
 | 
        return ret;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
    void setHostnameVerifier(HostnameVerifier hv) { | 
 | 
        this.hv = hv;  | 
 | 
    }  | 
 | 
 | 
 | 
    void setSSLSocketFactory(SSLSocketFactory sf) { | 
 | 
        sslSocketFactory = sf;  | 
 | 
    }  | 
 | 
 | 
 | 
    SSLSocketFactory getSSLSocketFactory() { | 
 | 
        return sslSocketFactory;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    protected Socket createSocket() throws IOException { | 
 | 
        try { | 
 | 
            return sslSocketFactory.createSocket();  | 
 | 
        } catch (SocketException se) { | 
 | 
            //  | 
 | 
            // bug 6771432  | 
 | 
            // javax.net.SocketFactory throws a SocketException with an  | 
 | 
            // UnsupportedOperationException as its cause to indicate that  | 
 | 
            // unconnected sockets have not been implemented.  | 
 | 
              | 
 | 
            Throwable t = se.getCause();  | 
 | 
            if (t != null && t instanceof UnsupportedOperationException) { | 
 | 
                return super.createSocket();  | 
 | 
            } else { | 
 | 
                throw se;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
    @Override  | 
 | 
    public boolean needsTunneling() { | 
 | 
        return (proxy != null && proxy.type() != Proxy.Type.DIRECT  | 
 | 
                && proxy.type() != Proxy.Type.SOCKS);  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    public void afterConnect() throws IOException, UnknownHostException { | 
 | 
        if (!isCachedConnection()) { | 
 | 
            SSLSocket s = null;  | 
 | 
            SSLSocketFactory factory = sslSocketFactory;  | 
 | 
            try { | 
 | 
                if (!(serverSocket instanceof SSLSocket)) { | 
 | 
                    s = (SSLSocket)factory.createSocket(serverSocket,  | 
 | 
                                                        host, port, true);  | 
 | 
                } else { | 
 | 
                    s = (SSLSocket)serverSocket;  | 
 | 
                    if (s instanceof SSLSocketImpl) { | 
 | 
                        ((SSLSocketImpl)s).setHost(host);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            } catch (IOException ex) { | 
 | 
                // If we fail to connect through the tunnel, try it  | 
 | 
                // locally, as a last resort.  If this doesn't work,  | 
 | 
                  | 
 | 
                try { | 
 | 
                    s = (SSLSocket)factory.createSocket(host, port);  | 
 | 
                } catch (IOException ignored) { | 
 | 
                    throw ex;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            //  | 
 | 
            // Force handshaking, so that we get any authentication.  | 
 | 
            // Register a handshake callback so our session state tracks any  | 
 | 
            // later session renegotiations.  | 
 | 
              | 
 | 
            String [] protocols = getProtocols();  | 
 | 
            String [] ciphers = getCipherSuites();  | 
 | 
            if (protocols != null) { | 
 | 
                s.setEnabledProtocols(protocols);  | 
 | 
            }  | 
 | 
            if (ciphers != null) { | 
 | 
                s.setEnabledCipherSuites(ciphers);  | 
 | 
            }  | 
 | 
            s.addHandshakeCompletedListener(this);  | 
 | 
 | 
 | 
            // We have two hostname verification approaches. One is in  | 
 | 
            // SSL/TLS socket layer, where the algorithm is configured with  | 
 | 
            // SSLParameters.setEndpointIdentificationAlgorithm(), and the  | 
 | 
            // hostname verification is done by X509ExtendedTrustManager when  | 
 | 
            // the algorithm is "HTTPS". The other one is in HTTPS layer,  | 
 | 
            // where the algorithm is customized by  | 
 | 
            // HttpsURLConnection.setHostnameVerifier(), and the hostname  | 
 | 
            // verification is done by HostnameVerifier when the default  | 
 | 
            // rules for hostname verification fail.  | 
 | 
            //  | 
 | 
            // The relationship between two hostname verification approaches  | 
 | 
            // likes the following:  | 
 | 
            //  | 
 | 
            //               |             EIA algorithm  | 
 | 
            //               +----------------------------------------------  | 
 | 
            //               |     null      |   HTTPS    |   LDAP/other   |  | 
 | 
            // -------------------------------------------------------------  | 
 | 
            //     |         |1              |2           |3               |  | 
 | 
            // HNV | default | Set HTTPS EIA | use EIA    | HTTPS          |  | 
 | 
            //     |--------------------------------------------------------  | 
 | 
            //     | non -   |4              |5           |6               |  | 
 | 
            //     | default | HTTPS/HNV     | use EIA    | HTTPS/HNV      |  | 
 | 
            // -------------------------------------------------------------  | 
 | 
            //  | 
 | 
            // Abbreviation:  | 
 | 
            //     EIA: the endpoint identification algorithm in SSL/TLS  | 
 | 
            //           socket layer  | 
 | 
            //     HNV: the hostname verification object in HTTPS layer  | 
 | 
            // Notes:  | 
 | 
            //     case 1. default HNV and EIA is null  | 
 | 
            //           Set EIA as HTTPS, hostname check done in SSL/TLS  | 
 | 
            //           layer.  | 
 | 
            //     case 2. default HNV and EIA is HTTPS  | 
 | 
            //           Use existing EIA, hostname check done in SSL/TLS  | 
 | 
            //           layer.  | 
 | 
            //     case 3. default HNV and EIA is other than HTTPS  | 
 | 
            //           Use existing EIA, EIA check done in SSL/TLS  | 
 | 
            //           layer, then do HTTPS check in HTTPS layer.  | 
 | 
            //     case 4. non-default HNV and EIA is null  | 
 | 
            //           No EIA, no EIA check done in SSL/TLS layer, then do  | 
 | 
            //           HTTPS check in HTTPS layer using HNV as override.  | 
 | 
            //     case 5. non-default HNV and EIA is HTTPS  | 
 | 
            //           Use existing EIA, hostname check done in SSL/TLS  | 
 | 
            //           layer. No HNV override possible. We will review this  | 
 | 
            //           decision and may update the architecture for JDK 7.  | 
 | 
            //     case 6. non-default HNV and EIA is other than HTTPS  | 
 | 
            //           Use existing EIA, EIA check done in SSL/TLS layer,  | 
 | 
              | 
 | 
            boolean needToCheckSpoofing = true;  | 
 | 
            String identification =  | 
 | 
                s.getSSLParameters().getEndpointIdentificationAlgorithm();  | 
 | 
            if (identification != null && identification.length() != 0) { | 
 | 
                if (identification.equalsIgnoreCase("HTTPS")) { | 
 | 
                    // Do not check server identity again out of SSLSocket,  | 
 | 
                    // the endpoint will be identified during TLS handshaking  | 
 | 
                      | 
 | 
                    needToCheckSpoofing = false;  | 
 | 
                }   // else, we don't understand the identification algorithm,  | 
 | 
                    // need to check URL spoofing here.  | 
 | 
            } else { | 
 | 
                boolean isDefaultHostnameVerifier = false;  | 
 | 
 | 
 | 
                // We prefer to let the SSLSocket do the spoof checks, but if  | 
 | 
                // the application has specified a HostnameVerifier (HNV),  | 
 | 
                  | 
 | 
                if (hv != null) { | 
 | 
                    String canonicalName = hv.getClass().getCanonicalName();  | 
 | 
                    if (canonicalName != null &&  | 
 | 
                    canonicalName.equalsIgnoreCase(defaultHVCanonicalName)) { | 
 | 
                        isDefaultHostnameVerifier = true;  | 
 | 
                    }  | 
 | 
                } else { | 
 | 
                    // Unlikely to happen! As the behavior is the same as the  | 
 | 
                    // default hostname verifier, so we prefer to let the  | 
 | 
                      | 
 | 
                    isDefaultHostnameVerifier = true;  | 
 | 
                }  | 
 | 
 | 
 | 
                if (isDefaultHostnameVerifier) { | 
 | 
                    // If the HNV is the default from HttpsURLConnection, we  | 
 | 
                      | 
 | 
                    SSLParameters paramaters = s.getSSLParameters();  | 
 | 
                    paramaters.setEndpointIdentificationAlgorithm("HTTPS"); | 
 | 
                    s.setSSLParameters(paramaters);  | 
 | 
 | 
 | 
                    needToCheckSpoofing = false;  | 
 | 
                }  | 
 | 
            }  | 
 | 
 | 
 | 
            s.startHandshake();  | 
 | 
            session = s.getSession();  | 
 | 
              | 
 | 
            serverSocket = s;  | 
 | 
            try { | 
 | 
                serverOutput = new PrintStream(  | 
 | 
                    new BufferedOutputStream(serverSocket.getOutputStream()),  | 
 | 
                    false, encoding);  | 
 | 
            } catch (UnsupportedEncodingException e) { | 
 | 
                throw new InternalError(encoding+" encoding not found");  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            if (needToCheckSpoofing) { | 
 | 
                checkURLSpoofing(hv);  | 
 | 
            }  | 
 | 
        } else { | 
 | 
            // if we are reusing a cached https session,  | 
 | 
            // we don't need to do handshaking etc. But we do need to  | 
 | 
              | 
 | 
            session = ((SSLSocket)serverSocket).getSession();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    // Server identity checking is done according to RFC 2818: HTTP over TLS  | 
 | 
      | 
 | 
    private void checkURLSpoofing(HostnameVerifier hostnameVerifier)  | 
 | 
            throws IOException { | 
 | 
        //  | 
 | 
        // Get authenticated server name, if any  | 
 | 
          | 
 | 
        String host = url.getHost();  | 
 | 
 | 
 | 
          | 
 | 
        if (host != null && host.startsWith("[") && host.endsWith("]")) { | 
 | 
            host = host.substring(1, host.length()-1);  | 
 | 
        }  | 
 | 
 | 
 | 
        Certificate[] peerCerts = null;  | 
 | 
        String cipher = session.getCipherSuite();  | 
 | 
        try { | 
 | 
            HostnameChecker checker = HostnameChecker.getInstance(  | 
 | 
                                                HostnameChecker.TYPE_TLS);  | 
 | 
 | 
 | 
              | 
 | 
            if (cipher.startsWith("TLS_KRB5")) { | 
 | 
                if (!HostnameChecker.match(host, getPeerPrincipal())) { | 
 | 
                    throw new SSLPeerUnverifiedException("Hostname checker" + | 
 | 
                                " failed for Kerberos");  | 
 | 
                }  | 
 | 
            } else { // X.509 | 
 | 
 | 
 | 
                  | 
 | 
                peerCerts = session.getPeerCertificates();  | 
 | 
 | 
 | 
                X509Certificate peerCert;  | 
 | 
                if (peerCerts[0] instanceof  | 
 | 
                        java.security.cert.X509Certificate) { | 
 | 
                    peerCert = (java.security.cert.X509Certificate)peerCerts[0];  | 
 | 
                } else { | 
 | 
                    throw new SSLPeerUnverifiedException(""); | 
 | 
                }  | 
 | 
                checker.match(host, peerCert);  | 
 | 
            }  | 
 | 
 | 
 | 
              | 
 | 
            return;  | 
 | 
 | 
 | 
        } catch (SSLPeerUnverifiedException e) { | 
 | 
 | 
 | 
            //  | 
 | 
            // client explicitly changed default policy and enabled  | 
 | 
            // anonymous ciphers; we can't check the standard policy  | 
 | 
            //  | 
 | 
            // ignore  | 
 | 
        } catch (java.security.cert.CertificateException cpe) { | 
 | 
            // ignore  | 
 | 
        }  | 
 | 
 | 
 | 
        if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) { | 
 | 
            return;  | 
 | 
        } else if ((hostnameVerifier != null) &&  | 
 | 
                   (hostnameVerifier.verify(host, session))) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
        serverSocket.close();  | 
 | 
        session.invalidate();  | 
 | 
 | 
 | 
        throw new IOException("HTTPS hostname wrong:  should be <" | 
 | 
                              + url.getHost() + ">");  | 
 | 
    }  | 
 | 
 | 
 | 
    @Override  | 
 | 
    protected void putInKeepAliveCache() { | 
 | 
        if (inCache) { | 
 | 
            assert false : "Duplicate put to keep alive cache";  | 
 | 
            return;  | 
 | 
        }  | 
 | 
        inCache = true;  | 
 | 
        kac.put(url, sslSocketFactory, this);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public void closeIdleConnection() { | 
 | 
        HttpClient http = kac.get(url, sslSocketFactory);  | 
 | 
        if (http != null) { | 
 | 
            http.closeServer();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    String getCipherSuite() { | 
 | 
        return session.getCipherSuite();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public java.security.cert.Certificate [] getLocalCertificates() { | 
 | 
        return session.getLocalCertificates();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    java.security.cert.Certificate [] getServerCertificates()  | 
 | 
            throws SSLPeerUnverifiedException  | 
 | 
    { | 
 | 
        return session.getPeerCertificates();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    javax.security.cert.X509Certificate [] getServerCertificateChain()  | 
 | 
            throws SSLPeerUnverifiedException  | 
 | 
    { | 
 | 
        return session.getPeerCertificateChain();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    Principal getPeerPrincipal()  | 
 | 
            throws SSLPeerUnverifiedException  | 
 | 
    { | 
 | 
        Principal principal;  | 
 | 
        try { | 
 | 
            principal = session.getPeerPrincipal();  | 
 | 
        } catch (AbstractMethodError e) { | 
 | 
            // if the provider does not support it, fallback to peer certs.  | 
 | 
              | 
 | 
            java.security.cert.Certificate[] certs =  | 
 | 
                        session.getPeerCertificates();  | 
 | 
            principal = ((X509Certificate)certs[0]).getSubjectX500Principal();  | 
 | 
        }  | 
 | 
        return principal;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    Principal getLocalPrincipal()  | 
 | 
    { | 
 | 
        Principal principal;  | 
 | 
        try { | 
 | 
            principal = session.getLocalPrincipal();  | 
 | 
        } catch (AbstractMethodError e) { | 
 | 
            principal = null;  | 
 | 
            // if the provider does not support it, fallback to local certs.  | 
 | 
              | 
 | 
            java.security.cert.Certificate[] certs =  | 
 | 
                        session.getLocalCertificates();  | 
 | 
            if (certs != null) { | 
 | 
                principal = ((X509Certificate)certs[0]).getSubjectX500Principal();  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return principal;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public void handshakeCompleted(HandshakeCompletedEvent event)  | 
 | 
    { | 
 | 
        session = event.getSession();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public String getProxyHostUsed() { | 
 | 
        if (!needsTunneling()) { | 
 | 
            return null;  | 
 | 
        } else { | 
 | 
            return super.getProxyHostUsed();  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    @Override  | 
 | 
    public int getProxyPortUsed() { | 
 | 
        return (proxy == null || proxy.type() == Proxy.Type.DIRECT ||  | 
 | 
                proxy.type() == Proxy.Type.SOCKS)? -1:  | 
 | 
            ((InetSocketAddress)proxy.address()).getPort();  | 
 | 
    }  | 
 | 
}  |