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.
Related
I am trying to encrypt/decrypt using AES-GCM and JDK 1.8 CipherOutputStream, But getting BadPaddingException during decryption. I am using same IV and secret key during encryption and decryption, but not sure what is going wrong. Please see the code below:
static String AES_GCM_MODE = "AES/GCM/NoPadding";
SecretKey secretKey;
public SymmetricFileEncryption(){
Security.insertProviderAt( new BouncyCastleProvider(), 1);
setSecretKey();
}
public static void main(String[] args) throws Exception {
File inputFile = new File("test.txt");
File outputFile = new File("test-crypt.txt");
File out = new File("test-decrypt.txt");
SymmetricFileEncryption sym = new SymmetricFileEncryption();
sym.encrypt(inputFile, outputFile);
sym.decrypt(outputFile, out);
}
public Cipher getEncryptionCipher() throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(), new IvParameterSpec(getInitializationVector()) );
return cipher;
}
private Cipher getDecryptionCipher(File inputFile) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchPaddingException, NoSuchAlgorithmException, IOException, NoSuchProviderException {
//initialize cipher
Cipher cipher = Cipher.getInstance(AES_GCM_MODE, "BC");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, getInitializationVector());
cipher.init(Cipher.DECRYPT_MODE, getSecretKey(),new IvParameterSpec(getInitializationVector()) );
return cipher;
}
public void encrypt(File inputFile, File outputFile) throws Exception {
Cipher cipher = getEncryptionCipher();
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(inputFile);
fos = new FileOutputStream(outputFile);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[16];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
}
cos.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
fos.close();
cos.close();
fis.close();
}
String iv = new String(cipher.getIV());
}
public void decrypt(File inputFile, File outputFile) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, NoSuchProviderException {
Cipher cipher = getDecryptionCipher(inputFile);
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
CipherInputStream cipherInputStream = null;
try{
inputStream = new FileInputStream(inputFile);
cipherInputStream = new CipherInputStream(inputStream, cipher);
outputStream = new FileOutputStream(outputFile);
byte[] data = new byte[16];
int read = cipherInputStream.read(data);
while(read != -1){
outputStream.write(data);
read = cipherInputStream.read(data);
}
outputStream.flush();
}catch (Exception e){
e.printStackTrace();
}
finally {
cipherInputStream.close();
inputStream.close();
outputStream.close();
}
}
public void setSecretKey(){
SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
secretKey = new SecretKeySpec(key, "AES");
}
public SecretKey getSecretKey(){
return secretKey;
}
public byte[] getInitializationVector(){
String ivstr = "1234567890ab"; //12 bytes
byte[] iv = ivstr.getBytes();//new byte[12];
return iv;
}
Above code results in following error during decryption at line
int read = cipherInputStream.read(data);
javax.crypto.BadPaddingException: mac check in GCM failed
at javax.crypto.CipherInputStream.getMoreData(CipherInputStream.java:128)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:246)
at javax.crypto.CipherInputStream.read(CipherInputStream.java:222)
at com.rocketsoftware.abr.encryption.SymmetricFileEncryption.decrypt(SymmetricFileEncryption.java:107)
Encryption doesn't work properly: In encrypt, CipherOutputStream#close must be called before FileOutputStream#close. This is because CipherOutputStream#close calls Cipher#doFinal that generates the tag and appends it to the ciphertext. This portion can only be written to the FileOutputStream-instance if FileOutputStream#close has not yet been called. By the way, CipherOutputStream#flush doesn't need to be called.
There is also a problem with decryption: In decrypt, outputStream.write(data) must be replaced by outputStream.write(data, 0, read). Otherwise usually too much data will be written to the FileOutputStream-instance.
The classes javax.crypto.CipherInputStream and javax.crypto.CipherOutputStream may perform the authentication false positive and are therefore not suitable for GCM-mode, e.g. from the documentation (Java 12) for CipherInputStream:
This class may catch BadPaddingException and other exceptions thrown by failed integrity checks during decryption. These exceptions are not re-thrown, so the client may not be informed that integrity checks failed. Because of this behavior, this class may not be suitable for use with decryption in an authenticated mode of operation (e.g. GCM). Applications that require authenticated encryption can use the Cipher API directly as an alternative to using this class.
Therefore, either the Cipher API should be used directly, as recommended in the documentation, or the BouncyCastle-implementations org.bouncycastle.crypto.io.CipherInputStream and org.bouncycastle.crypto.io.CipherOutputStream, e.g. for encryption:
import org.bouncycastle.crypto.io.CipherInputStream;
import org.bouncycastle.crypto.io.CipherOutputStream;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.AEADBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
...
public void encrypt(File inputFile, File outputFile) throws Exception {
AEADBlockCipher cipher = getEncryptionCipher();
// Following code as before (but with fixes described above)
...
}
public AEADBlockCipher getEncryptionCipher() throws Exception {
AEADBlockCipher cipher = new GCMBlockCipher(new AESEngine());
cipher.init(true, // encryption
new AEADParameters(
new KeyParameter(getSecretKey().getEncoded()),
128, // tag length
getInitializationVector(),
"Optional Associated Data".getBytes()));
return cipher;
}
...
and analog for decryption.
Note that even if authentication fails, decryption is performed, so the developer must ensure that the result is discarded and not used in this case.
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.
How can one use cipher.updateAAD() below API 19 (Java 7)? I know I can use BouncyCastle but it does not provide a backport for AEAD encryption and decryption. Are there any third-party libraries? I am using AES/GCM/NoPadding.
Actually BouncyCastle offers all the things I needed, including AAD. For AES/GCM we can use these methods:
static byte[] gcmDecrypt(byte[] ct, byte[] key, byte[] iv, byte[] aad) throws Exception {
AEADParameters parameters = new AEADParameters(new KeyParameter(key), 128, iv, aad);
GCMBlockCipher gcmEngine = new GCMBlockCipher(new AESFastEngine());
gcmEngine.init(false, parameters);
byte[] pt = new byte[gcmEngine.getOutputSize(ct.length)];
int len = gcmEngine.processBytes(ct, 0, ct.length, pt, 0);
gcmEngine.doFinal(pt, len);
return pt;
}
static byte[] gcmEncrypt(byte[] pt, byte[] key, byte[] iv, byte[] aad) throws Exception {
AEADParameters parameters = new AEADParameters(new KeyParameter(key), 128, iv, aad);
GCMBlockCipher gcmEngine = new GCMBlockCipher(new AESFastEngine());
gcmEngine.init(true, parameters);
byte[] ct = new byte[gcmEngine.getOutputSize(pt.length)];
int len = gcmEngine.processBytes(pt, 0, pt.length, ct, 0);
gcmEngine.doFinal(ct, len);
return ct;
}
I improved the encrypt code above and tested it on a Sony Android 4.3 phone
import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.AEADParameters;
import org.bouncycastle.crypto.params.KeyParameter;
public static byte[] encrypt(byte[] password, byte[] key, byte[] iv, byte[] aad) throws Exception {
byte[] encrypt;
if (Build.VERSION.SDK_INT >= 21) {
cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(128, iv));
cipher.updateAAD(aad);
encrypt = cipher.doFinal(password);
} else {
AEADParameters aeadParameters = new AEADParameters(new KeyParameter(key), 128, iv, aad);
GCMBlockCipher gcmBlockCipher = new GCMBlockCipher(new AESEngine());
gcmBlockCipher.init(true, aeadParameters);
//gcmEngine.processAADBytes(aad, 0, aad.length);//-- if use this remove aad in constructor AEADParameters
encrypt = new byte[gcmBlockCipher.getOutputSize(password.length)];
int length = gcmBlockCipher.processBytes(password, 0, password.length, encrypt, 0);
gcmBlockCipher.doFinal(encrypt, length);
}
Log.e("encrypted : " + Arrays.toString(encrypt));
return encrypt;
}
dependencies {
//-- https://www.bouncycastle.org/latest_releases.html
//implementation 'org.bouncycastle:bcpkix-jdk15to18:1.68'
implementation 'org.bouncycastle:bcprov-jdk15to18:1.68'
}
thanks #gi097, https://stackoverflow.com/a/62924766/6356601
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);
}
}
}
O want to encrypt and decrypt files using my Java program.
These files are compiled Java .class files, I don't know what I am doing wrong. I am just testing now, but after encrypt and decrypt with the same key. It shows that file is
Exception in thread "AWT-EventQueue-0" java.lang.ClassFormatError: Truncated class file
and that class cannot be loaded.
There is my encrypter/decrypter class code:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
public class EncTool {
public static void encrypt(String key, String filename) throws Throwable {
InputStream is = new FileInputStream("SomeFile.class");
OutputStream os = new FileOutputStream("SomeFile.class");
encrypt(key, Cipher.ENCRYPT_MODE, is, os);
}
public static void decrypt(String key, String filename) throws Throwable {
InputStream is = new FileInputStream("SomeFile.class");
OutputStream os = new FileOutputStream("SomeFile.class");
decrypt(key, Cipher.DECRYPT_MODE, is, os);
}
public static void encrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
cipher.init(Cipher.ENCRYPT_MODE, desKey);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
}
public static void decrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
cipher.init(Cipher.DECRYPT_MODE, desKey);
CipherOutputStream cos = new CipherOutputStream(os, cipher);
doCopy(is, cos);
}
public static void doCopy(InputStream is, OutputStream os) throws IOException {
byte[] bytes = new byte[64];
int numBytes;
while ((numBytes = is.read(bytes)) != -1) {
os.write(bytes, 0, numBytes);
}
os.flush();
os.close();
is.close();
}
}
and for testing im executing it that way:
try {
EncTool.encrypt("somekey123", "SomeFile");
EncTool.decrypt("somekey123", "SomeFile");
} catch (Throwable e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
What am i doing wrong?
Or maybe how should it be done?
EDIT
when my code looks that:
public static void encrypt(String key, String filename) throws Throwable {
InputStream is = new FileInputStream("Somefile.class");
OutputStream os = new FileOutputStream("tempfile.class");
DESKeySpec dks = new DESKeySpec(key.getBytes());
SecretKeyFactory skf = SecretKeyFactory.getInstance("DES");
SecretKey desKey = skf.generateSecret(dks);
Cipher cipher = Cipher.getInstance("DES"); // DES/ECB/PKCS5Padding for SunJCE
cipher.init(Cipher.ENCRYPT_MODE, desKey);
CipherInputStream cis = new CipherInputStream(is, cipher);
doCopy(cis, os);
File file2 = new File("tempfile.class");
File f = new File("somefile.class");
f.delete();
file2.renameTo(f);
}
Now it works, but these deleting and renaming thing doesn't look elegant, how can I do it more efficient?
You must write the output to a new file instead of overwriting the input while you are reading it. The way your code is written now the results will be undefined.