Skip to content

Commit

Permalink
Add the ISRG X1 certificate to the trust store used by OkHttp
Browse files Browse the repository at this point in the history
This allows connections to sites using letsencrypt certificates to
continue to work for now on pre Android 7.1 devices. In particular this
affects the OSM API.

It is likely that this fix increases memory usage by multiple MBs.

Note: this does not solve the issue for things that do not use OkHttp,
for example ACRA.

Resolves #2556

Resolves #1277
  • Loading branch information
simonpoole committed Jun 12, 2024
1 parent 615ebd4 commit b9f2199
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 4 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ dependencies {
implementation 'se.akerfeldt:okhttp-signpost:1.1.0'
implementation "com.squareup.okhttp3:okhttp:$okHttpVersion"
implementation "com.squareup.okhttp3:logging-interceptor:$okHttpVersion"
implementation "com.squareup.okhttp3:okhttp-tls:$okHttpVersion"

// other 3rd party dependencies
implementation 'com.heinrichreimersoftware:android-issue-reporter:1.4'
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/de/blau/android/App.java
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public static Resources resources() {
public static OkHttpClient getHttpClient() {
synchronized (httpClientLock) {
if (httpClient == null) {
OkHttpClient.Builder builder = OkHttpTlsCompat.enableTls12IfNeeded(new OkHttpClient.Builder());
OkHttpClient.Builder builder = OkHttpTlsCompat.getBuilder(new OkHttpClient.Builder());
builder.addNetworkInterceptor(new UserAgentInterceptor(userAgent));
httpClient = builder.build();
}
Expand Down
70 changes: 67 additions & 3 deletions src/main/java/de/blau/android/net/OkHttpTlsCompat.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package de.blau.android.net;

import static de.blau.android.contract.Constants.LOG_TAG_LEN;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand All @@ -20,13 +27,17 @@
import androidx.annotation.NonNull;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import okhttp3.tls.HandshakeCertificates;
import okhttp3.TlsVersion;

/**
* ¨ Nicked from https://github.com/slapperwan/gh4a/commit/985cc0459910bd8452db7e83e4427f01623d11d8
*/
public final class OkHttpTlsCompat {

private static final int TAG_LEN = Math.min(LOG_TAG_LEN, OkHttpTlsCompat.class.getSimpleName().length());
private static final String DEBUG_TAG = OkHttpTlsCompat.class.getSimpleName().substring(0, TAG_LEN);

/**
* Private constructor to stop instantiation
*/
Expand All @@ -35,12 +46,12 @@ private OkHttpTlsCompat() {
}

/**
* Add TLS 1.2 support for older Android versions
* Add TLS 1.2 support and ISRG X1 cert for letsencrypt for older for older Android versions
*
* @param builder an OkHttpClient.Builder instance
* @return the builder with TLS1.2 added if necessary
*/
public static OkHttpClient.Builder enableTls12IfNeeded(OkHttpClient.Builder builder) {
public static OkHttpClient.Builder getBuilder(OkHttpClient.Builder builder) {
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
try {
SSLContext sc = SSLContext.getInstance("TLSv1.2");
Expand All @@ -64,9 +75,62 @@ public static OkHttpClient.Builder enableTls12IfNeeded(OkHttpClient.Builder buil
builder.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()), tm);
builder.connectionSpecs(specs);
} catch (Exception exc) {
Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
Log.e(DEBUG_TAG, "Error while setting TLS 1.2", exc);
}
}
// Add ISRG X1 cert for letsencrypt on older devices (pre and including 7.1)
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
// download fresh from https://letsencrypt.org/certs/isrgrootx1.pem
//@formatter:off
String isgCert =
"-----BEGIN CERTIFICATE-----\n"
+ "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\n"
+ "-----END CERTIFICATE-----";
//@formatter:on
try {
CertificateFactory cf = CertificateFactory.getInstance("X.509");

java.security.cert.Certificate isgCertificate = cf.generateCertificate(new ByteArrayInputStream(isgCert.getBytes("UTF-8")));

HandshakeCertificates certificates = new HandshakeCertificates.Builder().addTrustedCertificate((X509Certificate) isgCertificate)
// Uncomment to allow connection to any site generally, but could possibly cause
// noticeable memory pressure in Android apps.
.addPlatformTrustedCertificates().build();

builder.sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager());
} catch (CertificateException | UnsupportedEncodingException e) {
Log.e(DEBUG_TAG, "Error adding ISRG X1 cert connections to letsencrypt secured servers will not work", e);
}
}

return builder;
}

Expand Down

0 comments on commit b9f2199

Please sign in to comment.