info.guardianproject.netcipher.NetCipher.java Source code

Java tutorial

Introduction

Here is the source code for info.guardianproject.netcipher.NetCipher.java

Source

/*
 * Copyright 2014-2016 Hans-Christoph Steiner
 * Copyright 2012-2016 Nathan Freitas
 *
 * Licensed 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 info.guardianproject.netcipher;

import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

import info.guardianproject.netcipher.client.TlsOnlySocketFactory;
import info.guardianproject.netcipher.proxy.OrbotHelper;

public class NetCipher {
    private static final String TAG = "NetCipher";

    private NetCipher() {
        // this is a utility class with only static methods
    }

    public final static Proxy ORBOT_HTTP_PROXY = new Proxy(Proxy.Type.HTTP,
            new InetSocketAddress("127.0.0.1", 8118));

    private static Proxy proxy;

    /**
     * Set the global HTTP proxy for all new {@link HttpURLConnection}s and
     * {@link HttpsURLConnection}s that are created after this is called.
     * <p>
     * {@link #useTor()} will override this setting.  Traffic must be directed
     * to Tor using the proxy settings, and Orbot has its own proxy settings
     * for connections that need proxies to work.  So if "use Tor" is enabled,
     * as tested by looking for the static instance of Proxy, then no other
     * proxy settings are allowed to override the current Tor proxy.
     *
     * @param host the IP address for the HTTP proxy to use globally
     * @param port the port number for the HTTP proxy to use globally
     */
    public static void setProxy(String host, int port) {
        if (!TextUtils.isEmpty(host) && port > 0) {
            InetSocketAddress isa = new InetSocketAddress(host, port);
            setProxy(new Proxy(Proxy.Type.HTTP, isa));
        } else if (NetCipher.proxy != ORBOT_HTTP_PROXY) {
            setProxy(null);
        }
    }

    /**
     * Set the global HTTP proxy for all new {@link HttpURLConnection}s and
     * {@link HttpsURLConnection}s that are created after this is called.
     * <p>
     * {@link #useTor()} will override this setting.  Traffic must be directed
     * to Tor using the proxy settings, and Orbot has its own proxy settings
     * for connections that need proxies to work.  So if "use Tor" is enabled,
     * as tested by looking for the static instance of Proxy, then no other
     * proxy settings are allowed to override the current Tor proxy.
     *
     * @param proxy the HTTP proxy to use globally
     */
    public static void setProxy(Proxy proxy) {
        if (proxy != null && NetCipher.proxy == ORBOT_HTTP_PROXY) {
            Log.w(TAG, "useTor is enabled, ignoring new proxy settings!");
        } else {
            NetCipher.proxy = proxy;
        }
    }

    /**
     * Get the currently active global HTTP {@link Proxy}.
     *
     * @return the active HTTP {@link Proxy}
     */
    public static Proxy getProxy() {
        return proxy;
    }

    /**
     * Clear the global HTTP proxy for all new {@link HttpURLConnection}s and
     * {@link HttpsURLConnection}s that are created after this is called. This
     * returns things to the default, proxy-less state.
     */
    public static void clearProxy() {
        setProxy(null);
    }

    /**
     * Set Orbot as the global HTTP proxy for all new {@link HttpURLConnection}
     * s and {@link HttpsURLConnection}s that are created after this is called.
     * This overrides all future calls to {@link #setProxy(Proxy)}, except to
     * clear the proxy, e.g. {@code #setProxy(null)} or {@link #clearProxy()}.
     * <p>
     * Traffic must be directed to Tor using the proxy settings, and Orbot has its
     * own proxy settings for connections that need proxies to work.  So if "use
     * Tor" is enabled, as tested by looking for the static instance of Proxy,
     * then no other proxy settings are allowed to override the current Tor proxy.
     */
    public static void useTor() {
        setProxy(ORBOT_HTTP_PROXY);
    }

    /**
     * Get a {@link TlsOnlySocketFactory} from NetCipher.
     *
     * @see HttpsURLConnection#setDefaultSSLSocketFactory(SSLSocketFactory)
     */
    public static TlsOnlySocketFactory getTlsOnlySocketFactory() {
        return getTlsOnlySocketFactory(false);
    }

    /**
     * Get a {@link TlsOnlySocketFactory} from NetCipher, and specify whether
     * it should use a more compatible, but less strong, suite of ciphers.
     *
     * @see HttpsURLConnection#setDefaultSSLSocketFactory(SSLSocketFactory)
     */
    public static TlsOnlySocketFactory getTlsOnlySocketFactory(boolean compatible) {
        SSLContext sslcontext;
        try {
            sslcontext = SSLContext.getInstance("TLSv1");
            sslcontext.init(null, null, null);
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        } catch (KeyManagementException e) {
            throw new IllegalArgumentException(e);
        }
        return new TlsOnlySocketFactory(sslcontext.getSocketFactory(), compatible);
    }

    /**
     * Get a {@link HttpURLConnection} from a {@link URL}, and specify whether
     * it should use a more compatible, but less strong, suite of ciphers.
     *
     * @param url
     * @param compatible
     * @return the {@code url} in an instance of {@link HttpURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getHttpURLConnection(URL url, boolean compatible) throws IOException {
        // .onion addresses only work via Tor, so force Tor for all of them
        Proxy proxy = NetCipher.proxy;
        if (OrbotHelper.isOnionAddress(url))
            proxy = ORBOT_HTTP_PROXY;

        HttpURLConnection connection;
        if (proxy != null) {
            connection = (HttpURLConnection) url.openConnection(proxy);
        } else {
            connection = (HttpURLConnection) url.openConnection();
        }

        if (connection instanceof HttpsURLConnection) {
            HttpsURLConnection httpsConnection = ((HttpsURLConnection) connection);
            SSLSocketFactory tlsOnly = getTlsOnlySocketFactory(compatible);
            httpsConnection.setSSLSocketFactory(tlsOnly);
            if (Build.VERSION.SDK_INT < 16) {
                httpsConnection
                        .setHostnameVerifier(org.apache.http.conn.ssl.SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
            }
        }
        return connection;
    }

    /**
     * Get a {@link HttpsURLConnection} from a URL {@link String} using the best
     * TLS configuration available on the device.
     *
     * @param urlString
     * @return the URL in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getHttpsURLConnection(String urlString) throws IOException {
        URL url = new URL(urlString.replaceFirst("^[Hh][Tt][Tt][Pp]:", "https:"));
        return getHttpsURLConnection(url, false);
    }

    /**
     * Get a {@link HttpsURLConnection} from a {@link Uri} using the best TLS
     * configuration available on the device.
     *
     * @param uri
     * @return the {@code uri} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getHttpsURLConnection(Uri uri) throws IOException {
        return getHttpsURLConnection(uri.toString());
    }

    /**
     * Get a {@link HttpsURLConnection} from a {@link URI} using the best TLS
     * configuration available on the device.
     *
     * @param uri
     * @return the {@code uri} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getHttpsURLConnection(URI uri) throws IOException {
        if (TextUtils.equals(uri.getScheme(), "https"))
            return getHttpsURLConnection(uri.toURL(), false);
        else
            // otherwise force scheme to https
            return getHttpsURLConnection(uri.toString());
    }

    /**
     * Get a {@link HttpsURLConnection} from a {@link URL} using the best TLS
     * configuration available on the device.
     *
     * @param url
     * @return the {@code url} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getHttpsURLConnection(URL url) throws IOException {
        return getHttpsURLConnection(url, false);
    }

    /**
     * Get a {@link HttpsURLConnection} from a {@link URL} using a more
     * compatible, but less strong, suite of ciphers.
     *
     * @param url
     * @return the {@code url} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getCompatibleHttpsURLConnection(URL url) throws IOException {
        return getHttpsURLConnection(url, true);
    }

    /**
     * Get a {@link HttpsURLConnection} from a {@link URL}, and specify whether
     * it should use a more compatible, but less strong, suite of ciphers.
     *
     * @param url
     * @param compatible
     * @return the {@code url} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect,
     *                                  or if an HTTP URL is given that does not support HTTPS
     */
    public static HttpsURLConnection getHttpsURLConnection(URL url, boolean compatible) throws IOException {
        // use default method, but enforce a HttpsURLConnection
        HttpURLConnection connection = getHttpURLConnection(url, compatible);
        if (connection instanceof HttpsURLConnection) {
            return (HttpsURLConnection) connection;
        } else {
            throw new IllegalArgumentException("not an HTTPS connection!");
        }
    }

    /**
     * Get a {@link HttpURLConnection} from a {@link URL}. If the connection is
     * {@code https://}, it will use a more compatible, but less strong, TLS
     * configuration.
     *
     * @param url
     * @return the {@code url} in an instance of {@link HttpsURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getCompatibleHttpURLConnection(URL url) throws IOException {
        return getHttpURLConnection(url, true);
    }

    /**
     * Get a {@link HttpURLConnection} from a URL {@link String}. If it is an
     * {@code https://} link, then this will use the best TLS configuration
     * available on the device.
     *
     * @param urlString
     * @return the URL in an instance of {@link HttpURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getHttpURLConnection(String urlString) throws IOException {
        return getHttpURLConnection(new URL(urlString));
    }

    /**
     * Get a {@link HttpURLConnection} from a {@link Uri}. If it is an
     * {@code https://} link, then this will use the best TLS configuration
     * available on the device.
     *
     * @param uri
     * @return the {@code uri} in an instance of {@link HttpURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getHttpURLConnection(Uri uri) throws IOException {
        return getHttpURLConnection(uri.toString());
    }

    /**
     * Get a {@link HttpURLConnection} from a {@link URI}. If it is an
     * {@code https://} link, then this will use the best TLS configuration
     * available on the device.
     *
     * @param uri
     * @return the {@code uri} in an instance of {@link HttpURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getHttpURLConnection(URI uri) throws IOException {
        return getHttpURLConnection(uri.toURL());
    }

    /**
     * Get a {@link HttpURLConnection} from a {@link URL}. If it is an
     * {@code https://} link, then this will use the best TLS configuration
     * available on the device.
     *
     * @param url
     * @return the {@code url} in an instance of {@link HttpURLConnection}
     * @throws IOException
     * @throws IllegalArgumentException if the proxy or TLS setup is incorrect
     */
    public static HttpURLConnection getHttpURLConnection(URL url) throws IOException {
        return (HttpURLConnection) getHttpURLConnection(url, false);
    }
}