Installing root certificate in android and mitmproxy

Well, since you are here, you are probably planning to spy on traffic generated by the apps on your phone, probably looking to find out what apps are leaking your private information !

How this all works: When an application on your phone contacts a website/web-server over a secure connection, your phone checks the certificate, and checks whether or not that certificate was issued by an authority the phone trusts, what we are doing is telling the phone to add YOU to the list of trusted authorities, then, we create a proxy that the phone connects through (mitmproxy). Now, the proxy contacts the website on behalf of the application that sent the request, gets the response / contents of that page, saves a copy of that response, and repackages it using our own security certificate, then sends it to the phone, at this stage, the phone thinks the certificate belongs to the website it was contacting (Because now it trusts you), and everything works as it normally would. This does not work if the app has a pinned certificate, a pinned certificate is when an app does not trust the list of authorities the system maintains and requires a certain certificate, but this too has a workaround !

here is what you need to do.

I am assuming you already have a rooted virtual android device (AVD), if not, here are the instructions, in my case, even though it is mid 2026, i decided to go with a Google Pixel 6 with android 12 (The pixel six has updates up to the most current Android 17, but i decided to go with Android 12)

You need to create a root certificate, and install it in both mitmproxy and android, the mitmproxy gets the pem file with the certificate and private key (So that it can create certificates for websites on the fly), while android gets the one with the certificate (Obviously)

1- Gnerate the private key

openssl genrsa -out android-mitm-ca.key 4096

2 – Create the CA certificate


openssl req -x509 -new -sha256 -key android-mitm-ca.key -out android-mitm-ca.crt -days 3650 -subj "/C=JO/O=Android Traffic Analysis/CN=Android MITM Analysis CA" -addext "basicConstraints=critical,CA:TRUE" -addext "keyUsage=critical,keyCertSign,cRLSign"

3- Installing mitmproxy (On your debian machine)

install Prerequisites:

sudo apt install python3-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg-dev zlib1g-dev

Debian 13 does not have it in repositories (12, and 14 do !), so we need to install from binaries downloaded from the website ! so i put them in /opt/mitmproxy and symlinked the files in /usr/local/bin

4- Concatinate/combine key and cert into 1 file for the mitmproxy

cat android-mitm-ca.key android-mitm-ca.crt > ~/.mitmproxy/mitmproxy-ca-my.pem
chmod 600 ~/.mitmproxy/mitmproxy-ca-my.pem

5- running and check that mitmproxy is using our cert

First, run mitmproxy.

NO-GO: mitmproxy –listen-host 192.168.7.119 –listen-port 8080 –certs “*=$HOME/.mitmproxy/mitmproxy-ca-my.pem” (No go because certs is for individual domains)

NO-GO:

mitmproxy --certs "*=$HOME/.mitmproxy/mitmproxy-ca-my.pem"

Or if needed

mitmproxy --listen-host 192.168.7.119 --listen-port 8080 --set confdir="$HOME/.mitmproxy-my"

Or if you want access from outside your network

mitmproxy --listen-host 192.168.7.129 --listen-port 8080 --set block_global=false --set confdir="$HOME/.mitmproxy-my"

From another terminal, let us try to get the contents of example.com through the proxy:

openssl s_client -proxy 192.168.7.119:8080 -connect example.com:443 -servername example.com </dev/null 2>/dev/null | openssl x509 -noout -issuer -subject

Now, if the proxy works as intended, and is correctly signing our requests with our own certificate, we should see something like isuer: Android MITM Analysis CA (The name we chose)

So, now our proxy is ready and serving, all that remains is relevant to Android, we have already made the android certificate to add (OUR_HASH.0)

Installing the certificate on the android phone

The only thing that is confusing about this section is that not all android phones work in the same way ! I am deliberately using Android 12 (Will install most software while being somewhat easier to tinker with), you on the other hand may be using android 16 or 17 (The latest at the time of writing).

Also, I am using a virtual android phone (With Android Studio), you may be using an actual rooter physical phone !

So, I will outline when something is going to be different on other android versions, and you will have to figure it out (Use Google, or gemini or something)

6- Certificate File Name for android

figure out the hash for the filename of the Android file (The file name should be “hash.0”, replace hash with the actual hash resulting from the below) this is how android expects the file name to be

HASH=$(openssl x509 -inform PEM -subject_hash_old -in android-mitm-ca.crt | head -1)
echo "$HASH"

Now, copy the file android-mitm-ca.crt and name the copy the same as the resulting hash with an added .0 at the end (THEHASH.0)

7- If android 16+ (I am using 12, but if you have 16+)

If your rootAVD image is Android 16 or above, don’t blindly follow the old /system/etc/security/cacerts/ procedure. Android 16 changed the system certificate location, and current mitmproxy documentation has a separate procedure involving a Magisk certificate-moving module.

check your android version

adb shell getprop ro.build.version.release
adb shell getprop ro.build.version.sdk
adb shell getprop ro.product.cpu.abi
adb shell su -c 'id'
$ adb shell getprop ro.build.version.release
12
$ adb shell getprop ro.build.version.sdk
31
$ adb shell getprop ro.product.cpu.abi
x86_64
$ adb shell su -c ‘id’
uid=0(root) gid=0(root) groups=0(root) context=u:r:magisk:s0

So, in my case, I am running android 12 ! Software development kit 31 (API level), X86 cpu, and (context=u:r:magisk:s0)

8- Explore the filesystem

Let us check and explore the files and folders on filesystem


> adb shell su -c 'ls -ld /system/etc/security/cacerts'
drwxr-xr-x 2 root root 4096 2023-07-26 23:02 /system/etc/security/cacerts

> adb shell su -c 'mount | grep -E " /system | / "'
/dev/block/dm-4 on / type ext4 (ro,seclabel,relatime)

> adb shell su -c 'ls -l /system/etc/security/cacerts | head'
total 952
-rw-r--r-- 1 root root 2871 2023-07-26 23:02 01419da9.0
-rw-r--r-- 1 root root 2914 2023-07-26 23:02 04f60c28.0
-rw-r--r-- 1 root root 2345 2023-07-26 23:02 0d69c7e1.0
-rw-r--r-- 1 root root 4622 2023-07-26 23:02 10531352.0
-rw-r--r-- 1 root root 4724 2023-07-26 23:02 111e6273.0
-rw-r--r-- 1 root root 4308 2023-07-26 23:02 12d55845.0
-rw-r--r-- 1 root root 2599 2023-07-26 23:02 1ae85e5e.0
-rw-r--r-- 1 root root 4931 2023-07-26 23:02 1df5a75f.0
-rw-r--r-- 1 root root 4535 2023-07-26 23:02 1e1eab7c.0

So, we do have the path /system/etc/security/cacerts on the phone, system (See second command above) is mounted read only, there are 952 certificate files in the /system/etc/security/cacerts folder.

Reminder: On Android 12, the traditional system CA location we’re interested in is: /system/etc/security/cacerts/ , The certificates are stored there using the OpenSSL subject-hash filename convention, e.g.: xxxxxxxx.0

The plan at this stage is to use magisk to add our certificate as an overlay, this way, we don’t need to modify the actual system, meaning, we will not be mounting system in read-write mode, read-only is fine by us

9- Explore Magisk

So let us start by checking what version of MAGISK is installed on the phone, and where it lives

> adb shell su -c 'magisk -v'
26.4:MAGISK:R
> adb shell su -c 'magisk --path'
/debug_ramdisk
> adb shell su -c 'ls -la /data/adb/modules'
total 24
drwxr-xr-x 3 root root 4096 2026-08-26 12:05 .
drwx------ 6 root root 4096 2026-08-26 12:11 ..
drwxr-xr-x 3 root root 4096 2026-08-26 12:07 hosts

Since this is rootAVD/Magisk, the clean approach is to use a Magisk overlay so we don’t modify the underlying system image.

10- Create the Magisk module with the certificate in it !

we can create our own small Magisk module specifically for the CA without touching the original system image. (the only existing module is hosts)

To create a magisk module we need to do the following

10.1- Create an identical folder structure on my debian machine *(Yours can be windows)

mkdir -p ~/.mitmproxy/module/system/etc/security/cacerts

Move your certificate (xxxxx.0) to the cacerts folder we created above

Now create the module file (Module dfinition file), obviously you can create it with a text editor as well if you wish.

cat > ~/.mitmproxy/module/module.prop <<'EOF'
id=android-mitm-ca
name=Android MITM Analysis CA
version=1.0
versionCode=1
author=Voodoo
description=Custom CA certificate for controlled Android traffic analysis
EOF

Now, put the contents of the module directory (Without the module directory itself) in a zip file

cd ~/.mitmproxy/module
zip -r ../android-mitm-ca.zip .

Now, open the zip file in fileroller (or 7-zip or winrar, whatever suits you) and make sure we have

module.prop
system/
system/etc/
system/etc/security/
system/etc/security/cacerts/
system/etc/security/cacerts/XXXXXXXX.0

If all looks good, super, we have the zip file for android…. go ahead and push it to the android virtual machine

> adb push ~/.mitmproxy/android-mitm-ca.zip /data/local/tmp/

Now, when the above works, install the zip file with magisk

> adb shell su -c 'magisk --install-module /data/local/tmp/android-mitm-ca.zip'
- Device is system-as-root
**************************
Android MITM Analysis CA
by Voodoo
**************************
*******************
Powered by Magisk
*******************
- Extracting module files
- Done

If you get an error above DO NOT REBOOT, only reboot if you get a message like the one i got. if not, Sorry man, you need to reverse the steps above ! something with the rootAVD restore command should do

Now, after rebooting (adb shell reboot), run this to see if our certificate appears !

adb shell su -c 'ls -l /system/etc/security/cacerts/'

Looks fine to me, but let us double check (replace the xxx with your actual file name)

adb shell su -c “ls -l /system/etc/security/cacerts/xxxxxx.0”
-rw-r–r– 1 root root 2000 2026-08-27 20:28 /system/etc/security/cacerts/xxxx.0

And if everything was done correctly, “adb shell su -c ‘mount | grep ” / “‘” should still show read-only

Now, let us play, I am very skeptical that this would work (Unless your android has openssl), but it can’t hurt to try… anyway, failiure here means nothing is the error is that “openssl: inaccessible or not found”, it is expected, and means nothing

adb shell su -c "openssl x509 -in /system/etc/security/cacerts/fea8a918.0 -noout -subject -issuer"

Pinned certificates

there are ways to get around this, ways from things as simple as using apk-mitm to remove the pinning, all the way to more serious stuff such as using jadx to modify the software before playing this game.

Here are some tools that can come in handy for this

  • FRIDA: Lets you inject snippets of JavaScript or your own library into native apps + simple tools built on top of the Frida API.
  • APK-MITM: (https://github.com/shroudedcode/apk-mitm): Remove certificate pinning from APK files
  • objection is a runtime mobile exploration toolkit powered by Frida, which supports certificate pinning bypasses on iOS and Android.
  • ssl-kill-switch2 blackbox tool to disable certificate pinning within iOS and macOS applications.
  • android-unpinner : From the same people who make the MITMPROXY, it modifies Android APKs to inject Frida and HTTP Toolkit’s unpinning scripts.

apk-mitm

node --version
npm --version
sudo apt install zip unzip
npm install -g apk-mitm

Now, to remove the pinning in your app

apk-mitm <path-to-apk>

SSH tunnel for VNC

If you were on a linux machine, it would be as simple as

ssh -L 5901:localhost:5901 root@192.168.7.119

Just kidding, don’t login as root, it is all just for tunneling, replace root and the IP of my super top secret server with something suitable

On windows, the most popular SSH client is putty, and to do that in putty, follow the following steps

Open putty, then enter the IP address or hostname of the remote machine

Tunneling Firefox traffic through SSH – Putty

I will here assume you already have a remote Linux machine that you can SSH into with putty, the instructions are simple from this point on

Putty Setup

1- Basic putty settings, assuming you have already downloaded putty from chiark.greenend.org.uk, now open putty, enter the IP of the server you wish to tunnel through, and save it with a name, the steps are…
– Open putty,
– enter the IP of your remote machine
– give it a name of your choice
– save (You don’t need to save now, you will save again in a bit, but you can do it anyway)

2- Go to Connection and expand it, then expand SSH, then select Tunnels, this will show a dialogue such as the below, fill in the data as follows

  • A Source port between 1025-65536 (of your choice), i chose 8081 but you can chose any other in that range
  • Check Dynamic and Auto, the click Add

3- From the menu on the left, go back to Session, and click the save button again (So that the new tunnel settings are saved for next time)

4- You are almost done, Now double click the saved session name or select it and hit open, the remote machine should now prompt you to enter a username and a password, once you enter those, you have a tunnel ready on your localhost (127.0.0.1) on port 8081, next we will setup Firefox to use that tunnel

Firefox setup

1- Go to firefox settings (Click the accordion menu to the right, and chose settings), once open, scroll down under general, until you find the Network Settings section, click the settings button in that section

Clicking settings above will show the following popup dialogue, setup your system as follows

  • Manual Proxy Configuration
  • SOCKS Host enter 127.0.0.1 and in the port area of that the port we chose in putty (In my case, 8081)
  • Optional – Add the IP address ranges of the IPs that you do not want to have tunneled through the remote machine
  • For more privacy, and sometimes functionality (When access is blocked from abroad), make sure you tunnel your DNS queries as well (See checkbox below)

Now, to verify that you are conencted to the remote machine, google the following

what is my ip

and google should tell you what your IP address is, at this stage, it should be the same as the remote machine’s IP (Not yours)

Linux

To do this in Linux, it is much easier, simply start the session with the remote machine with the following line

ssh -D 8081 qworqs@your_hostname_or_ip