/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package javax.net.ssl; import com.google.j2objc.net.IosHttpsHandler; import java.net.HttpURLConnection; import java.net.URL; import java.security.Principal; import java.security.cert.Certificate; import java.security.cert.X509Certificate; /** * An {@link HttpURLConnection} for HTTPS (<a * href="http://tools.ietf.org/html/rfc2818">RFC 2818</a>). A * connected {@code HttpsURLConnection} allows access to the * negotiated cipher suite, the server certificate chain, and the * client certificate chain if any. * * <h3>Providing an application specific X509TrustManager</h3> * * If an application wants to trust Certificate Authority (CA) * certificates that are not part of the system, it should specify its * own {@code X509TrustManager} via a {@code SSLSocketFactory} set on * the {@code HttpsURLConnection}. The {@code X509TrustManager} can be * created based on a {@code KeyStore} using a {@code * TrustManagerFactory} to supply trusted CA certificates. Note that * self-signed certificates are effectively their own CA and can be * trusted by including them in a {@code KeyStore}. * * <p>For example, to trust a set of certificates specified by a {@code KeyStore}: * <pre> {@code * KeyStore keyStore = ...; * String algorithm = TrustManagerFactory.getDefaultAlgorithm(); * TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm); * tmf.init(keyStore); * * SSLContext context = SSLContext.getInstance("TLS"); * context.init(null, tmf.getTrustManagers(), null); * * URL url = new URL("https://www.example.com/"); * HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); * urlConnection.setSSLSocketFactory(context.getSocketFactory()); * InputStream in = urlConnection.getInputStream(); * }</pre> * * <p>It is possible to implement {@code X509TrustManager} directly * instead of using one created by a {@code * TrustManagerFactory}. While this is straightforward in the insecure * case of allowing all certificate chains to pass verification, * writing a proper implementation will usually want to take advantage * of {@link java.security.cert.CertPathValidator * CertPathValidator}. In general, it might be better to write a * custom {@code KeyStore} implementation to pass to the {@code * TrustManagerFactory} than to try and write a custom {@code * X509TrustManager}. * * <h3>Providing an application specific X509KeyManager</h3> * * A custom {@code X509KeyManager} can be used to supply a client * certificate and its associated private key to authenticate a * connection to the server. The {@code X509KeyManager} can be created * based on a {@code KeyStore} using a {@code KeyManagerFactory}. * * <p>For example, to supply client certificates from a {@code KeyStore}: * <pre> {@code * KeyStore keyStore = ...; * String algorithm = KeyManagerFactory.getDefaultAlgorithm(); * KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm); * kmf.init(keyStore); * * SSLContext context = SSLContext.getInstance("TLS"); * context.init(kmf.getKeyManagers(), null, null); * * URL url = new URL("https://www.example.com/"); * HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); * urlConnection.setSSLSocketFactory(context.getSocketFactory()); * InputStream in = urlConnection.getInputStream(); * }</pre> * * <p>A {@code X509KeyManager} can also be implemented directly. This * can allow an application to return a certificate and private key * from a non-{@code KeyStore} source or to specify its own logic for * selecting a specific credential to use when many may be present in * a single {@code KeyStore}. * * <h3>TLS Intolerance Support</h3> * * This class attempts to create secure connections using common TLS * extensions and SSL deflate compression. Should that fail, the * connection will be retried with SSLv3 only. */ public abstract class HttpsURLConnection extends HttpURLConnection { /* * Holds default instances so class preloading doesn't create an instance of * it. */ private static class NoPreloadHolder { public static HostnameVerifier defaultHostnameVerifier; // TODO(tball): enable when okhttp is added. // static { // try { // defaultHostnameVerifier = (HostnameVerifier) // Class.forName("com.android.okhttp.internal.tls.OkHostnameVerifier") // .getField("INSTANCE").get(null); // } catch (Exception e) { // throw new AssertionError("Failed to obtain okhttp HostnameVerifier", e); // } // } public static SSLSocketFactory defaultSSLSocketFactory = (SSLSocketFactory) SSLSocketFactory .getDefault(); } /** * Sets the default hostname verifier to be used by new instances. * * @param v * the new default hostname verifier * @throws IllegalArgumentException * if the specified verifier is {@code null}. */ public static void setDefaultHostnameVerifier(HostnameVerifier v) { if (v == null) { throw new IllegalArgumentException("HostnameVerifier is null"); } NoPreloadHolder.defaultHostnameVerifier = v; } /** * Returns the default hostname verifier. * * @return the default hostname verifier. */ public static HostnameVerifier getDefaultHostnameVerifier() { return NoPreloadHolder.defaultHostnameVerifier; } /** * Sets the default SSL socket factory to be used by new instances. * * @param sf * the new default SSL socket factory. * @throws IllegalArgumentException * if the specified socket factory is {@code null}. */ public static void setDefaultSSLSocketFactory(SSLSocketFactory sf) { if (sf == null) { throw new IllegalArgumentException("SSLSocketFactory is null"); } NoPreloadHolder.defaultSSLSocketFactory = sf; } /** * Returns the default SSL socket factory for new instances. * * @return the default SSL socket factory for new instances. */ public static SSLSocketFactory getDefaultSSLSocketFactory() { return NoPreloadHolder.defaultSSLSocketFactory; } /** * The host name verifier used by this connection. It is initialized from * the default hostname verifier * {@link #setDefaultHostnameVerifier(HostnameVerifier)} or * {@link #getDefaultHostnameVerifier()}. */ protected HostnameVerifier hostnameVerifier; private SSLSocketFactory sslSocketFactory; /** * Creates a new {@code HttpsURLConnection} with the specified {@code URL}. * * @param url * the {@code URL} to connect to. */ protected HttpsURLConnection(URL url) { super(url); hostnameVerifier = NoPreloadHolder.defaultHostnameVerifier; sslSocketFactory = NoPreloadHolder.defaultSSLSocketFactory; } /** * Returns the name of the cipher suite negotiated during the SSL handshake. * * @return the name of the cipher suite negotiated during the SSL handshake. * @throws IllegalStateException * if no connection has been established yet. */ public abstract String getCipherSuite(); /** * Returns the list of local certificates used during the handshake. These * certificates were sent to the peer. * * @return Returns the list of certificates used during the handshake with * the local identity certificate followed by CAs, or {@code null} * if no certificates were used during the handshake. * @throws IllegalStateException * if no connection has been established yet. */ public abstract Certificate[] getLocalCertificates(); /** * Return the list of certificates identifying the peer during the * handshake. * * @return the list of certificates identifying the peer with the peer's * identity certificate followed by CAs. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified.. * @throws IllegalStateException * if no connection has been established yet. */ public abstract Certificate[] getServerCertificates() throws SSLPeerUnverifiedException; /** * Returns the {@code Principal} identifying the peer. * * @return the {@code Principal} identifying the peer. * @throws SSLPeerUnverifiedException * if the identity of the peer has not been verified. * @throws IllegalStateException * if no connection has been established yet. */ public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { Certificate[] certs = getServerCertificates(); if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) { throw new SSLPeerUnverifiedException("No server's end-entity certificate"); } return ((X509Certificate) certs[0]).getSubjectX500Principal(); } /** * Returns the {@code Principal} used to identify the local host during the handshake. * * @return the {@code Principal} used to identify the local host during the handshake, or * {@code null} if none was used. * @throws IllegalStateException * if no connection has been established yet. */ public Principal getLocalPrincipal() { Certificate[] certs = getLocalCertificates(); if (certs == null || certs.length == 0 || (!(certs[0] instanceof X509Certificate))) { return null; } return ((X509Certificate) certs[0]).getSubjectX500Principal(); } /** * Sets the hostname verifier for this instance. * * @param v * the hostname verifier for this instance. * @throws IllegalArgumentException * if the specified verifier is {@code null}. */ public void setHostnameVerifier(HostnameVerifier v) { if (v == null) { throw new IllegalArgumentException("HostnameVerifier is null"); } hostnameVerifier = v; } /** * Returns the hostname verifier used by this instance. * * @return the hostname verifier used by this instance. */ public HostnameVerifier getHostnameVerifier() { return hostnameVerifier; } /** * Sets the SSL socket factory for this instance. * * @param sf * the SSL socket factory to be used by this instance. * @throws IllegalArgumentException * if the specified socket factory is {@code null}. */ public void setSSLSocketFactory(SSLSocketFactory sf) { if (sf == null) { throw new IllegalArgumentException("SSLSocketFactory is null"); } sslSocketFactory = sf; } /** * Returns the SSL socket factory used by this instance. * * @return the SSL socket factory used by this instance. */ public SSLSocketFactory getSSLSocketFactory() { return sslSocketFactory; } // Create a compile-time link on the https stream handler, which is loaded dynamically by // URL.java. private static final Class<?> unused = IosHttpsHandler.class; }