This module does the following:
- The module bundles statically compiled openssl binaries for x86/x64/arm/arm64 to generate SPKI fingerprints for the user installed CA certificates on the device
- Create flag files picked up by Chrome containing
--ignore-certificate-errors-spki-listflag configured to the SPKI fingerprints for the user installed CAs - Configure global settings debug value to
com.android.chrome
This allows us to bypass the Certificate Transparency (CT) error NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED for self-signed certificates.
The flags used when Android starts Chrome can be manipulated using the following files.
/data/local/chrome-command-line
/data/local/android-webview-command-line
/data/local/webview-command-line
/data/local/content-shell-command-line
/data/local/tmp/chrome-command-line
/data/local/tmp/android-webview-command-line
/data/local/tmp/webview-command-line
/data/local/tmp/content-shell-command-line
Chrome will use these files when the chrome://flags option Enable command line on non-rooted devices is Enabled.
This works on eng and userdebug builds out of the box. On user builds you need to configure the global system-level device preference debug_app to com.android.chrome to make it load the flag files. If you are experiencing NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED on an app other than com.android.chrome, configuring the debug_app to that app is the solution (adb shell su -c settings put global debug_app <appname>).
- Visit
chrome://flagsand setEnable command line on non-rooted devicestoEnabled - Install user CA cert (instructions)
- Install Magisk
- Download Magisk module
- Install Magisk module in Magisk app
Visit chrome://version and verify that the --ignore-certificate-errors-spki-list flag is picked up by Chrome.
Use the following steps to compile the Magisk module manually.
git clone https://github.com/JelmerDeHen/MagiskBypassCertificateTransparencyError
cd MagiskBypassCertificateTransparencyError
# Compile statically compiled openssl binaries
bash build-openssl-static/wrapper.sh build
# Overwrite the precompiled binaries with your compiled binaries now located in `build-openssl-static/out`
mv -v build-openssl-static/out/* bin
# Create Magisk module
zip -r MagiskBypassCertificateTransparencyError.zip bin META-INF module.prop openssl-arm openssl-arm64 openssl-x64 openssl-x86 post-fs-data.sh update_info.json
# Upload module to device
adb push MagiskBypassCertificateTransparencyError.zip /sdcard/DownloadNow install the module in the Magisk app.
When you don't use Magisk but have root you can follow these steps to manually change the Chrome flags.
Use the CA certificate in DER format you are using to issue self-signed certificates. The der2spki.sh script can be used to generate SPKI fingerprints.
# Generate SPKI fingerprint
bash der2spki.sh cacert.derThis script can also directly connect to your device over adb and generate SPKI fingerprints for the user installed certificates located at /data/misc/user/0/cacerts-added/ on the device.
To do this run:
bash der2spki.sh adbAndroid applications don't have command line arguments. Instead they're simulated by reading a file at a specific location early during startup. Applications each defined their own files. The Chrome application (com.android.chrome) provides functionality to change the parameters via a series of files under /data/local/ and /data/local/tmp.
Create the files used by Chrome:
# Replace with your generated SPKI in step 1
FLAGS='chrome --ignore-certificate-errors-spki-list=<SPKI>'
# Create the flag files
echo "${FLAGS}" | adb shell su -c tee /data/local/chrome-command-line /data/local/android-webview-command-line /data/local/webview-command-line /data/local/content-shell-command-line /data/local/tmp/chrome-command-line /data/local/tmp/android-webview-command-line /data/local/tmp/webview-command-line /data/local/tmp/content-shell-command-line
# Set permissions on flag files
echo 'chmod 555 /data/local/*-command-line /data/local/tmp/*-command-line' | adb shell suIf you are on a eng or userdebug build continue with step 4.
Chrome picks up the command line parameters when:
- Current build is
engoruserdebug - Adb is enabled and this is the debug app
Check the Android build variant.
adb shell getprop ro.build.typeWhen the ro.build.type is user then enable adb and configure the debug app to com.android.chrome, on eng and userdebug builds this can be skipped.
adb shell settings put global adb_enabled 1
# This requires root
adb shell su -c settings put global debug_app com.android.chromeVisit chrome://flags and set Enable command line on non-rooted devices to Enabled
Restart Chrome and visit chrome://version to check debug flags.
adb shell am force-stop com.android.chrome
adb shell am start -n com.android.chrome/com.google.android.apps.chrome.MainChrome checks if it should use the flag files in shouldUseDebugCommandLine().
public static void initCommandLine(String fileName) {
initCommandLine(fileName, null);
}
public static void initCommandLine(
String fileName, @Nullable Supplier<Boolean> shouldUseDebugFlags) {
assert !CommandLine.isInitialized();
File commandLineFile = new File(COMMAND_LINE_FILE_PATH_DEBUG_APP, fileName);
// shouldUseDebugCommandLine() uses IPC, so don't bother calling it if no flags file exists.
boolean debugFlagsExist = commandLineFile.exists();
if (!debugFlagsExist || !shouldUseDebugCommandLine(shouldUseDebugFlags)) {
commandLineFile = new File(COMMAND_LINE_FILE_PATH, fileName);
}
CommandLine.initFromFile(commandLineFile.getPath());
}
private static boolean shouldUseDebugCommandLine(
@Nullable Supplier<Boolean> shouldUseDebugFlags) {
if (shouldUseDebugFlags != null && shouldUseDebugFlags.get()) return true;
Context context = ContextUtils.getApplicationContext();
// Check isDebugAndroid() last to get full code coverage when using userdebug devices.
return context.getPackageName().equals(getDebugApp(context)) || BuildInfo.isDebugAndroid();
}
private static String getDebugApp(Context context) {
boolean adbEnabled = Settings.Global.getInt(context.getContentResolver(),
Settings.Global.ADB_ENABLED, 0) == 1;
if (adbEnabled) {
return Settings.Global.getString(context.getContentResolver(),
Settings.Global.DEBUG_APP);
}
return null;
} public static boolean isDebugAndroid() {
return "eng".equals(Build.TYPE) || "userdebug".equals(Build.TYPE);
} private static Boolean shouldUseDebugFlags() {
return ChromeFeatureList.sCommandLineOnNonRooted.isEnabled();
}- Initial release