com.apothesource.pillfill.network.PFNetworkManager.java Source code

Java tutorial

Introduction

Here is the source code for com.apothesource.pillfill.network.PFNetworkManager.java

Source

/*
 *
 *  * The MIT License
 *  *
 *  * Copyright {$YEAR} Apothesource, Inc.
 *  *
 *  * Permission is hereby granted, free of charge, to any person obtaining a copy
 *  * of this software and associated documentation files (the "Software"), to deal
 *  * in the Software without restriction, including without limitation the rights
 *  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  * copies of the Software, and to permit persons to whom the Software is
 *  * furnished to do so, subject to the following conditions:
 *  *
 *  * The above copyright notice and this permission notice shall be included in
 *  * all copies or substantial portions of the Software.
 *  *
 *  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  * THE SOFTWARE.
 *
 */
package com.apothesource.pillfill.network;

import com.apothesource.pillfill.utilites.ResourceUtil;
import com.squareup.okhttp.*;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.nio.file.Files;
import java.security.GeneralSecurityException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;

import timber.log.Timber;

public class PFNetworkManager {

    public static final int DEFAULT_CACHE_SIZE = 100 * 1024 * 1024; //100MB
    private static OkHttpClient pfPinnedClient;
    private static OkHttpClient standardClient;
    private static boolean isHttpClientInit = false;

    public static OkHttpClient getPinnedPFHttpClient(boolean allowWeakCiphers) {
        if (!isHttpClientInit) {
            initHttpClient(allowWeakCiphers);
        }
        return pfPinnedClient;
    }

    public static OkHttpClient getStandardHttpClient() {
        if (!isHttpClientInit) {
            initHttpClient(false);
        }
        return standardClient;
    }

    private static Cache getDefaultCache() {
        try {
            return new Cache(Files.createTempDirectory("networkCache").toFile(), DEFAULT_CACHE_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static OkHttpClient getPinnedPFHttpClient() {
        return getPinnedPFHttpClient(false);
    }

    public static String doPinnedGetForUrl(String url) throws IOException {
        OkHttpClient client = getPinnedPFHttpClient();
        Request.Builder requestBuilder = new Request.Builder().url(url);
        Response response = client.newCall(requestBuilder.build()).execute();
        return response.body().string();
    }

    public static String doPinnedGetForUrl(String url, String authToken) throws IOException {
        OkHttpClient client = getPinnedPFHttpClient();
        Request.Builder requestBuilder = new Request.Builder().url(url);
        if (authToken != null)
            requestBuilder = requestBuilder.addHeader("Bearer", authToken);
        Response response = client.newCall(requestBuilder.build()).execute();
        return response.body().string();
    }

    public static HttpsURLConnection getPFHttpsURLConnection(String url) throws IOException {
        HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection();
        connection.setSSLSocketFactory(new PFSSLSocketFactory());
        return connection;
    }

    private static synchronized void initHttpClient(boolean allowWeakCiphers) {
        if (pfPinnedClient == null) {
            pfPinnedClient = new OkHttpClient();
            String apiKey = ResourceUtil.getInstance().getPFApiKey();
            if (apiKey == null) {
                throw new RuntimeException("API_KEY is not set.");
            }
            pfPinnedClient.setSslSocketFactory(new PFSSLSocketFactory(allowWeakCiphers));
            pfPinnedClient.setHostnameVerifier(PFSSLSocketFactory.getPfHostnameVerifier());
            pfPinnedClient.networkInterceptors().add(new ApiKeyInterceptor(apiKey));
            pfPinnedClient.setCache(getDefaultCache());
        }
        if (standardClient == null) {
            standardClient = new OkHttpClient();
            pfPinnedClient.setCache(getDefaultCache());
        }
        isHttpClientInit = true;
    }

}

class ApiKeyInterceptor implements Interceptor {

    private final String apiKey;

    public ApiKeyInterceptor(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request requestWithApiKey = request.newBuilder().addHeader("api_key", apiKey).build();
        return chain.proceed(requestWithApiKey);
    }
}

class PFSSLSocketFactory extends SSLSocketFactory {
    private static final String[] SSL_ENABLED_PROTOCOLS = new String[] { "TLSv1.2", "TLSv1.1" };

    private static final String[] SUPPORTED_CIPHER_SUITES = new String[] { "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
            "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
            "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" };
    private static final String[] PREFERRED_CIPHER_SUITES = new String[] { "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA",
            "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
            "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA",
            "TLS_DHE_RSA_WITH_AES_256_CBC_SHA" };

    private static final String PF_HOSTNAME_REGEX = ".*\\.pill[-]?fill.com";

    private static SSLContext tlsContext;

    private static HostnameVerifier pfHostnameVerifier = new HostnameVerifier() {
        @Override
        public boolean verify(String host, SSLSession session) {
            return checkHost(host);
        }

        private boolean checkHost(String host) {
            if (!host.matches(PF_HOSTNAME_REGEX)) {
                Timber.w("Rejecting access to non-PF url: %s", host);
                return false;
            }
            return true;
        }
    };

    public PFSSLSocketFactory(boolean allowWeakCiphers) {
        super();
        initSSLContext(allowWeakCiphers);
    }

    public PFSSLSocketFactory() {
        this(false);
    }

    public static HostnameVerifier getPfHostnameVerifier() {
        return pfHostnameVerifier;
    }

    private static void initSSLContext() {
        initSSLContext(false);
    }

    private static void initSSLContext(boolean allowWeakCiphers) {
        try {
            if (tlsContext == null) {
                PillFillTrustManager pftm = new PillFillTrustManager(allowWeakCiphers);
                tlsContext = SSLContext.getInstance("TLS");
                tlsContext.init(null, new TrustManager[] { pftm }, null);
            }
        } catch (GeneralSecurityException e) {
            Timber.e(e, "Error initializing SSLContext.");
            throw new RuntimeException(e);
        }
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return PREFERRED_CIPHER_SUITES;
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return SUPPORTED_CIPHER_SUITES;
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException {
        return tlsContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        SSLSocket sslSocket = (SSLSocket) tlsContext.getSocketFactory().createSocket();
        sslSocket.setEnabledProtocols(SSL_ENABLED_PROTOCOLS);
        return sslSocket;
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException {
        SSLSocket sslSocket = (SSLSocket) tlsContext.getSocketFactory().createSocket(host, port);
        sslSocket.setEnabledProtocols(SSL_ENABLED_PROTOCOLS);
        return sslSocket;
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
        SSLSocket sslSocket = (SSLSocket) tlsContext.getSocketFactory().createSocket(host, port, localHost,
                localPort);
        sslSocket.setEnabledProtocols(SSL_ENABLED_PROTOCOLS);
        return sslSocket;
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        SSLSocket sslSocket = (SSLSocket) tlsContext.getSocketFactory().createSocket(host, port);
        sslSocket.setEnabledProtocols(SSL_ENABLED_PROTOCOLS);
        return sslSocket;
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
            throws IOException {
        SSLSocket sslSocket = (SSLSocket) tlsContext.getSocketFactory().createSocket(address, port, localAddress,
                localPort);
        sslSocket.setEnabledProtocols(SSL_ENABLED_PROTOCOLS);
        return sslSocket;
    }
}