I have been encrypted file in Python, decrypted file in Android (JAVA). But the decrypted file is not available.
I use AES/CBC. The decrypted MD5 is different from the original file, and decrypted file is not available.
This is my python code that used in encrypt.
from Crypto import Random
from Crypto.Cipher import AES
import hashlib
class AESCipher:
MODE = AES.MODE_CBC
KEY_BYTES = 32
BLOCK_SIZE = AES.block_size
SEGMENT_SIZE = 128
def __init__(self, key: str):
self.key = hashlib.sha256(key.encode()).digest()
def encrypt_file(self, file_path, output_path):
'''
Encrypt file
'''
chunk_size = 64 * 1024
files_ize = str(os.path.getsize(file_path)).zfill(16)
IV = Random.new().read(self.BLOCK_SIZE)
encryptor = AES.new(self.key, AES.MODE_CBC, IV, segment_size=self.SEGMENT_SIZE)
with open(file_path, 'rb') as infile:
with open(output_path, 'wb') as outfile:
outfile.write(files_ize.encode('utf-8'))
outfile.write(IV)
while True:
chunk = infile.read(chunk_size)
if len(chunk) == 0:
break
elif len(chunk) % self.BLOCK_SIZE != 0:
chunk += b' ' * (self.BLOCK_SIZE - (len(chunk) % self.BLOCK_SIZE))
outfile.write(encryptor.encrypt(chunk))
This is my Android code that used in decrypt.
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.io.UnsupportedEncodingException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESCipher {
private static final String AES_ENCRYPTION_ALGORITHM = "AES";
private static final String CIPHER_TRANSFORMATION = "AES/CBC/NoPadding";
private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";
private static final String ENCODING = "UTF-8";
private static final String KEY_STR = "password";
private static final int IV_SIZE = 16;
private static byte[] KEY;
private static AESCipher instance = null;
AESCipher() {
try {
MessageDigest messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
messageDigest.update(KEY_STR.getBytes(ENCODING));
KEY = messageDigest.digest();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static AESCipher getInstance() {
if (instance == null) {
instance = new AESCipher();
}
return instance;
}
public void DecryptFileFromAsset(Context context, String fileName, String outputName) {
try {
AssetFileDescriptor assetFileDescriptor = context.getAssets().openFd(fileName);
int encryptedFileSize = (int) assetFileDescriptor.getLength();
// load file
InputStream inputStream = context.getAssets().open(fileName);
// get file size
byte[] fileSizeBytes = new byte[16];
inputStream.read(fileSizeBytes);
int fileSize = Integer.parseInt(new String(fileSizeBytes));
// get IV
byte[] ivBytes = new byte[IV_SIZE];
inputStream.read(ivBytes);
// initialize AES cipher
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
SecretKeySpec keySpec = new SecretKeySpec(KEY, AES_ENCRYPTION_ALGORITHM);
Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivParameterSpec);
ByteArrayOutputStream decryptedBytesBuffer = new ByteArrayOutputStream();
byte[] encryptedBytes = new byte[64 * 1024];
while (inputStream.read(encryptedBytes) != -1) {
decryptedBytesBuffer.write(cipher.doFinal(encryptedBytes));
}
File file = new File(context.getFilesDir() + outputName);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(decryptedBytesBuffer.toByteArray(), 0, fileSize);
fileOutputStream.flush();
fileOutputStream.close();
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
Related
I have to decrypt some data that are sent encrypted using AES256 Rijndael.
I have the encryption/decryption mechanism used by my partner, developed in JAVA, that I'm not very familiar with, but can't transpose it in C#.
The secret key that has been given to me is 10 chars long.
I think that below code is ok, excepted the IV calculation.
You'll find first the java code, and then the C# :
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class UtilsCrypto {
/* Rijndael/CFB8/NoPadding is default cipher */
final static String CHIPHER = "Rijndael/CFB8/NoPadding";
public static final String MESSAGE_DIGEST_ALGORITHM = "MD5";
public static final String AES = "AES";
public static final String AES_ECB_NO_PADDING = "AES/ECB/NoPadding";
private static byte[] md5(final String input) throws NoSuchAlgorithmException {
final MessageDigest md = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
return md.digest(input.getBytes());
}
private Cipher initCipher(final int mode, final String secretKey) throws Exception {
final byte[] key = md5(secretKey);
final byte[] iv = md5(secretKey);
final SecretKeySpec skeySpec = new SecretKeySpec(key, AES);
/* This valid with other ciphers than Rijndael/CFB8/NoPadding */
// final IvParameterSpec initialVector = new IvParameterSpec(iv);
/* Use this with Rijndael/CFB8/NoPadding */
final IvParameterSpec initialVector = new IvParameterSpec(getIvBytes(iv));
final Cipher cipher = Cipher.getInstance(CHIPHER);
cipher.init(mode, skeySpec, initialVector);
return cipher;
}
public String encrypt(final String dataToEncrypt, final String secretKey) {
if (Utils.isEmpty(secretKey))
return dataToEncrypt;
String encryptedData = null;
try {
final Cipher cipher = initCipher(Cipher.ENCRYPT_MODE, secretKey);
final byte[] encryptedByteArray = cipher.doFinal(dataToEncrypt.getBytes(Charset.forName("UTF8")));
final BASE64Encoder enc = new BASE64Encoder();
encryptedData = enc.encode(encryptedByteArray);
encryptedData = encryptedData.replace("+", "-");
encryptedData = encryptedData.replace("/", "_");
} catch (Exception e) {
System.err.println("Problem encrypting the data");
e.printStackTrace();
}
return encryptedData;
}
public String decrypt(final String encryptedData, final String secretKey) {
String decryptedData = null;
String inData = encryptedData;
try {
final Cipher cipher = initCipher(Cipher.DECRYPT_MODE, secretKey);
final BASE64Decoder dec = new BASE64Decoder();
inData = inData.replace("-", "+");
inData = inData.replace("_", "/");
final byte[] encryptedByteArray = dec.decodeBuffer(inData); // ok
final byte[] decryptedByteArray = cipher.doFinal(encryptedByteArray);
decryptedData = new String(decryptedByteArray, "UTF8");
} catch (Exception e) {
System.err.println("Problem decrypting the data");
e.printStackTrace();
}
return decryptedData;
}
/**
* This method is only for Rijndael/CFB8/NoPadding
*
* #param hashedKey
* md5
* #return byte array
* #throws Exception
* If any exceptions.
*/
// on passe en arg le hash de la clé
protected byte[] getIvBytes(byte[] hashedKey) throws Exception {
byte[] inputBytes = new byte[16]; // init son tableau a 16 bytes
final SecretKey key = new SecretKeySpec(hashedKey, AES); // secretKey
final Cipher cipher = Cipher.getInstance(AES_ECB_NO_PADDING);
cipher.init(Cipher.ENCRYPT_MODE, key); // chiffre sa clé en AES avec un IV
return cipher.doFinal(inputBytes);
}
}
now this is what I tried so far :
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Xml;
using System.Net;
using System.Web;
using System.Web.Services;
using Newtonsoft.Json;
using System.Security.Cryptography;
using System.Text;
namespace test
{
public byte[] getIVBytes(byte[] hashedKey)
{
byte[] inputBytes = new byte[16];
AesManaged tdes = new AesManaged();
tdes.Key = hashedKey;
tdes.Mode = CipherMode.ECB;
tdes.BlockSize = 128;
tdes.Padding = PaddingMode.None;
ICryptoTransform crypt = tdes.CreateEncryptor();
byte[] bla = crypt.TransformFinalBlock(hashedKey, 0, inputBytes.Length);
return bla;
}
[WebMethod]
public string decrypt(String input, String key)
{
byte[] md5KeyHash;
using (MD5 md5 = MD5.Create())
{
md5KeyHash = md5.ComputeHash(Encoding.UTF8.GetBytes(key));
}
input = input.Replace("-", "+");
input = input.Replace("_", "/");
input = input.Replace(" ", "");
byte[] data = Convert.FromBase64String(input); // récupérer l'array de bytes du message chiffré encodé en b64
String decrypted;
using (RijndaelManaged rijAlg = new RijndaelManaged())
{
rijAlg.Mode = CipherMode.CFB;
rijAlg.BlockSize = 128;
rijAlg.Padding = PaddingMode.None;
rijAlg.Key = md5KeyHash;
rijAlg.IV = getIVBytes(md5KeyHash);
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
using (MemoryStream msDecrypt = new MemoryStream(data))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt))
{
decrypted = srDecrypt.ReadToEnd();
}
}
}
}
return decrypted;
}
Code seems to be "correct" because no errors are thrown except this :
XML Error analysis : An Invalid character was found in text content.
At: http://localhost:55175/WebService1.asmx/decrypt
line 2, col44 :�Me����>m�H�ZԤ�af2ɾ`A�ٖ�H$�&/
What am I missing ?
There are some bugs in the C#-code:
In the getIVBytes-method, replace line
byte[] bla = crypt.TransformFinalBlock(hashedKey, 0, inputBytes.Length);
by
byte[] bla = crypt.TransformFinalBlock(inputBytes, 0, inputBytes.Length); // encrypt inputBytes
In the decrypt-method, add before the CreateDecryptor-call
rijAlg.FeedbackSize = 8; // Use CFB8
and replace line
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, null);
by
ICryptoTransform decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); // Consider the IV
Then the posted ciphertext can be decrypted with the C#-code into the posted plaintext.
Regardless of the file size, 32 bytes of additional characters are appended to each decrypted file. I could just cut off the 32 bytes, but where did they come from and how can I avoid them in the output file?
This is my source code:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
public class EtAesCrypto {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int KEY_LENGTH = 256;
private static final int SALT_LENGTH = 16;
private static final int IV_LENGTH = 12;
private static final int AUT_TAG_LENGTH = 128;
private static final int ITERATIONS = 100;
private static final String ALGORITHM = "AES";
private static final String SECRET_KEY_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final String TRANSFORMATION = "AES/GCM/NoPadding";
private String msg;
public void encrypt(String path2Original, String path2Encrypted, String password) {
try (FileOutputStream out = new FileOutputStream(path2Encrypted)) {
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
SecureRandom secureRandom = new SecureRandom();
secureRandom.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
secureRandom.nextBytes(iv);
logger.trace("IV length: {}", iv.length);
out.write(salt);
out.write(iv);
Cipher ci = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(AUT_TAG_LENGTH, iv);
ci.init(Cipher.ENCRYPT_MODE, skey, parameterSpec);
try (FileInputStream in = new FileInputStream(path2Original)) {
processStream(ci, in, out);
}
} catch (Exception e) {
throw new RuntimeException("Encryption of file with id failed.");
}
}
public void decrypt(String path2Encrypted, OutputStream os, String password, String fileId) {
try (FileInputStream in = new FileInputStream(path2Encrypted)) {
doDecryption(in, os, password);
} catch (Exception e){
msg = String.format("Decryption of file with id '%s' failed.", fileId);
logger.warn("Decryption of file '{}' with id '{}' failed: {}", path2Encrypted, fileId, e.getMessage(), e);
throw new RuntimeException(msg);
}
}
public void decrypt(String path2Encrypted, String path2Decrypted, String password, String fileId) {
try (FileInputStream in = new FileInputStream(path2Encrypted)) {
try (FileOutputStream os = new FileOutputStream(path2Decrypted)) {
doDecryption(in, os, password);
}
} catch (Exception e){
msg = String.format("Decryption of file with id '%s' failed.", fileId);
logger.warn("Decryption of file '{}' with id '{}' failed: {}", path2Encrypted, fileId, e.getMessage(), e);
throw new RuntimeException(msg);
}
}
private void doDecryption(InputStream in, OutputStream out, String password) throws Exception {
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
int saltBytes = in.read(salt);
int ivBytes = in.read(iv);
SecretKeyFactory factory = SecretKeyFactory.getInstance(SECRET_KEY_ALGORITHM);
KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
SecretKeySpec skey = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
Cipher ci = Cipher.getInstance(TRANSFORMATION);
GCMParameterSpec parameterSpec = new GCMParameterSpec(AUT_TAG_LENGTH, iv);
ci.init(Cipher.ENCRYPT_MODE, skey, parameterSpec);
processStream(ci, in, out);
}
private void processStream(Cipher ci, InputStream in, OutputStream out) throws Exception {
byte[] inBuffer = new byte[1024];
int len;
while ((len = in.read(inBuffer)) != -1) {
byte[] outBuffer = ci.update(inBuffer, 0, len);
if (outBuffer != null)
out.write(outBuffer);
}
byte[] outBuffer = ci.doFinal();
if (outBuffer != null)
out.write(outBuffer);
}
}
You should use Cipher.DECRYPT_MODE when decrypting.
The additional bytes are the GCM tag (MAC). It is created during encryption and checked during decryption.
In GCM mode the process of encryption and decryption is identical (XOR), that's why decrypting with Cipher.ENCRYPT_MODE appears to work, except for the MAC part.
I have a web app which is deployed on tomcat in both Linux and windows environment . Blowfish algo has been implemented for login password encryption/security check. It works fine in windows but throws illegal key size exception in linux. key file is packed with war
I have gone through multiple post but nothing really helped me.
generating key file
/** Generate a secret TripleDES encryption/decryption key */
KeyGenerator keygen = KeyGenerator.getInstance(SecurityConstant.BLOW_FISH_ALGO);
// Use it to generate a key
SecretKey key = keygen.generateKey();
// Convert the secret key to an array of bytes like this
byte[] rawKey = key.getEncoded();
// Write the raw key to the file
String keyPath = getBlowFishKeyPath();
FileOutputStream out = new FileOutputStream(keyPath);
Writer writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
Files.write( Paths.get(keyPath),rawkey,StandardOpenOption.CREATE);
writer.close();
key comparision
String hexCipher = null;
try {
byte[] byteClearText = pwd.getBytes("UTF-8");
byte[] ivBytes = SecurityUtil.hexToBytes("0000000000000000");
// read secretkey from key file
byte[] secretKeyByte = secretKey.getBytes();
Cipher cipher = null;
SecretKeySpec key = new SecretKeySpec(secretKeyByte, SecurityConstant.BLOW_FISH_ALGO);
// Create and initialize the encryption engine
cipher = Cipher.getInstance(SecurityConstant.BLOW_FISH_CBC_ZEROBYTE_ALGO, SecurityConstant.BC);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); // throws exception
byte[] cipherText = new byte[cipher.getOutputSize(byteClearText.length)];
int ctLength = cipher.update(byteClearText, 0, byteClearText.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
hexCipher = SecurityUtil.bytesToHex(cipherText);// hexdecimal password stored in DB
} catch (Exception e) {
ExceptionLogger.logException(logger, e);
}
made it simpler to test
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.io.FileUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
public class SecTest {
public static void main(String[] args) throws NoSuchAlgorithmException {
/** Generate a secret TripleDES encryption/decryption key */
Security.addProvider(new BouncyCastleProvider());
KeyGenerator keygen = KeyGenerator.getInstance("Blowfish");
// Use it to generate a key
SecretKey key = keygen.generateKey();
// Convert the secret key to an array of bytes like this
byte[] rawKey = key.getEncoded();
// Write the raw key to the file
String keyPath = "/data2/key/BlowFish.key";
FileOutputStream out = null;
try {
out = new FileOutputStream(keyPath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Writer writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
Files.write( Paths.get(keyPath),rawKey,StandardOpenOption.CREATE);
writer.close();
out.close();
} catch (UnsupportedEncodingException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
generateHexCode("a");
}
private static void generateHexCode(String pwd) {
String hexCipher = null;
try {
byte[] byteClearText = pwd.getBytes("UTF-8");
byte[] ivBytes = hexToBytes("0000000000000000");
// read secretkey from key file
byte[] secretKeyByte = readSecretKey().getBytes();
Cipher cipher = null;
SecretKeySpec key = new SecretKeySpec(secretKeyByte, "Blowfish");
// Create and initialize the encryption engine
cipher = Cipher.getInstance("Blowfish/CBC/ZeroBytePadding", "BC");
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); // throws exception
byte[] cipherText = new byte[cipher.getOutputSize(byteClearText.length)];
int ctLength = cipher.update(byteClearText, 0, byteClearText.length, cipherText, 0);
ctLength += cipher.doFinal(cipherText, ctLength);
hexCipher = bytesToHex(cipherText);// hexdecimal password stored in DB
System.out.println("hex cipher is "+hexCipher);
} catch (Exception e) {
e.printStackTrace();
}
}
private static String readSecretKey() {
byte[] rawkey = null;
String file ="";
// Read the raw bytes from the keyfile
String keyFile = "/data2/key/BlowFish.key";
String is = null;
try {
is = FileUtils.readFileToString(new File(keyFile),"UTF-8");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return is;
}
public static byte[] hexToBytes(String str) {
byte[] bytes = null;
if (str != null && str.length() >= 2) {
int len = str.length() / 2;
byte[] buffer = new byte[len];
for (int i = 0; i < len; i++) {
buffer[i] = (byte) Integer.parseInt(str.substring(i * 2, i * 2 + 2), 16);
}
bytes = buffer;
}
return bytes;
}
public static String bytesToHex(byte[] data) {
if (data == null) {
return null;
} else {
int len = data.length;
StringBuilder str = new StringBuilder();
for (int i = 0; i < len; i++) {
if ((data[i] & 0xFF) < 16) {
str = str.append("0").append(java.lang.Integer.toHexString(data[i] & 0xFF));
} else {
str.append(java.lang.Integer.toHexString(data[i] & 0xFF));
}
}
return str.toString().toUpperCase();
}
}
}
Made a simple program to test it out . This also runs fine on windows but fails on linux. any clue ?
I was trying to decrypt Whatsapp database file (msgstore.db.crypt) with java.
I found some python code and tried to do same thing with java. Probably its not that hard thing to do but I had some problems with handling decryption key.
But finally did it. So I wanted to share the code for people who need it.
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
public class Crypto {
public FileInputStream mIn;
public FileOutputStream mOut;
public Crypto(String fileIn, String fileOut) {
try {
mIn = new FileInputStream(new File(fileIn));
mOut = new FileOutputStream(new File(fileOut));
decryptAES(mIn, mOut);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void decryptAES(InputStream in, FileOutputStream out) throws Exception {
final String string = "346a23652a46392b4d73257c67317e352e3372482177652c";
byte[] hexAsBytes = DatatypeConverter.parseHexBinary(string);
SecretKeySpec keySpec = new SecretKeySpec(hexAsBytes, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
in = new CipherInputStream(in, cipher);
byte[] buffer = new byte[24];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1)
{
out.write(buffer, 0, bytesRead);
}
}
public static void main(String[] args){
Crypto c = new Crypto("C:\\msgstore.db.crypt", "D:\\WhatsappDb");
System.out.println("Decrypting Done");
}
}
An updated answer for .crypt12 files:
These are compressed, and then encrypted using AES in GCM mode
Here is some python code showing how:
"""
Example how to decrypt whatsapp msgstore backups with extension .crypt12.
Author: Willem Hengeveld <itsme#xs4all.nl>
"""
from Crypto.Cipher import AES
import zlib
import sys
datafile = keyfile = None
if len(sys.argv)==1:
print("Usage: decrypt12.py <keyfile> <msgstore.db.crypt12>")
print(" the key file is commonly found in /data/data/com.whatsapp/files/key")
print(" the crypt file is commonly found in the directory: /data/media/0/WhatsApp/Databases/")
exit(1)
for arg in sys.argv[1:]:
if arg.find('crypt12')>0:
datafile = arg
elif arg.find('key')>0:
keyfile = arg
else:
print("unknown arg", arg)
with open(keyfile, "rb") as fh:
keydata = fh.read()
key = keydata[126:]
with open(datafile, "rb") as fh:
filedata = fh.read()
iv = filedata[51:67]
aes = AES.new(key, mode=AES.MODE_GCM, nonce=iv)
with open("msg-decrypted.db", "wb") as fh:
fh.write(zlib.decompress(aes.decrypt(filedata[67:-20])))
here is an pure java routine for .db.crypt12 without bouncycastle, but only JDK.
public class Crypt12 {
public static void main(final String[] args) {
final String c12File = "1/msgstore.db.crypt12"; // input file
final String decryptedDbFile = "1/msgstore.db"; // sqlite3 db output file
final String keyFile = "1/key";
try {
final byte[] key; try(FileInputStream s = new FileInputStream(keyFile)) { key = s.readAllBytes(); }
final byte[] buf; try(FileInputStream s = new FileInputStream(c12File)) { buf = s.readAllBytes(); }
if(!Arrays.equals(key, 27, 78, buf, 0, 51)) { System.out.println("Wrong Key-File"); return; }
final int available = buf.length - 67 - 20; // 67 Byte Header + 20 byte footer
final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
final GCMParameterSpec iv = new GCMParameterSpec(128, buf, 51, 16);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, 126, 32, "AES"), iv);
final int zipLen = cipher.doFinal(buf, 67, available, buf, 0);
final Inflater unzip = new Inflater(false);
try(FileOutputStream s = new FileOutputStream(decryptedDbFile)) {
unzip.setInput(buf, 0, zipLen);
final byte[] b = new byte[1024];
while(!unzip.needsInput()) {
final int l = unzip.inflate(b, 0, b.length);
if(l > 0) s.write(b, 0, l);
}
}
} catch (final Exception e) {
e.printStackTrace(System.out);
}
}
}
Every method I write to encode a string in Java using 3DES can't be decrypted back to the original string. Does anyone have a simple code snippet that can just encode and then decode the string back to the original string?
I know I'm making a very silly mistake somewhere in this code. Here's what I've been working with so far:
** note, I am not returning the BASE64 text from the encrypt method, and I am not base64 un-encoding in the decrypt method because I was trying to see if I was making a mistake in the BASE64 part of the puzzle.
public class TripleDESTest {
public static void main(String[] args) {
String text = "kyle boon";
byte[] codedtext = new TripleDESTest().encrypt(text);
String decodedtext = new TripleDESTest().decrypt(codedtext);
System.out.println(codedtext);
System.out.println(decodedtext);
}
public byte[] encrypt(String message) {
try {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9".getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;)
{
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
final String encodedCipherText = new sun.misc.BASE64Encoder().encode(cipherText);
return cipherText;
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
catch (BadPaddingException e) { System.out.println("Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}
return null;
}
public String decrypt(byte[] message) {
try
{
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9".getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;)
{
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
//final byte[] encData = new sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(message);
return plainText.toString();
}
catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
catch (BadPaddingException e) { System.out.println("Invalid Key");}
catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
Your code was fine except for the Base 64 encoding bit (which you mentioned was a test), the reason the output may not have made sense is that you were displaying a raw byte array (doing toString() on a byte array returns its internal Java reference, not the String representation of the contents). Here's a version that's just a teeny bit cleaned up and which prints "kyle boon" as the decoded string:
import java.security.MessageDigest;
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class TripleDESTest {
public static void main(String[] args) throws Exception {
String text = "kyle boon";
byte[] codedtext = new TripleDESTest().encrypt(text);
String decodedtext = new TripleDESTest().decrypt(codedtext);
System.out.println(codedtext); // this is a byte array, you'll just see a reference to an array
System.out.println(decodedtext); // This correctly shows "kyle boon"
}
public byte[] encrypt(String message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
final byte[] plainTextBytes = message.getBytes("utf-8");
final byte[] cipherText = cipher.doFinal(plainTextBytes);
// final String encodedCipherText = new sun.misc.BASE64Encoder()
// .encode(cipherText);
return cipherText;
}
public String decrypt(byte[] message) throws Exception {
final MessageDigest md = MessageDigest.getInstance("md5");
final byte[] digestOfPassword = md.digest("HG58YZ3CR9"
.getBytes("utf-8"));
final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24);
for (int j = 0, k = 16; j < 8;) {
keyBytes[k++] = keyBytes[j++];
}
final SecretKey key = new SecretKeySpec(keyBytes, "DESede");
final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
decipher.init(Cipher.DECRYPT_MODE, key, iv);
// final byte[] encData = new
// sun.misc.BASE64Decoder().decodeBuffer(message);
final byte[] plainText = decipher.doFinal(message);
return new String(plainText, "UTF-8");
}
}
Here is a solution using the javax.crypto library and the apache commons codec library for encoding and decoding in Base64:
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import org.apache.commons.codec.binary.Base64;
public class TrippleDes {
private static final String UNICODE_FORMAT = "UTF8";
public static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String myEncryptionKey;
private String myEncryptionScheme;
SecretKey key;
public TrippleDes() throws Exception {
myEncryptionKey = "ThisIsSpartaThisIsSparta";
myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME;
arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(myEncryptionScheme);
cipher = Cipher.getInstance(myEncryptionScheme);
key = skf.generateSecret(ks);
}
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
public static void main(String args []) throws Exception
{
TrippleDes td= new TrippleDes();
String target="imparator";
String encrypted=td.encrypt(target);
String decrypted=td.decrypt(encrypted);
System.out.println("String To Encrypt: "+ target);
System.out.println("Encrypted String:" + encrypted);
System.out.println("Decrypted String:" + decrypted);
}
}
Running the above program results with the following output:
String To Encrypt: imparator
Encrypted String:FdBNaYWfjpWN9eYghMpbRA==
Decrypted String:imparator
I had hard times figuring it out myself and this post helped me to find the right answer for my case. When working with financial messaging as ISO-8583 the 3DES requirements are quite specific, so for my especial case the "DESede/CBC/PKCS5Padding" combinations wasn't solving the problem. After some comparative testing of my results against some 3DES calculators designed for the financial world I found the the value "DESede/ECB/Nopadding" is more suited for the the specific task.
Here is a demo implementation of my TripleDes class (using the Bouncy Castle provider)
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
*
* #author Jose Luis Montes de Oca
*/
public class TripleDesCipher {
private static String TRIPLE_DES_TRANSFORMATION = "DESede/ECB/Nopadding";
private static String ALGORITHM = "DESede";
private static String BOUNCY_CASTLE_PROVIDER = "BC";
private Cipher encrypter;
private Cipher decrypter;
public TripleDesCipher(byte[] key) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
encrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER);
encrypter.init(Cipher.ENCRYPT_MODE, keySpec);
decrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER);
decrypter.init(Cipher.DECRYPT_MODE, keySpec);
}
public byte[] encode(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
return encrypter.doFinal(input);
}
public byte[] decode(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
return decrypter.doFinal(input);
}
}
Here's a very simply static encrypt/decrypt class biased on the Bouncy Castle no padding example by Jose Luis Montes de Oca. This one is using "DESede/ECB/PKCS7Padding" so I don't have to bother manually padding.
package com.zenimax.encryption;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
/**
*
* #author Matthew H. Wagner
*/
public class TripleDesBouncyCastle {
private static String TRIPLE_DES_TRANSFORMATION = "DESede/ECB/PKCS7Padding";
private static String ALGORITHM = "DESede";
private static String BOUNCY_CASTLE_PROVIDER = "BC";
private static void init()
{
Security.addProvider(new BouncyCastleProvider());
}
public static byte[] encode(byte[] input, byte[] key)
throws IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, InvalidKeyException {
init();
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher encrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION,
BOUNCY_CASTLE_PROVIDER);
encrypter.init(Cipher.ENCRYPT_MODE, keySpec);
return encrypter.doFinal(input);
}
public static byte[] decode(byte[] input, byte[] key)
throws IllegalBlockSizeException, BadPaddingException,
NoSuchAlgorithmException, NoSuchProviderException,
NoSuchPaddingException, InvalidKeyException {
init();
SecretKey keySpec = new SecretKeySpec(key, ALGORITHM);
Cipher decrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION,
BOUNCY_CASTLE_PROVIDER);
decrypter.init(Cipher.DECRYPT_MODE, keySpec);
return decrypter.doFinal(input);
}
}
private static final String UNICODE_FORMAT = "UTF8";
private static final String DESEDE_ENCRYPTION_SCHEME = "DESede";
private KeySpec ks;
private SecretKeyFactory skf;
private Cipher cipher;
byte[] arrayBytes;
private String encryptionSecretKey = "ThisIsSpartaThisIsSparta";
SecretKey key;
public TripleDesEncryptDecrypt() throws Exception {
convertStringToSecretKey(encryptionSecretKey);
}
public TripleDesEncryptDecrypt(String encryptionSecretKey) throws Exception {
convertStringToSecretKey(encryptionSecretKey);
}
public SecretKey convertStringToSecretKey (String encryptionSecretKey) throws Exception {
arrayBytes = encryptionSecretKey.getBytes(UNICODE_FORMAT);
ks = new DESedeKeySpec(arrayBytes);
skf = SecretKeyFactory.getInstance(DESEDE_ENCRYPTION_SCHEME);
cipher = Cipher.getInstance(DESEDE_ENCRYPTION_SCHEME);
key = skf.generateSecret(ks);
return key;
}
/**
* Encrypt without specifying secret key
*
* #param unencryptedString
* #return String
*/
public String encrypt(String unencryptedString) {
String encryptedString = null;
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Encrypt with specified secret key
*
* #param unencryptedString
* #return String
*/
public String encrypt(String encryptionSecretKey, String unencryptedString) {
String encryptedString = null;
try {
key = convertStringToSecretKey(encryptionSecretKey);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT);
byte[] encryptedText = cipher.doFinal(plainText);
encryptedString = new String(Base64.encodeBase64(encryptedText));
} catch (Exception e) {
e.printStackTrace();
}
return encryptedString;
}
/**
* Decrypt without specifying secret key
* #param encryptedString
* #return
*/
public String decrypt(String encryptedString) {
String decryptedText=null;
try {
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
/**
* Decrypt with specified secret key
* #param encryptedString
* #return
*/
public String decrypt(String encryptionSecretKey, String encryptedString) {
String decryptedText=null;
try {
key = convertStringToSecretKey(encryptionSecretKey);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] encryptedText = Base64.decodeBase64(encryptedString);
byte[] plainText = cipher.doFinal(encryptedText);
decryptedText= new String(plainText);
} catch (Exception e) {
e.printStackTrace();
}
return decryptedText;
}
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.util.Base64;
import java.util.Base64.Encoder;
/**
*
* #author shivshankar pal
*
* this code is working properly. doing proper encription and decription
note:- it will work only with jdk8
*
*
*/
public class TDes {
private static byte[] key = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02,
0x02, 0x02, 0x02, 0x02, 0x02, 0x02 };
private static byte[] keyiv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00 };
public static String encode(String args) {
System.out.println("plain data==> " + args);
byte[] encoding;
try {
encoding = Base64.getEncoder().encode(args.getBytes("UTF-8"));
System.out.println("Base64.encodeBase64==>" + new String(encoding));
byte[] str5 = des3EncodeCBC(key, keyiv, encoding);
System.out.println("des3EncodeCBC==> " + new String(str5));
byte[] encoding1 = Base64.getEncoder().encode(str5);
System.out.println("Base64.encodeBase64==> " + new String(encoding1));
return new String(encoding1);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
public static String decode(String args) {
try {
System.out.println("encrypted data==>" + new String(args.getBytes("UTF-8")));
byte[] decode = Base64.getDecoder().decode(args.getBytes("UTF-8"));
System.out.println("Base64.decodeBase64(main encription)==>" + new String(decode));
byte[] str6 = des3DecodeCBC(key, keyiv, decode);
System.out.println("des3DecodeCBC==>" + new String(str6));
String data=new String(str6);
byte[] decode1 = Base64.getDecoder().decode(data.trim().getBytes("UTF-8"));
System.out.println("plaintext==> " + new String(decode1));
return new String(decode1);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "u mistaken in try block";
}
private static byte[] des3EncodeCBC(byte[] key, byte[] keyiv, byte[] data) {
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/ CBC/PKCS5Padding");
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.ENCRYPT_MODE, deskey, ips);
byte[] bout = cipher.doFinal(data);
return bout;
} catch (Exception e) {
System.out.println("methods qualified name" + e);
}
return null;
}
private static byte[] des3DecodeCBC(byte[] key, byte[] keyiv, byte[] data) {
try {
Key deskey = null;
DESedeKeySpec spec = new DESedeKeySpec(key);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("desede");
deskey = keyfactory.generateSecret(spec);
Cipher cipher = Cipher.getInstance("desede/ CBC/NoPadding");//PKCS5Padding NoPadding
IvParameterSpec ips = new IvParameterSpec(keyiv);
cipher.init(Cipher.DECRYPT_MODE, deskey, ips);
byte[] bout = cipher.doFinal(data);
return bout;
} catch (Exception e) {
System.out.println("methods qualified name" + e);
}
return null;
}
}