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);
}
}
}
Related
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());
}
}
}
I have a JAVA code that does the AES encryption of excel file on the Windows Operating System. I want to decrypt the same file on the MacOs Operating System using NodeJS. I've written a decrypt function in NodeJs which is giving me the following error
error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
Here is the JAVA Code
import java.security.Key;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class fileEncrypter {
try {
final SecretKeySpec key = new SecretKeySpec("1234".getBytes(), "AES");
final Cipher instance = Cipher.getInstance("AES/ECB/PKCS5Padding");
final BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(name));
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(name2));
instance.init(1, key);
final byte[] b = new byte[4096];
for (int i = bufferedInputStream.read(b); i > -1; i = bufferedInputStream.read(b)) {
final byte[] update = instance.update(b, 0, I);
bufferedOutputStream.write(update, 0, update.length);
}
bufferedInputStream.close();
final byte[] doFinal = instance.doFinal();
bufferedOutputStream.write(doFinal, 0, doFinal.length);
bufferedOutputStream.flush();
bufferedOutputStream.close();
return "success";
} catch(Exception obj) {
System.err.println("Exception occured while encryption:" + obj);
obj.printStackTrace();
return obj.toString();
}
}
Here is the NodeJs code to decrypt
function Decrypt_AES() {
const ALGORITHM = 'aes-128-ecb';
const ENCRYPTION_KEY = "1234";
var decipher = crypto.createDecipher(ALGORITHM, ENCRYPTION_KEY);
decipher.setAutoPadding(true);
var input = fs.createReadStream('test.enc');
var output = fs.createWriteStream('test_copy.xls');
input.pipe(decipher).pipe(output);
output.on('finish', function () {
console.log('Encrypted file written to disk!');
});
output.on('error', function (e) {
console.log(e);
});
}
I've created some examples in both Java and Node.js that will encrypt and decrypt files. The code will be compatible as long as the same key and IV values are used, that is to say the Node code will decrypt the output from the Java and vice-versa.
Now I'm using text files as input here, but you can use any file type as input.
I've updated to use 128-bit AES in ECB mode.
The Key cannot be "1234" since it must be 128-bits long, so I've used the key given below (16 bytes / 128 bits).
Java
import java.io.*;
import javax.crypto.*;
import java.security.*;
import javax.crypto.spec.*;
public class fileEncrypter {
private static final String key = "0123456789ABDCEF";
public static void main(String[] args)
{
encryptFile(key, "java-input.txt", "java-output.txt");
decryptFile(key, "java-output.txt", "java-decrypted.txt");
}
public static void encryptFile(String secret, String inputFile, String outputFile)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secret.getBytes(), "AES"));
byte[] inputData = readFile(inputFile);
byte[] outputData = cipher.doFinal(inputData);
writeToFile(outputFile, outputData);
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
}
public static void decryptFile(String secret, String inputFile, String outputFile)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secret.getBytes(), "AES"));
byte[] inputData = readFile(inputFile);
byte[] outputData = cipher.doFinal(inputData);
writeToFile(outputFile, outputData);
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
}
private static byte[] readFile(String fileName) throws IOException {
byte[] data = new byte[(int) new File(fileName).length()];
DataInputStream dis = new DataInputStream(new FileInputStream(fileName));
dis.readFully(data);
dis.close();
return data;
}
private static void writeToFile(String fileName, byte[] data) throws IOException {
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(fileName));
bufferedOutputStream.write(data, 0, data.length);
bufferedOutputStream.close();
}
}
Node.js
const crypto = require("crypto");
const Algorithm = "aes-128-ecb";
const fs = require("fs");
function encryptFile(key, inputFile, outputFile) {
const inputData = fs.readFileSync(inputFile);
const cipher = crypto.createCipheriv(Algorithm, key, Buffer.alloc(0));
const output = Buffer.concat([cipher.update(inputData) , cipher.final()]);
fs.writeFileSync(outputFile, output);
}
function decryptFile(key, inputFile, outputFile) {
const inputData = fs.readFileSync(inputFile);
const cipher = crypto.createDecipheriv(Algorithm, key, Buffer.alloc(0));
const output = Buffer.concat([cipher.update(inputData) , cipher.final()]);
fs.writeFileSync(outputFile, output);
}
const KEY = Buffer.from("0123456789ABDCEF", "utf8");
encryptFile(KEY, "node-input.txt", "node-output.txt");
decryptFile(KEY, "node-output.txt", "node-decrypted.txt");
I have solved this problem with the help of Terry Lennox. A big thanks to him first.
First Error:
error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
This was a padding issue, By default, nodeJs uses PKCS padding so I had to set auto padding to false by using decipher.setAutoPadding(false);
This solved the first error
Secondly, I was using crypto.createDecipher(ALGORITHM, ENCRYPTION_KEY) which processes the key before using it. To solve this I changed the function to
crypto.createDecipheriv(ALGORITHM, ENCRYPTION_KEY, null)
This was giving me error as we cannot pass null in place of IV value.
Since ECB does not require any IV, and we cannot pass null in place of IV I had to set my IV value to this Buffer.alloc(0)
This is the code that worked for me
function Decrypt_AES() {
const ALGORITHM = 'aes-128-ecb';
const key = "1234";
const ENCRYPTION_KEY = key;
var IV = Buffer.alloc(0);
var decipher = crypto.createDecipheriv(ALGORITHM, ENCRYPTION_KEY, IV);
decipher.setAutoPadding(false);
var input = fs.createReadStream('test.enc');
var output = fs.createWriteStream('test_copy.xls');
input.pipe(decipher).pipe(output);
output.on('finish', function () {
console.log('Decrypted file written to disk!');
});
output.on('error', function (e) {
console.log(e);
});
}
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 am using this below (E.1) for my application, there is obviously a huge glaring security hole in this that I recognize and understand. I have grown interested in encryption and want to understand it better, I need to generate a random key along with an IV but am unsure how to do so properly Can someone explain to me whom is familiar with AES encryption how this works (IV & KEY) So I am better able to understand in the future and can apply my knowledge, essentially I just want to make the code more secure, thank you.
(E.1)
byte[] key = "mykey".getBytes("UTF-8");
private byte[] getKeyBytes(final byte[] key) throws Exception {
byte[] keyBytes = new byte[16];
System.arraycopy(key, 0, keyBytes, 0, Math.min(key.length, keyBytes.length));
return keyBytes;
}
public Cipher getCipherEncrypt(final byte[] key) throws Exception {
byte[] keyBytes = getKeyBytes(key);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher;
}
public void encrypt(File in, File output, byte[] key) throws Exception {
Cipher cipher = getCipherEncrypt(key);
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(in);
fos = new FileOutputStream(output);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[1024];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
System.out.println(new String(data, "UTF-8").trim());
}
cos.flush();
} finally {
System.out.println("performed encrypt method now closing streams:\n" + output.toString());
cos.close();
fos.close();
fis.close();
}
}
public void watchMeEncrypt(){
encrypt(file, new File ("example.txt),key);
An AES key simply consists of random bytes. For CBC mode the IV mode should also be randomized (at least to an attacker). So in general you can simply use a SecureRandom instance to create the key and IV. The IV can then be included with the ciphertext; usually it is simply put in front of it.
With Java it is better to use a KeyGenerator though. If you look at the implementation of it in the SUN provider it will probably amount to the same thing. However using a KeyGenerator is more compatible with various kinds of keys and providers. It may well be that it is a requirement for generating keys in e.g. smart cards and HSM's.
So lets show a class with three simple methods:
package nl.owlstead.stackoverflow;
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.SecureRandom;
import java.util.Optional;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class CreateKeyAndIVForAES_CBC {
public static SecretKey createKey(final String algorithm, final int keysize, final Optional<Provider> provider, final Optional<SecureRandom> rng) throws NoSuchAlgorithmException {
final KeyGenerator keyGenerator;
if (provider.isPresent()) {
keyGenerator = KeyGenerator.getInstance(algorithm, provider.get());
} else {
keyGenerator = KeyGenerator.getInstance(algorithm);
}
if (rng.isPresent()) {
keyGenerator.init(keysize, rng.get());
} else {
// not really needed for the Sun provider which handles null OK
keyGenerator.init(keysize);
}
return keyGenerator.generateKey();
}
public static IvParameterSpec createIV(final int ivSizeBytes, final Optional<SecureRandom> rng) {
final byte[] iv = new byte[ivSizeBytes];
final SecureRandom theRNG = rng.orElse(new SecureRandom());
theRNG.nextBytes(iv);
return new IvParameterSpec(iv);
}
public static IvParameterSpec readIV(final int ivSizeBytes, final InputStream is) throws IOException {
final byte[] iv = new byte[ivSizeBytes];
int offset = 0;
while (offset < ivSizeBytes) {
final int read = is.read(iv, offset, ivSizeBytes - offset);
if (read == -1) {
throw new IOException("Too few bytes for IV in input stream");
}
offset += read;
}
return new IvParameterSpec(iv);
}
public static void main(String[] args) throws Exception {
final SecureRandom rng = new SecureRandom();
// you somehow need to distribute this key
final SecretKey aesKey = createKey("AES", 128, Optional.empty(), Optional.of(rng));
final byte[] plaintext = "owlstead".getBytes(UTF_8);
final byte[] ciphertext;
{
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final IvParameterSpec ivForCBC = createIV(aesCBC.getBlockSize(), Optional.of(rng));
aesCBC.init(Cipher.ENCRYPT_MODE, aesKey, ivForCBC);
baos.write(ivForCBC.getIV());
try (final CipherOutputStream cos = new CipherOutputStream(baos, aesCBC)) {
cos.write(plaintext);
}
ciphertext = baos.toByteArray();
}
final byte[] decrypted;
{
final ByteArrayInputStream bais = new ByteArrayInputStream(ciphertext);
final Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding");
final IvParameterSpec ivForCBC = readIV(aesCBC.getBlockSize(), bais);
aesCBC.init(Cipher.DECRYPT_MODE, aesKey, ivForCBC);
final byte[] buf = new byte[1_024];
try (final CipherInputStream cis = new CipherInputStream(bais, aesCBC);
final ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int read;
while ((read = cis.read(buf)) != -1) {
baos.write(buf, 0, read);
}
decrypted = baos.toByteArray();
}
}
System.out.println(new String(decrypted, UTF_8));
}
}
Note that you may not always want to generate and distribute an AES key "out-of-band". Here are a few other methods of generating a key (part #2 onwards). You may also want to take a look at more advanced exception handling for the cryptographic operation.
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 ?