summaryrefslogtreecommitdiff
path: root/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java166
1 files changed, 166 insertions, 0 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
new file mode 100644
index 00000000..1bf679f8
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/OkHttpClientGenerator.java
@@ -0,0 +1,166 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient;
+
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Build;
+import android.support.annotation.NonNull;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import okhttp3.CipherSuite;
+import okhttp3.ConnectionSpec;
+import okhttp3.Cookie;
+import okhttp3.CookieJar;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.TlsVersion;
+
+import static android.text.TextUtils.isEmpty;
+import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.R.string.certificate_error;
+import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
+import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
+import static se.leap.bitmaskclient.R.string.keyChainAccessError;
+import static se.leap.bitmaskclient.R.string.server_unreachable_message;
+
+/**
+ * Created by cyberta on 08.01.18.
+ */
+
+public class OkHttpClientGenerator {
+
+ SharedPreferences preferences;
+ Resources resources;
+
+ public OkHttpClientGenerator(SharedPreferences preferences, Resources resources) {
+ this.preferences = preferences;
+ this.resources = resources;
+ }
+
+ public OkHttpClient initCommercialCAHttpClient(JSONObject initError) {
+ return initHttpClient(initError, null);
+ }
+
+ public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError) {
+ String certificate = preferences.getString(Provider.CA_CERT, "");
+ return initHttpClient(initError, certificate);
+ }
+
+ public OkHttpClient initSelfSignedCAHttpClient(JSONObject initError, String certificate) {
+ return initHttpClient(initError, certificate);
+ }
+
+
+ private OkHttpClient initHttpClient(JSONObject initError, String certificate) {
+ try {
+ TLSCompatSocketFactory sslCompatFactory;
+ ConnectionSpec spec = getConnectionSpec();
+ OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
+
+ if (!isEmpty(certificate)) {
+ sslCompatFactory = new TLSCompatSocketFactory(certificate);
+ } else {
+ sslCompatFactory = new TLSCompatSocketFactory();
+ }
+ sslCompatFactory.initSSLSocketFactory(clientBuilder);
+ clientBuilder.cookieJar(getCookieJar())
+ .connectionSpecs(Collections.singletonList(spec));
+ return clientBuilder.build();
+ } catch (IllegalArgumentException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, resources.getString(R.string.certificate_error));
+ } catch (IllegalStateException | KeyManagementException | KeyStoreException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, String.format(resources.getString(keyChainAccessError), e.getLocalizedMessage()));
+ } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, resources.getString(error_no_such_algorithm_exception_user_message));
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, resources.getString(certificate_error));
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, resources.getString(server_unreachable_message));
+ } catch (IOException e) {
+ e.printStackTrace();
+ addErrorMessageToJson(initError, resources.getString(error_io_exception_user_message));
+ }
+ return null;
+ }
+
+
+
+ @NonNull
+ private ConnectionSpec getConnectionSpec() {
+ ConnectionSpec.Builder connectionSpecbuilder = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
+ .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_3);
+ //FIXME: restrict connection further to the following recommended cipher suites for ALL supported API levels
+ //figure out how to use bcjsse for that purpose
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
+ connectionSpecbuilder.cipherSuites(
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+ CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+ CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ );
+ return connectionSpecbuilder.build();
+ }
+
+ @NonNull
+ private CookieJar getCookieJar() {
+ return new CookieJar() {
+ private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();
+
+ @Override
+ public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
+ cookieStore.put(url.host(), cookies);
+ }
+
+ @Override
+ public List<Cookie> loadForRequest(HttpUrl url) {
+ List<Cookie> cookies = cookieStore.get(url.host());
+ return cookies != null ? cookies : new ArrayList<Cookie>();
+ }
+ };
+ }
+
+ private void addErrorMessageToJson(JSONObject jsonObject, String errorMessage) {
+ try {
+ jsonObject.put(ERRORS, errorMessage);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+}