diff --git a/app/build.gradle b/app/build.gradle
index 6db759b480b9f563efb2c7c8a864e037a84fdbb1..07cf4ca58f58beb5f6491a1cd8834e31d90bff3e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -426,7 +426,9 @@ dependencies {
   compileOnly 'com.squareup.dagger:dagger-compiler:1.2.2'
   implementation 'com.github.pedrovgs:renderers:1.5'
   implementation 'com.google.code.gson:gson:2.8.6'
-  implementation 'com.squareup.okhttp3:okhttp:3.12.12'
+  implementation 'com.squareup.okhttp3:okhttp:4.10.0'
+  implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.10.0'
+  implementation 'org.conscrypt:conscrypt-android:2.5.2'
   implementation 'androidx.legacy:legacy-support-core-utils:1.0.0'
   implementation 'androidx.annotation:annotation:1.4.0'
   implementation 'androidx.legacy:legacy-support-v4:1.0.0'
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
index 828ef27d64643a5d91819b20e7d7f4955c1817c1..0ccef0ae884694191c2903d40d7dc4846ced484b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
@@ -34,6 +34,10 @@ import androidx.appcompat.app.AppCompatDelegate;
 import androidx.localbroadcastmanager.content.LocalBroadcastManager;
 import androidx.multidex.MultiDexApplication;
 
+import org.conscrypt.Conscrypt;
+
+import java.security.Security;
+
 import se.leap.bitmaskclient.BuildConfig;
 import se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver;
 import se.leap.bitmaskclient.base.models.ProviderObservable;
@@ -59,6 +63,7 @@ public class BitmaskApp extends MultiDexApplication {
         super.onCreate();
         // Normal app init code...*/
         PRNGFixes.apply();
+        Security.insertProviderAt(Conscrypt.newProvider(), 1);
         SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
         providerObservable = ProviderObservable.getInstance();
         providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences));
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index d65f6b52d8c5aef0bd4bbc8ce80064bf32f1f37d..2412efdd6115d4e91d7d912bc38406f7b456701a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -277,7 +277,7 @@ public class ConfigHelper {
     }
 
     public static String getDomainFromMainURL(@NonNull String mainUrl) throws NullPointerException {
-        return PublicSuffixDatabase.get().getEffectiveTldPlusOne(mainUrl).replaceFirst("http[s]?://", "").replaceFirst("/.*", "");
+        return PublicSuffixDatabase.Companion.get().getEffectiveTldPlusOne(mainUrl).replaceFirst("http[s]?://", "").replaceFirst("/.*", "");
     }
     
     public static boolean isCalyxOSWithTetheringSupport(Context context) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
index 5655e7b73f36b7873723dafdacea1022829e73f4..e8249692266215de39a9dd51b27924639dc7615a 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
@@ -1,5 +1,9 @@
 package se.leap.bitmaskclient.providersetup.connectivity;
 
+import static java.net.InetAddress.getByName;
+
+import android.util.Log;
+
 import androidx.annotation.NonNull;
 
 import java.net.InetAddress;
@@ -9,34 +13,109 @@ import java.util.List;
 
 import de.blinkt.openvpn.core.VpnStatus;
 import okhttp3.Dns;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.dnsoverhttps.DnsOverHttps;
 import se.leap.bitmaskclient.base.models.Provider;
 import se.leap.bitmaskclient.base.models.ProviderObservable;
 import se.leap.bitmaskclient.base.utils.IPAddress;
 
-class DnsResolver implements Dns {
+public class DnsResolver implements Dns {
+    OkHttpClient dohHttpClient;
+    boolean preferDoH;
+
+    public DnsResolver(OkHttpClient dohHttpClient, boolean preferDoH) {
+        this.dohHttpClient = dohHttpClient;
+        this.preferDoH = preferDoH;
+    }
 
+    @NonNull
     @Override
     public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
+        Log.d("DNS", "trying to resolve DNS for " + hostname);
+        List<InetAddress> list = null;
+        if (preferDoH) {
+            if ((list = tryLookupDoH(hostname)) == null) {
+                list  = tryLookupSystemDNS(hostname);
+            }
+        } else {
+            if ((list = tryLookupSystemDNS(hostname)) == null) {
+                list = tryLookupDoH(hostname);
+            }
+        }
+
+        if (list != null) {
+            return list;
+        }
+
+        Log.d("DNS", "try hard coded IPs");
+        // let's check if there's an hard-coded IP we can use
+        ProviderObservable observable = ProviderObservable.getInstance();
+        Provider currentProvider;
+        if (observable.getProviderForDns() != null) {
+            currentProvider = observable.getProviderForDns();
+        } else {
+            currentProvider = observable.getCurrentProvider();
+        }
+        String ip = currentProvider.getIpForHostname(hostname);
+        if (!ip.isEmpty()) {
+            VpnStatus.logWarning("[API] Normal DNS resolution for " + hostname + " seems to be blocked. Circumventing.");
+            ArrayList<InetAddress> addresses = new ArrayList<>();
+            addresses.add(InetAddress.getByAddress(hostname, IPAddress.asBytes(ip)));
+            return addresses;
+        } else {
+            VpnStatus.logWarning("[API] Could not resolve DNS for " + hostname);
+            throw new UnknownHostException("Hostname " + hostname + " not found");
+        }
+    }
+
+    private List<InetAddress> tryLookupSystemDNS(@NonNull String hostname) throws RuntimeException, UnknownHostException {
         try {
+            Log.d("DNS", "trying to resolve " + hostname + "with system DNS");
             return Dns.SYSTEM.lookup(hostname);
         } catch (UnknownHostException e) {
-            ProviderObservable observable = ProviderObservable.getInstance();
-            Provider currentProvider;
-            if (observable.getProviderForDns() != null) {
-                currentProvider = observable.getProviderForDns();
-            } else {
-                currentProvider = observable.getCurrentProvider();
-            }
-            String ip = currentProvider.getIpForHostname(hostname);
-            if (!ip.isEmpty()) {
-                VpnStatus.logWarning("[API] Normal DNS resolution for " + hostname + " seems to be blocked. Circumventing.");
-                ArrayList<InetAddress> addresses = new ArrayList<>();
-                addresses.add(InetAddress.getByAddress(hostname, IPAddress.asBytes(ip)));
-                return addresses;
-            } else {
-                VpnStatus.logWarning("[API] Could not resolve DNS for " + hostname);
-                throw new UnknownHostException("Hostname " + hostname + " not found");
-            }
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private List<InetAddress> tryLookupDoH(@NonNull String hostname) throws UnknownHostException {
+        DnsOverHttps njallaDoH = new DnsOverHttps.Builder().client(dohHttpClient)
+                .url(HttpUrl.get("https://dns.njal.la/dns-query"))
+                .bootstrapDnsHosts(getByName("95.215.19.53"), getByName("2001:67c:2354:2::53"))
+                .build();
+        try {
+            Log.d("DNS", "DoH via dns.njal.la");
+            return njallaDoH.lookup(hostname);
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+            Log.e("DNS", "DoH via dns.njal.la failed");
+        }
+
+        DnsOverHttps quad9 = new DnsOverHttps.Builder().client(dohHttpClient)
+                .url(HttpUrl.get("https://dns.quad9.net/dns-query"))
+                .bootstrapDnsHosts(getByName("9.9.9.9"), getByName("149.112.112.112"), getByName("2620:fe::fe"), getByName("2620:fe::9"))
+                .build();
+        try {
+            Log.d("DNS", "DoH via dns.quad9.net");
+            return quad9.lookup(hostname);
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+            Log.e("DNS", "DoH via dns.quad9.net failed");
+        }
+
+        DnsOverHttps cloudFlareDoHClient = new DnsOverHttps.Builder().client(dohHttpClient)
+                .url(HttpUrl.get("https://1.1.1.1/dns-query"))
+                .bootstrapDnsHosts(getByName("1.1.1.1"), getByName("1.0.0.1"))
+                .build();
+
+        try {
+            Log.d("DNS", "DoH via cloudflare 1.1.1.1");
+            return cloudFlareDoHClient.lookup(hostname);
+        } catch (UnknownHostException e) {
+            e.printStackTrace();
+            Log.e("DNS", "DoH via cloudflare failed");
         }
+        return null;
     }
 }
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
index ea6192636036ef4d15715cfe7541adf0309ea984..973935515b35ac5be5878f34fc1a28a9f390131b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
@@ -17,8 +17,16 @@
 
 package se.leap.bitmaskclient.providersetup.connectivity;
 
+import static android.text.TextUtils.isEmpty;
+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;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+
 import android.content.res.Resources;
-import android.net.LocalSocketAddress;
 import android.os.Build;
 
 import androidx.annotation.NonNull;
@@ -29,7 +37,6 @@ import org.json.JSONObject;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.Proxy;
-import java.net.SocketAddress;
 import java.net.UnknownHostException;
 import java.security.KeyManagementException;
 import java.security.KeyStoreException;
@@ -49,16 +56,6 @@ import okhttp3.HttpUrl;
 import okhttp3.OkHttpClient;
 import okhttp3.TlsVersion;
 
-import static android.text.TextUtils.isEmpty;
-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.proxy;
-import static se.leap.bitmaskclient.R.string.server_unreachable_message;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
-
 /**
  * Created by cyberta on 08.01.18.
  */
@@ -68,7 +65,7 @@ public class OkHttpClientGenerator {
     Resources resources;
     private final static String PROXY_HOST = "127.0.0.1";
 
-    public OkHttpClientGenerator(/*SharedPreferences preferences,*/ Resources resources) {
+    public OkHttpClientGenerator(Resources resources) {
         this.resources = resources;
     }
 
@@ -133,13 +130,15 @@ public class OkHttpClientGenerator {
         } else {
             sslCompatFactory = new TLSCompatSocketFactory();
         }
-        sslCompatFactory.initSSLSocketFactory(clientBuilder);
         clientBuilder.cookieJar(getCookieJar())
                 .connectionSpecs(Collections.singletonList(spec));
-        clientBuilder.dns(new DnsResolver());
+
         if (proxyPort != -1) {
             clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, proxyPort)));
         }
+
+        clientBuilder.dns(new DnsResolver(clientBuilder.build(), true));
+        sslCompatFactory.initSSLSocketFactory(clientBuilder);
         return clientBuilder.build();
     }
 
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
index cc68b5a843973291f3a9ea8c722073560c4f93df..1420d6661ac174cc1bcb96732b7b5aa071d2dccf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
@@ -28,8 +28,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
 
 /**
  * Created by cyberta on 24.10.17.
- * This class ensures that modern TLS algorithms will also be used on old devices (Android 4.1 - Android 4.4.4) in order to avoid
- * attacks like POODLE.
+ * This class ensures that modern TLS algorithms will also be used on old devices
  */
 
 public class TLSCompatSocketFactory extends SSLSocketFactory {
@@ -150,9 +149,8 @@ public class TLSCompatSocketFactory extends SSLSocketFactory {
     }
 
     private Socket enableTLSOnSocket(Socket socket) throws IllegalArgumentException {
-        if(socket != null && (socket instanceof SSLSocket)) {
-            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
-            //TODO: add a android version check as soon as a new Android API or bcjsse supports TLSv1.3
+        if((socket instanceof SSLSocket)) {
+            ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.3", "TLSv1.2"});
         }
         return socket;
 
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index d30e8b7ed6ecc3b93720b347a26b8bb6c33b1517..c272970d7e8be482d3c9bf0041538dc94461fed7 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -74,6 +74,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
 import se.leap.bitmaskclient.base.utils.FileHelper;
 import se.leap.bitmaskclient.base.utils.InputStreamHelper;
 import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.providersetup.connectivity.DnsResolver;
 import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
 import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
 import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
@@ -577,13 +578,14 @@ public class MockHelper {
 
     public static OkHttpClientGenerator mockClientGenerator(boolean resolveDNS) throws UnknownHostException {
         OkHttpClientGenerator mockClientGenerator = mock(OkHttpClientGenerator.class);
-        OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class, RETURNS_DEEP_STUBS);
+        OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class);
+        DnsResolver mockedDnsResolver = mock(DnsResolver.class);
         when(mockClientGenerator.initCommercialCAHttpClient(any(JSONObject.class), anyInt())).thenReturn(mockedOkHttpClient);
         when(mockClientGenerator.initSelfSignedCAHttpClient(anyString(), anyInt(), any(JSONObject.class))).thenReturn(mockedOkHttpClient);
         if (resolveDNS) {
-            when(mockedOkHttpClient.dns().lookup(anyString())).thenReturn(new ArrayList<>());
+            when(mockedDnsResolver.lookup(anyString())).thenReturn(new ArrayList<>());
         } else {
-            when(mockedOkHttpClient.dns().lookup(anyString())).thenThrow(new UnknownHostException());
+            when(mockedDnsResolver.lookup(anyString())).thenThrow(new UnknownHostException());
         }
         return mockClientGenerator;
     }