BraveNewPipeLegacy: move custom TrustManagerFactory creation into BraveTLSSocketFactory

As the custom TrustManagerFactory was only set correctly in BraveOkHttpTlsHelper
but not if setting the default SSLSocketFactory for HttpsURLConnection.

So the whole code for adding own CA's was moved into BraveTrustManagerFactoryHelper
and the BraveTLSSocketFactory handles now its usage internally and not via
constructor.
This commit is contained in:
evermind 2024-04-03 23:05:29 +02:00
parent 837ae586f2
commit 3df1047d91
4 changed files with 186 additions and 116 deletions

View file

@ -1,26 +1,14 @@
package org.schabi.newpipe.util; package org.schabi.newpipe.util;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.util.Log;
import org.schabi.newpipe.App; import org.schabi.newpipe.BraveTag;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager; import javax.net.ssl.X509TrustManager;
@ -30,6 +18,9 @@ import static org.schabi.newpipe.MainActivity.DEBUG;
public final class BraveOkHttpTlsHelper { public final class BraveOkHttpTlsHelper {
private static final String TAG =
new BraveTag().tagShort23(BraveOkHttpTlsHelper.class.getSimpleName());
private BraveOkHttpTlsHelper() { private BraveOkHttpTlsHelper() {
} }
@ -50,105 +41,23 @@ public final class BraveOkHttpTlsHelper {
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
try { try {
final KeyStore customCAsKeystore = createKeystoreWithCustomCAsAndSystemCAs(); final BraveTLSSocketFactory sslSocketFactory = BraveTLSSocketFactory.getInstance();
final TrustManagerFactory trustManagerFactory = final TrustManagerFactory trustManagerFactory = sslSocketFactory
getTrustManagerFactory(customCAsKeystore); .getTrustManagerFactory();
final SSLContext context = SSLContext.getInstance("TLS"); final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagerFactory.getTrustManagers(), null); context.init(null, trustManagerFactory.getTrustManagers(), null);
final SSLSocketFactory sslSocketFactory =
new BraveTLSSocketFactory(trustManagerFactory);
builder.sslSocketFactory(sslSocketFactory, builder.sslSocketFactory(sslSocketFactory,
(X509TrustManager) trustManagerFactory.getTrustManagers()[0]); (X509TrustManager) trustManagerFactory.getTrustManagers()[0]);
} catch (final KeyManagementException | NoSuchAlgorithmException | KeyStoreException } catch (final KeyManagementException | NoSuchAlgorithmException e) {
| IOException | CertificateException e) {
if (DEBUG) { if (DEBUG) {
e.printStackTrace(); e.printStackTrace();
Log.e(TAG, "Unable to insert own {SSLSocket,TrustManager}Factory in OkHttp", e);
} }
} }
} }
return builder; return builder;
} }
public static TrustManagerFactory getTrustManagerFactory(
final KeyStore keyStore)
throws NoSuchAlgorithmException, KeyStoreException {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Tell TrustManager to trust the CAs in our KeyStore
trustManagerFactory.init(keyStore);
// only allow one TrustManager
final TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return trustManagerFactory;
}
/**
* Add our trusted CAs for rumble.com and framatube.org to keystore.
*
* @return custom CA keystore with our added CAs
* @throws KeyStoreException
* @throws CertificateException
* @throws IOException
* @throws NoSuchAlgorithmException
*/
private static KeyStore createKeystoreWithCustomCAsAndSystemCAs()
throws KeyStoreException, CertificateException,
IOException, NoSuchAlgorithmException {
final List<String> rawCertFiles = Arrays.asList("ca_digicert_global_g2", "ca_lets_encrypt");
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (final String rawCertFile : rawCertFiles) {
final Certificate cert = readCertificateFromFile(rawCertFile);
keyStore.setCertificateEntry(rawCertFile, cert);
}
addSystemCAsToKeystore(keyStore);
return keyStore;
}
private static void addSystemCAsToKeystore(
final KeyStore keyStore) throws NoSuchAlgorithmException, KeyStoreException {
// Default TrustManager to get device trusted CA's
final TrustManagerFactory defaultTrustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
defaultTrustManagerFactory.init((KeyStore) null);
final X509TrustManager trustManager =
(X509TrustManager) defaultTrustManagerFactory.getTrustManagers()[0];
int idx = 0;
for (final Certificate cert : trustManager.getAcceptedIssuers()) {
keyStore.setCertificateEntry(Integer.toString(idx), cert);
idx++;
}
}
private static Certificate readCertificateFromFile(
final String rawFile)
throws IOException, CertificateException {
final Context context = App.getApp().getApplicationContext();
final InputStream inputStream = context.getResources().openRawResource(
context.getResources().getIdentifier(rawFile,
"raw", context.getPackageName()));
final byte[] rawBytes = new byte[inputStream.available()];
inputStream.read(rawBytes);
inputStream.close();
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
return cf.generateCertificate(new ByteArrayInputStream(rawBytes));
}
} }

View file

@ -1,5 +1,9 @@
package org.schabi.newpipe.util; package org.schabi.newpipe.util;
import android.util.Log;
import org.schabi.newpipe.BraveTag;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
@ -12,32 +16,27 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
import android.util.Log;
/** /**
* This is an extension of the SSLSocketFactory which enables TLS 1.2 and 1.1. * This is an extension of the SSLSocketFactory which enables TLS 1.2 and 1.3.
* Created for usage on Android 4.1-4.4 devices, which haven't enabled those by default. * Created for usage on Android 4.1-4.4 devices, which haven't enabled those by default.
*/ */
public class BraveTLSSocketFactory extends SSLSocketFactory { public final class BraveTLSSocketFactory extends SSLSocketFactory {
private static final String TAG = "TLSSocketFactoryCom"; private static final String TAG =
new BraveTag().tagShort23(BraveTLSSocketFactory.class.getSimpleName());
private static BraveTLSSocketFactory instance = null; private static BraveTLSSocketFactory instance = null;
private final SSLSocketFactory internalSSLSocketFactory; private final SSLSocketFactory internalSSLSocketFactory;
private final BraveTrustManagerFactoryHelper trustManagerFactoryHelper;
public BraveTLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { private BraveTLSSocketFactory()
final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, null, null);
internalSSLSocketFactory = context.getSocketFactory();
}
public BraveTLSSocketFactory(
final TrustManagerFactory trustManagerFactory)
throws NoSuchAlgorithmException, KeyManagementException { throws NoSuchAlgorithmException, KeyManagementException {
trustManagerFactoryHelper = new BraveTrustManagerFactoryHelper();
final SSLContext context = SSLContext.getInstance("TLS"); final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManagerFactory.getTrustManagers(), null); context.init(null, trustManagerFactoryHelper.getTrustManagerFactory()
.getTrustManagers(), null);
internalSSLSocketFactory = context.getSocketFactory(); internalSSLSocketFactory = context.getSocketFactory();
} }
@ -110,4 +109,8 @@ public class BraveTLSSocketFactory extends SSLSocketFactory {
} }
return socket; return socket;
} }
public TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactoryHelper.getTrustManagerFactory();
}
} }

View file

@ -0,0 +1,136 @@
package org.schabi.newpipe.util;
import android.content.Context;
import android.util.Log;
import org.schabi.newpipe.App;
import org.schabi.newpipe.BraveTag;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import static org.schabi.newpipe.MainActivity.DEBUG;
/**
* This helper class basically init the TrustManagerFactory with our custom CA's.
* <p>
* The CA's are for rumble.com and framatube.org
*/
public class BraveTrustManagerFactoryHelper {
private static final String TAG =
new BraveTag().tagShort23(BraveTrustManagerFactoryHelper.class.getSimpleName());
TrustManagerFactory trustManagerFactory;
public BraveTrustManagerFactoryHelper() {
try {
final KeyStore customCAsKeystore = createKeystoreWithCustomCAsAndSystemCAs();
trustManagerFactory =
addOurKeystoreToTrustManagerFactory(customCAsKeystore);
} catch (final NoSuchAlgorithmException | KeyStoreException
| IOException | CertificateException e) {
if (DEBUG) {
e.printStackTrace();
Log.e(TAG, "Unable to create TrustManagerFactory with own CA's", e);
}
}
}
public TrustManagerFactory getTrustManagerFactory() {
return trustManagerFactory;
}
private TrustManagerFactory addOurKeystoreToTrustManagerFactory(
final KeyStore keyStore)
throws NoSuchAlgorithmException, KeyStoreException {
final TrustManagerFactory managerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Tell TrustManager to trust the CAs in our KeyStore
managerFactory.init(keyStore);
// only allow one TrustManager
final TrustManager[] trustManagers = managerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return managerFactory;
}
/**
* Add our trusted CAs for rumble.com and framatube.org to keystore.
*
* @return custom CA keystore with our added CAs
* @throws KeyStoreException
* @throws CertificateException
* @throws IOException
* @throws NoSuchAlgorithmException
*/
private KeyStore createKeystoreWithCustomCAsAndSystemCAs()
throws KeyStoreException, CertificateException,
IOException, NoSuchAlgorithmException {
final List<String> rawCertFiles = Arrays.asList("ca_digicert_global_g2",
"ca_lets_encrypt_root" /*, "ca_lets_encrypt"*/);
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
for (final String rawCertFile : rawCertFiles) {
final Certificate cert = readCertificateFromFile(rawCertFile);
keyStore.setCertificateEntry(rawCertFile, cert);
}
addSystemCAsToKeystore(keyStore);
return keyStore;
}
private void addSystemCAsToKeystore(
final KeyStore keyStore) throws NoSuchAlgorithmException, KeyStoreException {
// Default TrustManager to get device trusted CA's
final TrustManagerFactory defaultTrustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
defaultTrustManagerFactory.init((KeyStore) null);
final X509TrustManager trustManager =
(X509TrustManager) defaultTrustManagerFactory.getTrustManagers()[0];
int idx = 0;
for (final Certificate cert : trustManager.getAcceptedIssuers()) {
keyStore.setCertificateEntry(Integer.toString(idx), cert);
idx++;
}
}
private Certificate readCertificateFromFile(
final String rawFile)
throws IOException, CertificateException {
final Context context = App.getApp().getApplicationContext();
final InputStream inputStream = context.getResources().openRawResource(
context.getResources().getIdentifier(rawFile,
"raw", context.getPackageName()));
final byte[] rawBytes = new byte[inputStream.available()];
inputStream.read(rawBytes);
inputStream.close();
final CertificateFactory cf = CertificateFactory.getInstance("X.509");
return cf.generateCertificate(new ByteArrayInputStream(rawBytes));
}
}

View file

@ -0,0 +1,22 @@
package org.schabi.newpipe;
public final class BraveTag {
/**
* This just truncate the string to have 23 chars.
* <p>
* This has to be <= 23 chars on devices running Android 7 or lower (API <= 25)
* or it fails with an IllegalArgumentException
* https://stackoverflow.com/a/54744028
*
* @param longTag the tag you want to shorten
* @return the 23 chars tag string
*/
public String tagShort23(final String longTag) {
if (longTag.length() > 23) {
return longTag.substring(0, 22);
} else {
return longTag;
}
}
}