Decrypts encrypted archive files from Titanium Backup for Android.
If you use this program, please let me know via a message on Github. Thanks!
https://github.com/bhafer/TitaniumBackupDecrypt
=====================
Dependencies:
http://phpseclib.sourceforge.net/ :: Install via PEAR with:
pear channel-discover phpseclib.sourceforge.net
pear install phpseclib/Crypt_AES phpseclib/Crypt_RSA
Usage:
php TitaniumBackupDecrypt <archive-file>
Will check for TB_ARCHIVE_PASSWORD environment variable or else prompt for password.
--OR--
php TitaniumBackupDecrypt <archive-file> <password>
Where archive-file is a file of one of the following types:
.tar.bz2
.tar.gz
.tar.lzop
.tar
.xml.bz2
.xml.gz
.xml.lzop
.xml
Based on the file format specification information from Titanium Backup at:
https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5
=====================
Snapshot of https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5 ::
So, the file format for encrypted data backups is as follows:
"TB_ARMOR_V1" '\n' passphraseHmacKey '\n' passphraseHmacResult '\n' publicKey '\n' encryptedPrivateKey '\n' encryptedSessionKey '\n' Data
Each of the 5 "variables" (passphraseHmacKey, passphraseHmacResult, publicKey, encryptedPrivateKey, encryptedSessionKey) is stored in Base64 format without linewraps (of course) and can be decoded with: Base64.decode(passphraseHmacKey, Base64.NO_WRAP)
Then the user-supplied passphrase (String) can be verified as follows:
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(passphraseHmacKey, "HmacSHA1"));
byte[] sigBytes = mac.doFinal(passphrase.getBytes("UTF-8"));
boolean passphraseMatches = Arrays.equals(sigBytes, passphraseHmacResult);
Then the passphrase is independently hashed with SHA-1. We append [twelve] 0x00 bytes to the 160-bit result to constitute the 256-bit AES key which is used to decrypt "encryptedPrivateKey" (with an IV of [sixteen] 0x00 bytes). [Decrypt using AES-256 in CBC mode and perform PKCS5 unpadding. Note that AES-256 is equivalent to Rijndael-128 using a 256 bit key.]
Then we build the KeyPair object as follows:
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey2 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
PublicKey publicKey2 = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
KeyPair keyPair = new KeyPair(publicKey2, privateKey2);
Then we decrypt the session key as follows:
Cipher rsaDecrypt = Cipher.getInstance("RSA/NONE/PKCS1Padding");
rsaDecrypt.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(baos, rsaDecrypt);
cos.write(encryptedSessionKey);
cos.close();
byte[] sessionKey = baos.toByteArray();
And finally, we decrypt the data itself with the session key (which can be either a 128-bit, 192-bit or 256-bit key) and with a 0x00 IV.
While the "zero" IV is suboptimal from a security standpoint, it allows files to be encoded faster - because every little bit counts, especially when we store backups with LZO compression.
=====================
Docker usage: (until a full image is built)
First, build your own image with Docker
docker build -t TitaniumBackupDecrypt .
Then, run as a standalone container.
You MUST mount the directory that holds TitaniumBackup files into the container.
Example for Windows hosts
docker run --rm -ti -v C:\Users\Example\TitaniumBackup:/app /app/encrypted-backup.tar.gz
Example for Linux hosts
docker run --rm -ti -v /home/example/TitaniumBackup:/app /app/encrypted-backup.tar.gz