How to Decrypt WhatsApp crypt8 Database Messages

WhatsApp backup conversation files are now saved with the .crypt8 extension. This means that WhatsApp has once again changed (do they ever stop??!!) the encryption algorithm used to generate the chat history message backups that are saved on the SD card.

Changes between crypt7 and crypt8

As far as the encryption algorithm is concerned, there are no changes between crypt7 and crypt8 files. For crypt8, an additional step has been introduced – gunzip the decrypted output file.

With the crypt8 update, WhatsApp has also disabled the Android ADB backup feature. This method was used previously to extract the keys on a non-rooted phone. So, what this means, is that the key file that holds the encryption keys cannot be retrieved anymore unless your phone is rooted. We can confirm that the android:allowBackup feature has been turned off by running aapt (Android Asset Packing Tool) on the latest WhatsApp APK file.

$ aapt dump xmltree com.whatsapp.apk AndroidManifest.xml | grep allowBackup
      A: android:allowBackup(0x01010280)=(type 0x12)0x0

Extract Key File

To decrypt the crypt8 files, you will need the key file. The key file stores two sets of decryption keys – the actual encryption key,K and an initialisation vector called IV . WhatsApp stores the key file in a secure location.

/data/data/com.whatsapp/files/key

Using a rooted phone, launch ADB shell, switch to root and copy the key file to the SD card.

$ adb shell
android$ su
android# cp /data/data/com.whatsapp/files/key /sdcard
android# exit
android$ exit

Once the file has been copied to the SD card, use ADB again to copy it to your computer.

$ adb pull /sdcard/key

Extract crypt8 Backup File

Pull the encrypted WhatsApp messages file from your phone using ADB.

$ adb pull /sdcard/WhatsApp/Databases/msgstore.db.crypt8

Extract Decryption Keys

The encryption method being used is AES with a key length of 256 bits and an initialisation vector size of 128 bits. The 256-bit AES key is saved from offset 0x7E till 0x9D in the file. Offsets start from 0x00. Extract the AES key with hexdump and assign the value to variable $k. You can choose either one of the two commands below. Both will give the same result.

$ k=$(hexdump -ve '2/1 "%02x"' key | cut -b 253-316)
$ k=$(dd if=key bs=1 skip=126 count=32 2>/dev/null | hexdump -ve '2/1 "%02x"')

The $k variable will hold a 64-digit hexadecimal value in ASCII that is actually 256 bits in length.

Next, extract the IV or the initialisation vector and assign the value to variable $iv. For WhatsApp version before v2.12.38, the IV value is saved from offset 0x6E till 0x7D in the key file. Again, you can choose either one of the two commands below. Both will give the same result.

$ iv=$(hexdump -ve '2/1 "%02x"' key | cut -b 221-252)
$ iv=$(dd if=key bs=1 skip=110 count=16 2>/dev/null | hexdump -ve '2/1 "%02x"')

For WhatsApp version from v2.12.38 onwards, the IV value is saved from offset 0x33 till 0x42 in the crypt8 file and not in the key file. Use one of the following commands to extract IV.

$ iv=$(hexdump -n 67 -ve '2/1 "%02x"' msgstore.db.crypt8 | cut -b 103-134)
$ iv=$(dd if=msgstore.db.crypt8 bs=1 skip=51 count=16 2>/dev/null | hexdump -ve '2/1 "%02x"')

The $iv variable will hold a 32-digit hexadecimal value in ASCII that is actually 128 bits in length.

Strip Header in crypt8 File

Before we start the decryption process, we will need to strip the 67 byte header from the crypt8 file.

$ dd if=msgstore.db.crypt8 of=msgstore.db.crypt8.nohdr ibs=67 skip=1

The above command will strip the the first 67 bytes from the crypt8 file and save it to a file with extension crypt8.nohdr. If you look at the contents of the header, the IV value is actually stored there – between offset 0x33 till 0x42.

The file size of the header stripped file must be divisible by 128 bits or 16 bytes. For example, the size of my output file is 16216176 bytes – a number that is divisible by 16. If the file size is not in multiples of 16 bytes, then most probably your file is corrupted.

$ ls -l msgstore.db.crypt8.nohdr
-rw-r--r--+ 1 ibrahim users 16216176 Dec 17 18:35 msgstore.db.crypt8.nohdr

Decrypt crypt8 File

Now we have the necessary parameters to decrypt the crypt8.nohdr file using the openssl utility.

$ openssl enc -aes-256-cbc -d -nosalt -nopad -bufsize 16384 -in msgstore.db.crypt8.nohdr -K $k -iv $iv | gunzip > msgstore.db
$ openssl enc -aes-256-cbc -d -nosalt -bufsize 16384 -in msgstore.db.crypt8.nohdr -K $k -iv $iv | gunzip > msgstore.db

The $k and $iv variables hold the AES encryption key and IV values that we retrieved using hexdump earlier. A non encrypted SQLite database file will be generated and saved to a file called msgstore.db.

I got the following warning message from gunzip utility when using the openssl command with the -nopad option. I assume that the warning can be safely ignored.

"gzip: stdin: decompression OK, trailing garbage ignored"

Updated (28-Dec-2014): openssl command has been modified to prevent gzip warning.

Updated (18-May-2015): Method updated to extract IV from crypt8 file. Credit to TripCode @ XDA for the method.

Updated (18-May-2015): Force hexdump to print all input data by using -v option, else hexdump could print identical data with asterisk instead.

Related: Extract WhatsApp Password on Android

ibrahim = { interested_in(unix, linux, android, open_source, reverse_engineering); coding(c, shell, php, python, java, javascript, nodejs, react, vuejs); plays_on(xbox, ps4); linux_desktop_user(true); }