749 lines · 27.6 KB · Java
RawDownload
/* * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */  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.Socket;import java.net.SocketException;import java.net.URL;import java.net.UnknownHostException;import java.net.InetSocketAddress;import java.net.Proxy;import java.net.CookieHandler;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.security.action.*; import sun.security.util.HostnameChecker;import sun.security.ssl.SSLSocketImpl;  /** * 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 for export control reasons (access to APIs); remove with carefinal class HttpsClient extends HttpClient    implements HandshakeCompletedListener{    // STATIC STATE and ACCESSORS THERETO     // HTTPS uses a different default port number than HTTP.    private static final int    httpsPortNumber = 443;     // default HostnameVerifier class canonical name    private static final String defaultHVCanonicalName =            "javax.net.ssl.HttpsURLConnection.DefaultHostnameVerifier";     /** Returns the default HTTPS port (443) */    @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     // last negotiated SSL session    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;    }     // should remove once HttpClient.newHttpProxy is putback    private static Proxy newHttpProxy(String proxyHost, int proxyPort) {        InetSocketAddress saddr = null;        final String phost = proxyHost;        final int pport = proxyPort < 0 ? httpsPortNumber : proxyPort;        try {            saddr = java.security.AccessController.doPrivileged(new                java.security.PrivilegedExceptionAction<InetSocketAddress>() {                public InetSocketAddress run() {                    return new InetSocketAddress(phost, pport);                }});        } catch (java.security.PrivilegedActionException pae) {        }        return new Proxy(Proxy.Type.HTTP, saddr);    }     // CONSTRUCTOR, FACTORY      /**     * Create an HTTPS client URL.  Traffic will be tunneled through any     * intermediate nodes rather than proxied, so that confidentiality     * of data exchanged can be preserved.  However, note that all the     * anonymous SSL flavors are subject to "person-in-the-middle"     * attacks against confidentiality.  If you enable use of those     * flavors, you may be giving up the protection you get through     * SSL tunneling.     *     * Use New to get new HttpsClient. This constructor is meant to be     * used only by New method. New properly checks for URL spoofing.     *     * @param URL https URL with which a connection must be established     */    private HttpsClient(SSLSocketFactory sf, URL url)    throws IOException    {        // HttpClient-level proxying is always disabled,        // because we override doConnect to do tunneling instead.        this(sf, url, (String)null, -1);    }     /**     *  Create an HTTPS client URL.  Traffic will be tunneled through     * the specified proxy server.     */    HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort)        throws IOException {        this(sf, url, proxyHost, proxyPort, -1);    }     /**     *  Create an HTTPS client URL.  Traffic will be tunneled through     * the specified proxy server, with a connect timeout     */    HttpsClient(SSLSocketFactory sf, URL url, String proxyHost, int proxyPort,                int connectTimeout)        throws IOException {        this(sf, url,             (proxyHost == null? null:                HttpsClient.newHttpProxy(proxyHost, proxyPort)),                connectTimeout);    }     /**     *  Same as previous constructor except using a Proxy     */    HttpsClient(SSLSocketFactory sf, URL url, Proxy proxy,                int connectTimeout)        throws IOException {        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);        // get the cookieHandler if there is any        cookieHandler = java.security.AccessController.doPrivileged(            new java.security.PrivilegedAction<CookieHandler>() {                public CookieHandler run() {                    return CookieHandler.getDefault();                }            });        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)            throws IOException {        return HttpsClient.New(sf, url, hv, true);    }     /** See HttpClient for the model for this method. */    static HttpClient New(SSLSocketFactory sf, URL url,            HostnameVerifier hv, boolean useCache) throws IOException {        return HttpsClient.New(sf, url, hv, (String)null, -1, useCache);    }     /**     * Get a HTTPS client to the URL.  Traffic will be tunneled through     * the specified proxy server.     */    static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,                           String proxyHost, int proxyPort) throws IOException {        return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, true);    }     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,                           String proxyHost, int proxyPort, boolean useCache)        throws IOException {        return HttpsClient.New(sf, url, hv, proxyHost, proxyPort, useCache, -1);    }     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,                          String proxyHost, int proxyPort, boolean useCache,                          int connectTimeout)        throws IOException {         return HttpsClient.New(sf, url, hv,                               (proxyHost == null? null :                                HttpsClient.newHttpProxy(proxyHost, proxyPort)),                               useCache, connectTimeout);    }     static HttpClient New(SSLSocketFactory sf, URL url, HostnameVerifier hv,                          Proxy p, boolean useCache,                          int connectTimeout)        throws IOException {        HttpsClient ret = null;        if (useCache) {            /* see if one's already around */            ret = (HttpsClient) kac.get(url, sf);            if (ret != null) {                ret.cachedHttpClient = true;            }        }        if (ret == null) {            ret = new HttpsClient(sf, url, p, connectTimeout);        } else {            SecurityManager security = System.getSecurityManager();            if (security != null) {                security.checkConnect(url.getHost(), url.getPort());            }            ret.url = url;        }        ret.setHostnameVerifier(hv);         return ret;    }     // METHODS    void setHostnameVerifier(HostnameVerifier hv) {        this.hv = hv;    }     void setSSLSocketFactory(SSLSocketFactory sf) {        sslSocketFactory = sf;    }     SSLSocketFactory getSSLSocketFactory() {        return sslSocketFactory;    }     /**     * The following method, createSocket, is defined in NetworkClient     * and overridden here so that the socket facroty is used to create     * new sockets.     */    @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,                // throw the original exception.                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,            //           then do HTTPS check in HTTPS layer as override.            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                    // in SSLSocket.                    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),                // we will always use that.                if (hv != null) {                    String canonicalName = hv.getClass().getCanonicalName();

Lines 1–500 of 749.