TL;DR: Follow these steps to intercept traffic using Burp with a self made root CA on Android (or any browser)
In a previous blogpost, we presented a Magisk module that easily integrates user certificates into the system CA store in order to bypass Android N’s new hardened security model. For instrumenting applications, this works pretty well, and it has become a standard module on our pentest devices. The flow is really easy:
- Set up your WIFI to use Burp as your proxy
- Go to http://burp
- Download & install the certificate
However, if we now open Chrome on the Android device, we get the following error: NET::ERR_CERT_VALIDITY_TOO_LONG.
If we take a look at the specifics of the certificate, we see that the certificate expires on Jan 11, 2023. A quick google search tells us that Google has chosen only to allow leaf certificates that expire within 39 months. This seems like a reasonable requirement, and after searching Portswigger’s site, the recommendation was to reissue the Burp CA certificate. Unfortunately, the problem persisted after doing so.
Burp also allows us to import a self made certificate + private key to be used instead of the automatically generated one. This sounds like an easy solution, as we can decrease the lifetime of the root CA, so it’s the next obvious step. In practice, it turns out to be a lot more difficult to get the configuration right than you would think. The goal of this blog post isn’t about all my failed attempts and why they failed, but about sparing you the trouble and giving you a working step-by-step guide to get this to work.
As a side note, this problem does not occur with the normal setup, where Burp’s root CA is added as a trusted user certificate. Chrome is only picky about “real” CA, which are evidently installed in the system root CA store. However, as I would like to have one certificate setup to rule them all, I searched for a solution…
The solution – Creating custom CA and importing it into Burp suite
I order to successfully install our custom root CA in both Burp and Android, we need to create a CA that has the v3_ca extension. The following steps are executed on a clean 14.04 Ubuntu installation. Granted, there are only a few steps to the processes, but that’s only because I’m not listing all my failed attempts. You can also find the commands in a copy/pastable format below.
# Keep it clean > mkdir certificates && cd certificates # Install openssl > sudo apt-get install openssl # Borrow the default openssl.cnf (location may differ, see note) > cp /usr/lib/ssl/openssl.cnf ./ # Create a private key. # It's important to have the v3_ca extension and to supply the openssl.cnf file > openssl req -x509 -days 730 -nodes -newkey rsa:2048 -outform der -keyout server.key -out ca.der -extensions v3_ca -config openssl.cnf Generating a 2048 bit RSA private key ....+++ .............................+++ writing new private key to 'server.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:BE State or Province Name (full name) [Some-State]:NVISO CA Locality Name (eg, city) :NVISO CA Organization Name (eg, company) [Internet Widgits Pty Ltd]:NVISO CA Organizational Unit Name (eg, section) :NVISO CA Common Name (e.g. server FQDN or YOUR name) :NVISO CA Email Address :NVISO CA # Convert to der format > openssl rsa -in server.key -inform pem -out server.key.der -outform der writing RSA key # Convert key to pkcs8 format > openssl pkcs8 -topk8 -in server.key.der -inform der -out server.key.pkcs8.der -outform der -nocrypt
# Same commands for copy/pasting mkdir certificates && cd certificates sudo apt-get install openssl cp /usr/lib/ssl/openssl.cnf ./ openssl req -x509 -days 730 -nodes -newkey rsa:2048 -outform der -keyout server.key -out ca.der -extensions v3_ca -config openssl.cnf openssl rsa -in server.key -inform pem -out server.key.der -outform der openssl pkcs8 -topk8 -in server.key.der -inform der -out server.key.pkcs8.der -outform der -nocrypt
Let’s take a quick look at the generated certificate:
> openssl x509 -in ca.der -inform der -noout -text Certificate: Data: Version: 3 (0x2) Serial Number: 17570736880079538514 (0xf3d7cc1942ff0952) Signature Algorithm: sha256WithRSAEncryption Issuer: C=BE, ST=NVISO CA, L=NVISO CA, O=NVISO CA, OU=NVISO CA, CN=NVISO CA/emailAddress=NVISO CA Validity Not Before: Jan 11 16:23:19 2018 GMT Not After : Jan 11 16:23:19 2020 GMT Subject: C=BE, ST=NVISO CA, L=NVISO CA, O=NVISO CA, OU=NVISO CA, CN=NVISO CA/emailAddress=NVISO CA Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:d8:a8:f3:40:39:50:3e:7e:5d:25:e0:62:5d:c9: b1:f5:bb:d0:c5:40:5d:1b:68:f4:fc:e7:52:f9:36: a9:fa:78:97:76:eb:05:86:0e:70:54:3a:69:9c:e7: 22:f7:dd:3a:20:71:ee:4a:f6:44:76:02:4f:bd:25: 31:9f:32:5e:93:34:64:30:a5:6b:8e:79:4d:7d:06: e7:7f:fc:26:8f:1a:62:b4:65:08:46:4c:e1:ed:17: 25:a8:d8:54:7c:61:3b:39:54:8f:f4:66:5f:0d:6f: aa:f0:e4:9e:50:af:f4:0b:6e:80:96:98:76:3a:b2: 3f:a8:5a:92:ea:9d:7c:c3:c9:c5:47:c4:d9:56:e3: c0:43:83:3b:fb:08:6d:cf:c9:d0:29:61:1c:26:a3: b5:4c:76:bb:0c:88:5c:53:b8:84:31:4b:f8:a4:2e: 25:29:09:cb:a8:a9:62:de:6e:61:f6:70:e8:85:52: 34:08:20:d3:ca:ba:1c:81:e3:13:c6:00:d5:2c:3c: bd:20:0b:a2:e8:51:cc:e6:68:e1:ef:30:69:ae:fb: 89:84:83:ad:ea:37:59:b5:f0:0c:30:d1:f9:b6:02: 2e:12:8f:73:06:02:ed:c5:87:06:43:2d:58:7e:31: da:bb:b2:0a:15:ef:c7:19:aa:62:87:96:c0:2a:22: e4:03 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: B9:CA:08:19:9A:5F:AE:63:3C:50:83:BC:A1:FA:36:00:B9:66:55:53 X509v3 Authority Key Identifier: keyid:B9:CA:08:19:9A:5F:AE:63:3C:50:83:BC:A1:FA:36:00:B9:66:55:53 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha256WithRSAEncryption 04:33:11:54:aa:dc:f1:92:d6:30:88:89:0b:56:f6:07:ff:aa: 28:65:21:aa:ea:72:3b:72:ba:3d:14:66:33:2d:67:de:80:23: 10:0c:30:31:32:e0:6c:20:a6:6a:58:6f:d4:5b:db:fc:61:de: 9d:d3:4e:66:1d:a1:ed:26:d4:1b:ce:a2:fc:4b:ab:d0:4f:04: 2c:a8:e3:68:7d:e7:dc:04:3d:8c:31:85:e6:c2:d8:65:f0:f6: 64:f2:23:ce:b1:91:43:34:57:97:e4:fd:79:79:f7:03:8b:1a: fc:42:ac:fe:78:26:25:aa:7b:65:2f:f8:4e:25:8e:e9:1e:ce: 93:c8:02:ba:04:59:6c:0e:e4:f2:a8:2f:cd:69:58:42:28:ff: 6f:7c:27:3c:b8:ed:2a:10:e2:36:bd:f5:b9:62:f1:8a:14:57: b8:2e:90:db:15:ce:f2:3a:79:57:f9:a4:76:ef:e9:08:0d:aa: 1d:b9:eb:b9:08:cb:58:06:19:6d:ef:07:d9:25:f4:4d:a4:28: d3:db:ec:c1:5a:0f:40:3a:cf:49:44:d5:8d:b2:42:53:3a:35: 55:0b:54:67:da:b4:13:dc:52:31:10:d4:8c:8d:7f:29:40:bb: dd:14:3c:c3:a3:66:24:95:64:91:a4:a8:74:a6:ad:92:fc:8c: 87:38:31:03
The generated certificate has the v3_ca extension enabled, so we can import it into Android. Using our magisk module, you can install this certificate through the normal certificate installation flow, and after rebooting your device, the CA should be listed in the system CA store.
The next step is importing these files into Burp. Go to the proxy settings page and choose “Import / Export CA Certificate” -> “Import” -> “Certificate and private key in DER format”. The correct files to choose are `ca.der` and server.key.pkcs8.der:
After installing the certificate, restart Burp just to be sure.
If everything worked, you’ll now have your custom root CA as a system certificate, and you can intercept all chrome traffic.
It took quite some time to figure out the correct procedure, so I hope I’ve at least saved someone else from spending hours trying to figure it out.
And, in order to help anyone searching for the correct answer, here’s a short overview of the problems I encountered:
- NET::ERR_CERT_VALIDITY_TOO_LONG (-> Make sure your certificate’s validity is not longer than 39 months)
- Custom root CA won’t install correctly on Android (-> Make sure it has the v3_ca extension)
- Burp import errors (-> Follow the steps above for correct setup)
- Failed to import certificate: java.security.cert.CertificateParsingException: signed fields invalid
- Failed to import certificate: java.security.cert.CertificateException: Unable to initialize, java.io.IOException: extra data given to DerValue constructor
- OS X / MacOS openssl: Error Loading extension section v3_ca (-> Copy the openssl.cnf file and manually add it as an argument with -config )
About the author
Jeroen Beckers is a mobile security expert working in the NVISO Cyber Resilience team. He is a SANS instructor and SANS lead author of the SEC575 course. Jeroen is also a co-author of OWASP Mobile Security Testing Guide (MSTG) and the OWASP Mobile Application Security Verification Standard (MASVS). He loves to both program and reverse engineer stuff. You can find Jeroen on LinkedIn.