Decrypting WhatsApp crypt12 files

Search This thread

TripCode

Senior Member
Mar 13, 2014
171
162
I will just leave this here. :good: ;) :)

Python:
Code:
#!/usr/bin/env python

""" decrypt12.py: Decrypts WhatsApp msgstore.db.crypt12 files. """
"""               Requires pycrypto and pycryptodome packages. """

__author__       =    'TripCode'
__copyright__    =    'Copyright (C) 2016'
__license__      =    'GPLv3'
__status__       =    'Production'
__version__      =    '1.0'

from Crypto.Cipher import AES
import os
import sys
import zlib

def keyfile(kf):
    global t1, key
    if os.path.isfile(kf) == False:
        quit('The specified input key file does not exist.')
    elif os.path.getsize(kf) != 158:
        quit('The specified input key file is invalid.')
    with open(kf, 'rb') as keyfile:
        keyfile.seek(30)
        t1 = keyfile.read(32)
        keyfile.seek(126)
        key = keyfile.read(32)
    return True

def decrypt12(cf, of):
    global t2, iv
    if os.path.isfile(cf) == False:
        quit('The specified input crypt12 file does not exist.')
    tf = cf+'.tmp'
    with open(cf, 'rb') as crypt12:
        crypt12.seek(3)
        t2 = crypt12.read(32)
        if t1 != t2:
            quit('Key file mismatch or crypt12 file is corrupt.')
        crypt12.seek(51)
        iv = crypt12.read(16)
        crypt12.seek(67)
        primer(tf, crypt12, 20)
    cipher = AES.new(key, AES.MODE_GCM, iv)
    sqlite = zlib.decompress(cipher.decrypt(open(tf, 'rb').read()))
    with open(of, 'wb') as msgstore:
        msgstore.write(sqlite)
        msgstore.close()
        os.remove(tf)
    return True

def primer(tf, crypt12, sb):
    with open(tf, 'wb') as header:
	    header.write(crypt12.read())
	    header.close()
    with open(tf, 'rb+') as footer:
        footer.seek(-sb, os.SEEK_END)
        footer.truncate()
        footer.close()

def validate(ms):
    with open(ms, 'rb') as msgstore:
        if msgstore.read(6).decode('ascii').lower() != 'sqlite':
            os.remove(ms)
            msg = 'Decryption of crypt12 file has failed.'
        else:
            msg = 'Decryption of crypt12 file was successful.'
    msgstore.close()
    quit(msg)

def main():
    if len(sys.argv) > 2 and len(sys.argv) < 5:
        if len(sys.argv) == 3:
            outfile = 'msgstore.db'
        else:
            outfile = sys.argv[3]
        if keyfile(sys.argv[1]) and decrypt12(sys.argv[2], outfile):
            validate(outfile)
    else:
        print('\nWhatsApp Crypt12 Database Decrypter '+__version__+' '+__copyright__+' by '+__author__+'\n')
        print('\tUsage: python '+str(sys.argv[0])+' key msgstore.db.crypt12 msgstore.db\n')

if __name__ == "__main__":
main()

Code:
Usage: python decrypt12.py key msgstore.db.crypt12 msgstore.db

Java:
Code:
package decrypt12;

/*
 * 
 *** decrypt12.jar: Decrypts WhatsApp msgstore.db.crypt12 files. ***
 *
 * Author		:	TripCode
 * Copyright		:	Copyright (C) 2016
 * License		:	GPLv3
 * Status		:	Production
 * Version		:	1.0
 *
 */

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.Security;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.spongycastle.jce.provider.BouncyCastleProvider; // Android

public class decrypt12 {

	static {
		Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
		// Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); // Android
	}

	public static void decrypt(String KeyFile, String C12File, String SQLFile) throws Exception {

		final File tempFile = new File(System.getProperty("java.io.tmpdir") + "/"
				+ (int) (System.currentTimeMillis() / 1000L) + "-msgstore.enc");

		if (!new File(KeyFile).isFile())
			quit("The specified input key file does not exist.");

		else if (new File(KeyFile).length() != 158)
			quit("The specified input key file is invalid.");

		else if (!new File(C12File).isFile())
			quit("The specified input crypt12 file does not exist.");

		InputStream KeyIn = new FileInputStream(KeyFile);
		InputStream WdbIn = new BufferedInputStream(new FileInputStream(C12File));

		byte[] KeyData = new byte[158];
		KeyIn.read(KeyData);
		byte[] T1 = new byte[32];
		System.arraycopy(KeyData, 30, T1, 0, 32);
		byte[] KEY = new byte[32];
		System.arraycopy(KeyData, 126, KEY, 0, 32);
		KeyIn.close();

		byte[] C12Data = new byte[67];
		WdbIn.read(C12Data);
		byte[] T2 = new byte[32];
		System.arraycopy(C12Data, 3, T2, 0, 32);
		byte[] IV = new byte[16];
		System.arraycopy(C12Data, 51, IV, 0, 16);

		if (!new String(T1, 0, T1.length, "ASCII").equals(new String(T2, 0, T2.length, "ASCII")))
			quit("Key file mismatch or crypt12 file is corrupt.");

		int InputLength = WdbIn.available();
		RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");

		byte[] tempBuffer = new byte[1024];
		int I;

		while ((I = WdbIn.read(tempBuffer)) != -1)
			raf.write(tempBuffer, 0, I);
		raf.setLength(InputLength - 20);
		raf.close();
		WdbIn.close();

		InputStream PdbSt = new BufferedInputStream(new FileInputStream(tempFile));

		Cipher cipher;
		Security.addProvider(new BouncyCastleProvider());
		cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); // BouncyCastle
		// cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC"); // SpongyCastle (Android)

		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY, "AES"), new IvParameterSpec(IV));
		CipherInputStream CipherStream = new CipherInputStream(PdbSt, cipher);

		InflaterInputStream CryptOutput = new InflaterInputStream(CipherStream, new Inflater(false));

		try {
			FileOutputStream InflateBuffer = new FileOutputStream(SQLFile);
			int N = 0;
			byte[] CryptBuffer = new byte[8192];

			while ((N = CryptOutput.read(CryptBuffer)) != -1) {
				InflateBuffer.write(CryptBuffer, 0, N);
			}
			InflateBuffer.close();

		} catch (IOException ex) {
			quit("Fatal error:" + ex);
		}

		CipherStream.close();
		tempFile.delete();

		InputStream SqlDB = new FileInputStream(SQLFile);

		byte[] SqlData = new byte[6];
		SqlDB.read(SqlData);
		byte[] MS = new byte[6];
		System.arraycopy(SqlData, 0, MS, 0, 6);
		SqlDB.close();

		if (!new String(MS, 0, MS.length, "ASCII").toLowerCase().equals("sqlite")) {
			new File(SQLFile).delete();
			quit("Decryption of crypt12 file has failed.");
		}

		else
			quit("Decryption of crypt12 file was successful.");
	}

	private static void quit(String Msg) {
		System.out.println(Msg);
		System.exit(0);
	}

	public static void main(String[] args) throws Exception {

		String outFile;
		if (args.length > 1 && args.length < 4) {
			if (args.length == 3)
				outFile = args[2];
			else
				outFile = "msgstore.db";
			decrypt(args[0], args[1], outFile);
		} else {
			System.out.println("\nWhatsApp Crypt12 Database Decrypter 1.0 Copyright (C) 2016 by TripCode");
			System.out.println("\tUsage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db\n");
		}
	}

}

Code:
Usage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db

GitHub Repository: WhatsApp-Crypt12-Decrypter
 

Attachments

  • decrypt12.jar
    2.8 MB · Views: 12,239
Last edited:

jmrd

New member
Mar 10, 2017
1
0
Just a quick question: My phone was swapped by a repair shop and the other person who got my phone didn't realize this at first. She did a factory reset, and probably installed Whatsapp with her SIM card before I got my phone back. I'm so angry right now...
I guess my key file is gone - either deleted by the factory reset or overwritten with her Whatsapp's key file. I have several backups of the Whatsapp directory and still the same Google account, IMEI and phone number. Can I restore my messages or are any attempts futile?
(I see that the code asks for a key file, so if I cannot get it/extract it somewhere, there is probably no chance anymore?)
 

Tim Niklas

Senior Member
Jun 2, 2015
198
27
hi, realy great tool

with that i could open the msgstore.db and see my chats :)

but is it possible to convert this file into a .db.crypt12 file or something like that, so i can open it with whatsapp an have my chats back?

or are there other ways, i can use this msgstore.db + wa.db to recover the chats?
 

ettorebonfanti

New member
Mar 18, 2017
2
0
Hi!
I have a crypt12 database but no key...is there anyone out there able to decrypt it?
How much will it cost to me to do this? Can anyone do it for me?
Is the code written here above working in decrypting a crypt12 database?
I don't want to root or install any spyware, just trying to decrypt the file.
anyone interested in trying?
Please write dawn here if you're interested and we'll get in contact...
 

Fusseldieb

Senior Member
Jul 27, 2013
317
369
28
Anápolis
Hi!
I have a crypt12 database but no key...is there anyone out there able to decrypt it?
How much will it cost to me to do this? Can anyone do it for me?
Is the code written here above working in decrypting a crypt12 database?
I don't want to root or install any spyware, just trying to decrypt the file.
anyone interested in trying?
Please write dawn here if you're interested and we'll get in contact...
There is a program out there which claims to obtain the key file without root. If it's still working today, I don't know. Here: https://xdaforums.com/showthread.php?t=2770982
If you want the key file, it's better to root your phone and get it. It's not that hard. You can even unroot your phone after catching the key.
And no, someone can't just decrypt the database, because the key is there for a reason, and this reason is security.
 

smasher007

Senior Member
Jun 24, 2012
54
7
Excellent! Just used this to fix a Whatapp DB.

---------- Post added at 10:57 AM ---------- Previous post was at 10:50 AM ----------

This is great thanks! How do I encrypt the database again with crypt12?

I have both, the DB and the key.

I will just leave this here. :good: ;) :)

Python:
Code:
#!/usr/bin/env python

""" decrypt12.py: Decrypts WhatsApp msgstore.db.crypt12 files. """
"""               Requires pycrypto and pycryptodome packages. """

__author__       =    'TripCode'
__copyright__    =    'Copyright (C) 2016'
__license__      =    'GPLv3'
__status__       =    'Production'
__version__      =    '1.0'

from Crypto.Cipher import AES
import os
import sys
import zlib

def keyfile(kf):
    global t1, key
    if os.path.isfile(kf) == False:
        quit('The specified input key file does not exist.')
    elif os.path.getsize(kf) != 158:
        quit('The specified input key file is invalid.')
    with open(kf, 'rb') as keyfile:
        keyfile.seek(30)
        t1 = keyfile.read(32)
        keyfile.seek(126)
        key = keyfile.read(32)
    return True

def decrypt12(cf, of):
    global t2, iv
    if os.path.isfile(cf) == False:
        quit('The specified input crypt12 file does not exist.')
    tf = cf+'.tmp'
    with open(cf, 'rb') as crypt12:
        crypt12.seek(3)
        t2 = crypt12.read(32)
        if t1 != t2:
            quit('Key file mismatch or crypt12 file is corrupt.')
        crypt12.seek(51)
        iv = crypt12.read(16)
        crypt12.seek(67)
        primer(tf, crypt12, 20)
    cipher = AES.new(key, AES.MODE_GCM, iv)
    sqlite = zlib.decompress(cipher.decrypt(open(tf, 'rb').read()))
    with open(of, 'wb') as msgstore:
        msgstore.write(sqlite)
        msgstore.close()
        os.remove(tf)
    return True

def primer(tf, crypt12, sb):
    with open(tf, 'wb') as header:
	    header.write(crypt12.read())
	    header.close()
    with open(tf, 'rb+') as footer:
        footer.seek(-sb, os.SEEK_END)
        footer.truncate()
        footer.close()

def validate(ms):
    with open(ms, 'rb') as msgstore:
        if msgstore.read(6).decode('ascii').lower() != 'sqlite':
            os.remove(ms)
            msg = 'Decryption of crypt12 file has failed.'
        else:
            msg = 'Decryption of crypt12 file was successful.'
    msgstore.close()
    quit(msg)

def main():
    if len(sys.argv) > 2 and len(sys.argv) < 5:
        if len(sys.argv) == 3:
            outfile = 'msgstore.db'
        else:
            outfile = sys.argv[3]
        if keyfile(sys.argv[1]) and decrypt12(sys.argv[2], outfile):
            validate(outfile)
    else:
        print('\nWhatsApp Crypt12 Database Decrypter '+__version__+' '+__copyright__+' by '+__author__+'\n')
        print('\tUsage: python '+str(sys.argv[0])+' key msgstore.db.crypt12 msgstore.db\n')

if __name__ == "__main__":
main()

Code:
Usage: python decrypt12.py key msgstore.db.crypt12 msgstore.db

Java:
Code:
package decrypt12;

/*
 * 
 *** decrypt12.jar: Decrypts WhatsApp msgstore.db.crypt12 files. ***
 *
 * Author		:	TripCode
 * Copyright		:	Copyright (C) 2016
 * License		:	GPLv3
 * Status		:	Production
 * Version		:	1.0
 *
 */

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.security.Security;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
// import org.spongycastle.jce.provider.BouncyCastleProvider; // Android

public class decrypt12 {

	static {
		Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
		// Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); // Android
	}

	public static void decrypt(String KeyFile, String C12File, String SQLFile) throws Exception {

		final File tempFile = new File(System.getProperty("java.io.tmpdir") + "/"
				+ (int) (System.currentTimeMillis() / 1000L) + "-msgstore.enc");

		if (!new File(KeyFile).isFile())
			quit("The specified input key file does not exist.");

		else if (new File(KeyFile).length() != 158)
			quit("The specified input key file is invalid.");

		else if (!new File(C12File).isFile())
			quit("The specified input crypt12 file does not exist.");

		InputStream KeyIn = new FileInputStream(KeyFile);
		InputStream WdbIn = new BufferedInputStream(new FileInputStream(C12File));

		byte[] KeyData = new byte[158];
		KeyIn.read(KeyData);
		byte[] T1 = new byte[32];
		System.arraycopy(KeyData, 30, T1, 0, 32);
		byte[] KEY = new byte[32];
		System.arraycopy(KeyData, 126, KEY, 0, 32);
		KeyIn.close();

		byte[] C12Data = new byte[67];
		WdbIn.read(C12Data);
		byte[] T2 = new byte[32];
		System.arraycopy(C12Data, 3, T2, 0, 32);
		byte[] IV = new byte[16];
		System.arraycopy(C12Data, 51, IV, 0, 16);

		if (!new String(T1, 0, T1.length, "ASCII").equals(new String(T2, 0, T2.length, "ASCII")))
			quit("Key file mismatch or crypt12 file is corrupt.");

		int InputLength = WdbIn.available();
		RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");

		byte[] tempBuffer = new byte[1024];
		int I;

		while ((I = WdbIn.read(tempBuffer)) != -1)
			raf.write(tempBuffer, 0, I);
		raf.setLength(InputLength - 20);
		raf.close();
		WdbIn.close();

		InputStream PdbSt = new BufferedInputStream(new FileInputStream(tempFile));

		Cipher cipher;
		Security.addProvider(new BouncyCastleProvider());
		cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); // BouncyCastle
		// cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC"); // SpongyCastle (Android)

		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY, "AES"), new IvParameterSpec(IV));
		CipherInputStream CipherStream = new CipherInputStream(PdbSt, cipher);

		InflaterInputStream CryptOutput = new InflaterInputStream(CipherStream, new Inflater(false));

		try {
			FileOutputStream InflateBuffer = new FileOutputStream(SQLFile);
			int N = 0;
			byte[] CryptBuffer = new byte[8192];

			while ((N = CryptOutput.read(CryptBuffer)) != -1) {
				InflateBuffer.write(CryptBuffer, 0, N);
			}
			InflateBuffer.close();

		} catch (IOException ex) {
			quit("Fatal error:" + ex);
		}

		CipherStream.close();
		tempFile.delete();

		InputStream SqlDB = new FileInputStream(SQLFile);

		byte[] SqlData = new byte[6];
		SqlDB.read(SqlData);
		byte[] MS = new byte[6];
		System.arraycopy(SqlData, 0, MS, 0, 6);
		SqlDB.close();

		if (!new String(MS, 0, MS.length, "ASCII").toLowerCase().equals("sqlite")) {
			new File(SQLFile).delete();
			quit("Decryption of crypt12 file has failed.");
		}

		else
			quit("Decryption of crypt12 file was successful.");
	}

	private static void quit(String Msg) {
		System.out.println(Msg);
		System.exit(0);
	}

	public static void main(String[] args) throws Exception {

		String outFile;
		if (args.length > 1 && args.length < 4) {
			if (args.length == 3)
				outFile = args[2];
			else
				outFile = "msgstore.db";
			decrypt(args[0], args[1], outFile);
		} else {
			System.out.println("\nWhatsApp Crypt12 Database Decrypter 1.0 Copyright (C) 2016 by TripCode");
			System.out.println("\tUsage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db\n");
		}
	}

}

Code:
Usage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db

GitHub Repository: WhatsApp-Crypt12-Decrypter
 
Last edited:
  • Like
Reactions: m.a.r.k.i.94

GURUMOORTHI

New member
Dec 20, 2017
2
0
error message..line 84..intended error

hi tripcode...i used the python script in python3.6...it showed an intended error..error line 84...can u please help me in this issue..or can u guid me to decrypt the file..am not having that key file..thats y..mobile lost..i retrieved the files from drive..so plz help me in this issue..
 

GURUMOORTHI

New member
Dec 20, 2017
2
0
error the key file not found

hi it was fine till it shows the key file is not found...plz help me in this issues
 

apri3d

New member
Sep 25, 2014
3
0
gbwhatsapp

There is a program out there which claims to obtain the key file without root. If it's still working today, I don't know. Here: https://xdaforums.com/showthread.php?t=2770982
If you want the key file, it's better to root your phone and get it. It's not that hard. You can even unroot your phone after catching the key.
And no, someone can't just decrypt the database, because the key is there for a reason, and this reason is security.

You can try:
a tutorial from Ik Teach:
https://www.youtube.com/watch?v=4b1f2iTnoO8

link to the file gbwhatsapp:
https://idescargar.com/gbwhatsapp/descargar/
It give you de key file whithout root. It worked for me. But it is a little tricky. and you end up with this modded whatsapp.

Cheers!
 

shanya_creation

New member
Apr 24, 2019
1
0
hii tripcode
I am not having that key file..
i lost my mobile....i retrieved the files from drive..so plz help me to decrypt my database.
 
hii tripcode
I am not having that key file..
i lost my mobile....i retrieved the files from drive..so plz help me to decrypt my database.

You need to re-download WhatsApp in another phone and use your old number. (Yes, you'll need to recover your phone number before recovering your chats). Once you have WhatsApp installed you can take out the Key file from /data/data/com.whatsapp/files

To do that stuff i recommend to use a old phone that can be easily rooted or one that has already rooted.

Good luck :good:
 
Last edited:

samruthstom

New member
Sep 13, 2019
1
0
Hello,

Does anyone know if by any chance KEY file gets deleted on old phone when whatsapp is activated on a new phone and old phone whatsapp gets locked asking for conf code by SMS?

I rooted android 4.x.x
I then searched using ez file manager in root and in data/data /com.whatsapp but i do not see a KEY file.
 

heyvijay

New member
Jul 18, 2020
1
0
Need Help!

I have whatsapp db.crypt12 file. I am novice in Mobile. Can anybody make that file readable professionally for me? I am ready to pay for this.
 

zaengi

Member
Oct 14, 2014
7
0
HI,
I have extracted the key by the procedure of "WhatsApp Key/DB Extractor 4.7 Enhanced 1.0" (https://xdaforums.com/showthread.php?t=2770982&page=55), but eventually your JAVA code failed with message:
Key file mismatch or crypt12 file is corrupt.

Why is that?
Somewhere (guess at Elcomsoft Explorer) I have read, that the key can only decrypt backups taken from a previous time. This should true in my case, isnt?

1) Is the key somehow related to the phone device itself? (I have used a spare phone for this analysis, so not the original WA was regularly running on.)

Since end of August (when an Upgrade of WA has destroyed my chat history database) I have tried several attempts to restore it by downgrading WA to 2.20.64 and 2.20.89, so the key I have now is not the original of what the crypted backup was created with.
Moreover, due to a database schema error my chat history can only be loaded with WA version previous to 2.20.89 (this is why: https://medium.com/@leonardocezaryc...hatsapp-windows-phone-to-android-6b225918af55)

My experiments now led to the situation, that I not any more can register any pre-2.20.89 version by the SMS verification process, even though I dated my phone back to mid of August.
Thus I currently have no working installation with all my chat history where I could extract a crypto key from.

2) Any ideas how i can bypass Whatsapp to complain about its expired versions and register by SMS again?
3) Is it a hard-coded deadline in the binaries of the app?
4) Does the crypt key change between installations and become incompatible? I wonder, how to restore any old backup then...

Thanks!
 

zaengi

Member
Oct 14, 2014
7
0
Hello,

Does anyone know if by any chance KEY file gets deleted on old phone when whatsapp is activated on a new phone and old phone whatsapp gets locked asking for conf code by SMS?

I rooted android 4.x.x
I then searched using ez file manager in root and in data/data /com.whatsapp but i do not see a KEY file.

Yes - upon inactivation WA 2.20.89+ destroys the "key" file and instead creates a "backup_token" file.
It is recoverable by activating it via SMS code.

---------- Post added at 07:40 PM ---------- Previous post was at 07:26 PM ----------

I have whatsapp db.crypt12 file. I am novice in Mobile. Can anybody make that file readable professionally for me? I am ready to pay for this.

What kind of problem can't you solve so far?
There is a commercial solution called "Elcomsoft eXplorer for WhatsApp ".
I also would like and am about to try it out, if you are interested, we can share the costs of it. :)
 

adflyer

Member
Dec 15, 2012
5
8
After years of successfully using this to access MY chat data, this week it stopped working :-(

WhatsApp on my phone looks to have updated to v2.21.8.17 and is now generating local backups with a .crypt14 extension instead of .crypt12.

Unsurprisingly, this chokes on this with the error Key file mismatch or crypt12 file is corrupt.

However, armed with hexdump and a bit cunning, I've worked out how to tweak the python so that it successfully decodes .crypt14 files.

in a .crypt14 file ... [.py hacks in decrypt12 function shown below in square brackets]
... t2 is at offset 14 [so change to crypt12.seek(14) t2 = crypt12.read(32)]
... iv is at offset 66 [change to crypt12.seek(66) iv = crypt12.read(16)]
... encrypted content starts at offset 99, (I haven't yet worked out the correct amount of footer to remove, but removing 0 seems to result in a successful decryption so far) [change to crypt12.seek(99) primer(tf, crypt12, 0)]

I've also noticed that the 16 bytes at offset 62 to 77 in the key file also appear in the .cryptX file, just like the t bytes at offset 30 to 61 do.

In a .crypt12 file they are at offset 35 to 50.
In a .crypt14 file they are at offset 48 to 63.

In my code I've called these x1 and x2 and do the same equality check as happens for t1 and t2.

In addition, in the .crypt14 file, bytes at 86 to 94 look to contain the WA version number, so in my case 2.21.8.17 - presumably the version that wrote the .crypt14 file. I suspect that when the version number rolls over to more digits (e.g. 2.21.10.10) then the offset to the start of the encrypted content (currently 99 in my backup files) might well change?
 
Jan 11, 2017
19
6
After years of successfully using this to access MY chat data, this week it stopped working :-(

WhatsApp on my phone looks to have updated to v2.21.8.17 and is now generating local backups with a .crypt14 extension instead of .crypt12.

Unsurprisingly, this chokes on this with the error Key file mismatch or crypt12 file is corrupt.

However, armed with hexdump and a bit cunning, I've worked out how to tweak the python so that it successfully decodes .crypt14 files.

in a .crypt14 file ... [.py hacks in decrypt12 function shown below in square brackets]
... t2 is at offset 14 [so change to crypt12.seek(14) t2 = crypt12.read(32)]
... iv is at offset 66 [change to crypt12.seek(66) iv = crypt12.read(16)]
... encrypted content starts at offset 99, (I haven't yet worked out the correct amount of footer to remove, but removing 0 seems to result in a successful decryption so far) [change to crypt12.seek(99) primer(tf, crypt12, 0)]

I've also noticed that the 16 bytes at offset 62 to 77 in the key file also appear in the .cryptX file, just like the t bytes at offset 30 to 61 do.

In a .crypt12 file they are at offset 35 to 50.
In a .crypt14 file they are at offset 48 to 63.

In my code I've called these x1 and x2 and do the same equality check as happens for t1 and t2.

In addition, in the .crypt14 file, bytes at 86 to 94 look to contain the WA version number, so in my case 2.21.8.17 - presumably the version that wrote the .crypt14 file. I suspect that when the version number rolls over to more digits (e.g. 2.21.10.10) then the offset to the start of the encrypted content (currently 99 in my backup files) might well change?
It's not working for me for some reason. I don't know what I am missing here. Can you provide the modified code itself?
 

Top Liked Posts

  • There are no posts matching your filters.
  • 3
    I will just leave this here. :good: ;) :)

    Python:
    Code:
    #!/usr/bin/env python
    
    """ decrypt12.py: Decrypts WhatsApp msgstore.db.crypt12 files. """
    """               Requires pycrypto and pycryptodome packages. """
    
    __author__       =    'TripCode'
    __copyright__    =    'Copyright (C) 2016'
    __license__      =    'GPLv3'
    __status__       =    'Production'
    __version__      =    '1.0'
    
    from Crypto.Cipher import AES
    import os
    import sys
    import zlib
    
    def keyfile(kf):
        global t1, key
        if os.path.isfile(kf) == False:
            quit('The specified input key file does not exist.')
        elif os.path.getsize(kf) != 158:
            quit('The specified input key file is invalid.')
        with open(kf, 'rb') as keyfile:
            keyfile.seek(30)
            t1 = keyfile.read(32)
            keyfile.seek(126)
            key = keyfile.read(32)
        return True
    
    def decrypt12(cf, of):
        global t2, iv
        if os.path.isfile(cf) == False:
            quit('The specified input crypt12 file does not exist.')
        tf = cf+'.tmp'
        with open(cf, 'rb') as crypt12:
            crypt12.seek(3)
            t2 = crypt12.read(32)
            if t1 != t2:
                quit('Key file mismatch or crypt12 file is corrupt.')
            crypt12.seek(51)
            iv = crypt12.read(16)
            crypt12.seek(67)
            primer(tf, crypt12, 20)
        cipher = AES.new(key, AES.MODE_GCM, iv)
        sqlite = zlib.decompress(cipher.decrypt(open(tf, 'rb').read()))
        with open(of, 'wb') as msgstore:
            msgstore.write(sqlite)
            msgstore.close()
            os.remove(tf)
        return True
    
    def primer(tf, crypt12, sb):
        with open(tf, 'wb') as header:
    	    header.write(crypt12.read())
    	    header.close()
        with open(tf, 'rb+') as footer:
            footer.seek(-sb, os.SEEK_END)
            footer.truncate()
            footer.close()
    
    def validate(ms):
        with open(ms, 'rb') as msgstore:
            if msgstore.read(6).decode('ascii').lower() != 'sqlite':
                os.remove(ms)
                msg = 'Decryption of crypt12 file has failed.'
            else:
                msg = 'Decryption of crypt12 file was successful.'
        msgstore.close()
        quit(msg)
    
    def main():
        if len(sys.argv) > 2 and len(sys.argv) < 5:
            if len(sys.argv) == 3:
                outfile = 'msgstore.db'
            else:
                outfile = sys.argv[3]
            if keyfile(sys.argv[1]) and decrypt12(sys.argv[2], outfile):
                validate(outfile)
        else:
            print('\nWhatsApp Crypt12 Database Decrypter '+__version__+' '+__copyright__+' by '+__author__+'\n')
            print('\tUsage: python '+str(sys.argv[0])+' key msgstore.db.crypt12 msgstore.db\n')
    
    if __name__ == "__main__":
    main()

    Code:
    Usage: python decrypt12.py key msgstore.db.crypt12 msgstore.db

    Java:
    Code:
    package decrypt12;
    
    /*
     * 
     *** decrypt12.jar: Decrypts WhatsApp msgstore.db.crypt12 files. ***
     *
     * Author		:	TripCode
     * Copyright		:	Copyright (C) 2016
     * License		:	GPLv3
     * Status		:	Production
     * Version		:	1.0
     *
     */
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.security.Security;
    import java.util.zip.Inflater;
    import java.util.zip.InflaterInputStream;
    import javax.crypto.Cipher;
    import javax.crypto.CipherInputStream;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    // import org.spongycastle.jce.provider.BouncyCastleProvider; // Android
    
    public class decrypt12 {
    
    	static {
    		Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
    		// Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); // Android
    	}
    
    	public static void decrypt(String KeyFile, String C12File, String SQLFile) throws Exception {
    
    		final File tempFile = new File(System.getProperty("java.io.tmpdir") + "/"
    				+ (int) (System.currentTimeMillis() / 1000L) + "-msgstore.enc");
    
    		if (!new File(KeyFile).isFile())
    			quit("The specified input key file does not exist.");
    
    		else if (new File(KeyFile).length() != 158)
    			quit("The specified input key file is invalid.");
    
    		else if (!new File(C12File).isFile())
    			quit("The specified input crypt12 file does not exist.");
    
    		InputStream KeyIn = new FileInputStream(KeyFile);
    		InputStream WdbIn = new BufferedInputStream(new FileInputStream(C12File));
    
    		byte[] KeyData = new byte[158];
    		KeyIn.read(KeyData);
    		byte[] T1 = new byte[32];
    		System.arraycopy(KeyData, 30, T1, 0, 32);
    		byte[] KEY = new byte[32];
    		System.arraycopy(KeyData, 126, KEY, 0, 32);
    		KeyIn.close();
    
    		byte[] C12Data = new byte[67];
    		WdbIn.read(C12Data);
    		byte[] T2 = new byte[32];
    		System.arraycopy(C12Data, 3, T2, 0, 32);
    		byte[] IV = new byte[16];
    		System.arraycopy(C12Data, 51, IV, 0, 16);
    
    		if (!new String(T1, 0, T1.length, "ASCII").equals(new String(T2, 0, T2.length, "ASCII")))
    			quit("Key file mismatch or crypt12 file is corrupt.");
    
    		int InputLength = WdbIn.available();
    		RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
    
    		byte[] tempBuffer = new byte[1024];
    		int I;
    
    		while ((I = WdbIn.read(tempBuffer)) != -1)
    			raf.write(tempBuffer, 0, I);
    		raf.setLength(InputLength - 20);
    		raf.close();
    		WdbIn.close();
    
    		InputStream PdbSt = new BufferedInputStream(new FileInputStream(tempFile));
    
    		Cipher cipher;
    		Security.addProvider(new BouncyCastleProvider());
    		cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); // BouncyCastle
    		// cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC"); // SpongyCastle (Android)
    
    		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY, "AES"), new IvParameterSpec(IV));
    		CipherInputStream CipherStream = new CipherInputStream(PdbSt, cipher);
    
    		InflaterInputStream CryptOutput = new InflaterInputStream(CipherStream, new Inflater(false));
    
    		try {
    			FileOutputStream InflateBuffer = new FileOutputStream(SQLFile);
    			int N = 0;
    			byte[] CryptBuffer = new byte[8192];
    
    			while ((N = CryptOutput.read(CryptBuffer)) != -1) {
    				InflateBuffer.write(CryptBuffer, 0, N);
    			}
    			InflateBuffer.close();
    
    		} catch (IOException ex) {
    			quit("Fatal error:" + ex);
    		}
    
    		CipherStream.close();
    		tempFile.delete();
    
    		InputStream SqlDB = new FileInputStream(SQLFile);
    
    		byte[] SqlData = new byte[6];
    		SqlDB.read(SqlData);
    		byte[] MS = new byte[6];
    		System.arraycopy(SqlData, 0, MS, 0, 6);
    		SqlDB.close();
    
    		if (!new String(MS, 0, MS.length, "ASCII").toLowerCase().equals("sqlite")) {
    			new File(SQLFile).delete();
    			quit("Decryption of crypt12 file has failed.");
    		}
    
    		else
    			quit("Decryption of crypt12 file was successful.");
    	}
    
    	private static void quit(String Msg) {
    		System.out.println(Msg);
    		System.exit(0);
    	}
    
    	public static void main(String[] args) throws Exception {
    
    		String outFile;
    		if (args.length > 1 && args.length < 4) {
    			if (args.length == 3)
    				outFile = args[2];
    			else
    				outFile = "msgstore.db";
    			decrypt(args[0], args[1], outFile);
    		} else {
    			System.out.println("\nWhatsApp Crypt12 Database Decrypter 1.0 Copyright (C) 2016 by TripCode");
    			System.out.println("\tUsage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db\n");
    		}
    	}
    
    }

    Code:
    Usage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db

    GitHub Repository: WhatsApp-Crypt12-Decrypter
    3
    After years of successfully using this to access MY chat data, this week it stopped working :-(

    WhatsApp on my phone looks to have updated to v2.21.8.17 and is now generating local backups with a .crypt14 extension instead of .crypt12.

    Unsurprisingly, this chokes on this with the error Key file mismatch or crypt12 file is corrupt.

    However, armed with hexdump and a bit cunning, I've worked out how to tweak the python so that it successfully decodes .crypt14 files.

    in a .crypt14 file ... [.py hacks in decrypt12 function shown below in square brackets]
    ... t2 is at offset 14 [so change to crypt12.seek(14) t2 = crypt12.read(32)]
    ... iv is at offset 66 [change to crypt12.seek(66) iv = crypt12.read(16)]
    ... encrypted content starts at offset 99, (I haven't yet worked out the correct amount of footer to remove, but removing 0 seems to result in a successful decryption so far) [change to crypt12.seek(99) primer(tf, crypt12, 0)]

    I've also noticed that the 16 bytes at offset 62 to 77 in the key file also appear in the .cryptX file, just like the t bytes at offset 30 to 61 do.

    In a .crypt12 file they are at offset 35 to 50.
    In a .crypt14 file they are at offset 48 to 63.

    In my code I've called these x1 and x2 and do the same equality check as happens for t1 and t2.

    In addition, in the .crypt14 file, bytes at 86 to 94 look to contain the WA version number, so in my case 2.21.8.17 - presumably the version that wrote the .crypt14 file. I suspect that when the version number rolls over to more digits (e.g. 2.21.10.10) then the offset to the start of the encrypted content (currently 99 in my backup files) might well change?
    1
    Excellent! Just used this to fix a Whatapp DB.

    ---------- Post added at 10:57 AM ---------- Previous post was at 10:50 AM ----------

    This is great thanks! How do I encrypt the database again with crypt12?

    I have both, the DB and the key.

    I will just leave this here. :good: ;) :)

    Python:
    Code:
    #!/usr/bin/env python
    
    """ decrypt12.py: Decrypts WhatsApp msgstore.db.crypt12 files. """
    """               Requires pycrypto and pycryptodome packages. """
    
    __author__       =    'TripCode'
    __copyright__    =    'Copyright (C) 2016'
    __license__      =    'GPLv3'
    __status__       =    'Production'
    __version__      =    '1.0'
    
    from Crypto.Cipher import AES
    import os
    import sys
    import zlib
    
    def keyfile(kf):
        global t1, key
        if os.path.isfile(kf) == False:
            quit('The specified input key file does not exist.')
        elif os.path.getsize(kf) != 158:
            quit('The specified input key file is invalid.')
        with open(kf, 'rb') as keyfile:
            keyfile.seek(30)
            t1 = keyfile.read(32)
            keyfile.seek(126)
            key = keyfile.read(32)
        return True
    
    def decrypt12(cf, of):
        global t2, iv
        if os.path.isfile(cf) == False:
            quit('The specified input crypt12 file does not exist.')
        tf = cf+'.tmp'
        with open(cf, 'rb') as crypt12:
            crypt12.seek(3)
            t2 = crypt12.read(32)
            if t1 != t2:
                quit('Key file mismatch or crypt12 file is corrupt.')
            crypt12.seek(51)
            iv = crypt12.read(16)
            crypt12.seek(67)
            primer(tf, crypt12, 20)
        cipher = AES.new(key, AES.MODE_GCM, iv)
        sqlite = zlib.decompress(cipher.decrypt(open(tf, 'rb').read()))
        with open(of, 'wb') as msgstore:
            msgstore.write(sqlite)
            msgstore.close()
            os.remove(tf)
        return True
    
    def primer(tf, crypt12, sb):
        with open(tf, 'wb') as header:
    	    header.write(crypt12.read())
    	    header.close()
        with open(tf, 'rb+') as footer:
            footer.seek(-sb, os.SEEK_END)
            footer.truncate()
            footer.close()
    
    def validate(ms):
        with open(ms, 'rb') as msgstore:
            if msgstore.read(6).decode('ascii').lower() != 'sqlite':
                os.remove(ms)
                msg = 'Decryption of crypt12 file has failed.'
            else:
                msg = 'Decryption of crypt12 file was successful.'
        msgstore.close()
        quit(msg)
    
    def main():
        if len(sys.argv) > 2 and len(sys.argv) < 5:
            if len(sys.argv) == 3:
                outfile = 'msgstore.db'
            else:
                outfile = sys.argv[3]
            if keyfile(sys.argv[1]) and decrypt12(sys.argv[2], outfile):
                validate(outfile)
        else:
            print('\nWhatsApp Crypt12 Database Decrypter '+__version__+' '+__copyright__+' by '+__author__+'\n')
            print('\tUsage: python '+str(sys.argv[0])+' key msgstore.db.crypt12 msgstore.db\n')
    
    if __name__ == "__main__":
    main()

    Code:
    Usage: python decrypt12.py key msgstore.db.crypt12 msgstore.db

    Java:
    Code:
    package decrypt12;
    
    /*
     * 
     *** decrypt12.jar: Decrypts WhatsApp msgstore.db.crypt12 files. ***
     *
     * Author		:	TripCode
     * Copyright		:	Copyright (C) 2016
     * License		:	GPLv3
     * Status		:	Production
     * Version		:	1.0
     *
     */
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.security.Security;
    import java.util.zip.Inflater;
    import java.util.zip.InflaterInputStream;
    import javax.crypto.Cipher;
    import javax.crypto.CipherInputStream;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    // import org.spongycastle.jce.provider.BouncyCastleProvider; // Android
    
    public class decrypt12 {
    
    	static {
    		Security.insertProviderAt(new org.bouncycastle.jce.provider.BouncyCastleProvider(), 1);
    		// Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1); // Android
    	}
    
    	public static void decrypt(String KeyFile, String C12File, String SQLFile) throws Exception {
    
    		final File tempFile = new File(System.getProperty("java.io.tmpdir") + "/"
    				+ (int) (System.currentTimeMillis() / 1000L) + "-msgstore.enc");
    
    		if (!new File(KeyFile).isFile())
    			quit("The specified input key file does not exist.");
    
    		else if (new File(KeyFile).length() != 158)
    			quit("The specified input key file is invalid.");
    
    		else if (!new File(C12File).isFile())
    			quit("The specified input crypt12 file does not exist.");
    
    		InputStream KeyIn = new FileInputStream(KeyFile);
    		InputStream WdbIn = new BufferedInputStream(new FileInputStream(C12File));
    
    		byte[] KeyData = new byte[158];
    		KeyIn.read(KeyData);
    		byte[] T1 = new byte[32];
    		System.arraycopy(KeyData, 30, T1, 0, 32);
    		byte[] KEY = new byte[32];
    		System.arraycopy(KeyData, 126, KEY, 0, 32);
    		KeyIn.close();
    
    		byte[] C12Data = new byte[67];
    		WdbIn.read(C12Data);
    		byte[] T2 = new byte[32];
    		System.arraycopy(C12Data, 3, T2, 0, 32);
    		byte[] IV = new byte[16];
    		System.arraycopy(C12Data, 51, IV, 0, 16);
    
    		if (!new String(T1, 0, T1.length, "ASCII").equals(new String(T2, 0, T2.length, "ASCII")))
    			quit("Key file mismatch or crypt12 file is corrupt.");
    
    		int InputLength = WdbIn.available();
    		RandomAccessFile raf = new RandomAccessFile(tempFile, "rw");
    
    		byte[] tempBuffer = new byte[1024];
    		int I;
    
    		while ((I = WdbIn.read(tempBuffer)) != -1)
    			raf.write(tempBuffer, 0, I);
    		raf.setLength(InputLength - 20);
    		raf.close();
    		WdbIn.close();
    
    		InputStream PdbSt = new BufferedInputStream(new FileInputStream(tempFile));
    
    		Cipher cipher;
    		Security.addProvider(new BouncyCastleProvider());
    		cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); // BouncyCastle
    		// cipher = Cipher.getInstance("AES/GCM/NoPadding", "SC"); // SpongyCastle (Android)
    
    		cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(KEY, "AES"), new IvParameterSpec(IV));
    		CipherInputStream CipherStream = new CipherInputStream(PdbSt, cipher);
    
    		InflaterInputStream CryptOutput = new InflaterInputStream(CipherStream, new Inflater(false));
    
    		try {
    			FileOutputStream InflateBuffer = new FileOutputStream(SQLFile);
    			int N = 0;
    			byte[] CryptBuffer = new byte[8192];
    
    			while ((N = CryptOutput.read(CryptBuffer)) != -1) {
    				InflateBuffer.write(CryptBuffer, 0, N);
    			}
    			InflateBuffer.close();
    
    		} catch (IOException ex) {
    			quit("Fatal error:" + ex);
    		}
    
    		CipherStream.close();
    		tempFile.delete();
    
    		InputStream SqlDB = new FileInputStream(SQLFile);
    
    		byte[] SqlData = new byte[6];
    		SqlDB.read(SqlData);
    		byte[] MS = new byte[6];
    		System.arraycopy(SqlData, 0, MS, 0, 6);
    		SqlDB.close();
    
    		if (!new String(MS, 0, MS.length, "ASCII").toLowerCase().equals("sqlite")) {
    			new File(SQLFile).delete();
    			quit("Decryption of crypt12 file has failed.");
    		}
    
    		else
    			quit("Decryption of crypt12 file was successful.");
    	}
    
    	private static void quit(String Msg) {
    		System.out.println(Msg);
    		System.exit(0);
    	}
    
    	public static void main(String[] args) throws Exception {
    
    		String outFile;
    		if (args.length > 1 && args.length < 4) {
    			if (args.length == 3)
    				outFile = args[2];
    			else
    				outFile = "msgstore.db";
    			decrypt(args[0], args[1], outFile);
    		} else {
    			System.out.println("\nWhatsApp Crypt12 Database Decrypter 1.0 Copyright (C) 2016 by TripCode");
    			System.out.println("\tUsage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db\n");
    		}
    	}
    
    }

    Code:
    Usage: java -jar decrypt12.jar key msgstore.db.crypt12 msgstore.db

    GitHub Repository: WhatsApp-Crypt12-Decrypter