de.thksystems.util.crypto.EncryptionUtils Maven / Gradle / Ivy
/*
* tksCommons
*
* Author : Thomas Kuhlmann (ThK-Systems, http://www.thk-systems.de)
* License : LGPL (https://www.gnu.org/licenses/lgpl.html)
*/
package de.thksystems.util.crypto;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import de.thksystems.exception.ServiceRuntimeException;
/**
* Some utils to keep simple crypting-tasks simple.
*
* No auditing by a second person has been done so far. If you found any problems, please contact the author!!!
* Use it on your own risk (as always!!!)
*
* The AES-256 algorithm is used, if not mentioned other. You may patch you JDK/JRE to use a keysize of 256bit. (see
* 'Unlimited_JCE_Policy-README.txt' in the docs folder or
* http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html )
*
* @see http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html
*/
public final class EncryptionUtils {
private static final String ALGORITHM = "AES";
private static final String TEXT_ENCODING = "UTF-8";
private static final int KEYSIZE = 256;
private static final int ITERATIONS = 65536;
private static final byte[] SALT = new byte[] { 119, 79, 88, 32, -87, 91, 56, -23, 70, -78, -69, 4, -35, 20, -71, -39 };
private static boolean init;
private static SecretKeyFactory skf;
private EncryptionUtils() {
}
private static void lazyInit() {
try {
if (!init) {
Security.addProvider(new BouncyCastleProvider());
skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
init = true;
}
} catch (NoSuchAlgorithmException e) {
throw new ServiceRuntimeException(e);
}
}
/**
* Encrypts the given string using AES (256 bit).
*/
public static EncryptionContainer encryptString(String passphrase, String text) throws CryptoException {
try {
return encryptData(passphrase, text.getBytes(TEXT_ENCODING));
} catch (UnsupportedEncodingException e) {
throw new CryptoException("Encryption failed", e);
}
}
/**
* Encrypts arbitary data using AES (256 bit)
*/
public static EncryptionContainer encryptData(String passphrase, byte[] data) throws CryptoException {
lazyInit();
try {
SecretKey secret = getSecretKey(passphrase);
/* Encrypt the message. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = cipher.doFinal(data);
return new EncryptionContainer(iv, ciphertext);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | InvalidParameterSpecException
| IllegalBlockSizeException | BadPaddingException e) {
throw new CryptoException("Encryption failed", e);
}
}
/**
* Decrypts the data fromerly encrypted using {@link #encryptString(String, String)} using AES (256 bit).
*/
public static String decryptToString(String passphrase, EncryptionContainer er) throws CryptoException {
return decryptToString(passphrase, er.getData(), er.getIv());
}
/**
* Decrypts the data fromerly encrypted using {@link #encryptString(String, String)} using AES (256 bit).
*/
public static String decryptToString(String passphrase, byte[] data, byte[] iv) throws CryptoException {
lazyInit();
try {
SecretKey secret = getSecretKey(passphrase);
/* Decrypt the message, given derived key and initialization vector. */
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
return new String(cipher.doFinal(data), TEXT_ENCODING);
} catch (NoSuchAlgorithmException | InvalidKeySpecException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException
| BadPaddingException | UnsupportedEncodingException | InvalidAlgorithmParameterException e) {
throw new CryptoException("Decryption failed", e);
}
}
private static SecretKey getSecretKey(String passphrase) throws InvalidKeySpecException {
KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), SALT, ITERATIONS, KEYSIZE);
SecretKey tmp = skf.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), ALGORITHM);
return secret;
}
/**
* Writes a text encrypted to a file. (Encrypt using AES-256)
*/
public static void encryptToFile(File file, String text, String passphrase) throws CryptoException, IOException {
EncryptionContainer ec = encryptString(passphrase, text);
ec.writeToFile(file);
}
/**
* Reads a text from an encrypted file, encrypted and written by {@link #encryptToFile(File, String, String)} using AES (256 bit).
*/
public static String decryptFromFile(File file, String passphrase) throws CryptoException, IOException {
EncryptionContainer ec = EncryptionContainer.readFromFile(file);
return decryptToString(passphrase, ec);
}
}