package io.emax.cosigner.common.crypto; import io.emax.cosigner.common.ByteUtilities; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.CBCBlockCipher; import org.bouncycastle.crypto.paddings.BlockCipherPadding; import org.bouncycastle.crypto.paddings.PKCS7Padding; import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.SecretKeySpec; public class Aes { private static final Logger LOGGER = LoggerFactory.getLogger(Aes.class); private static final String RANDOM_NUMBER_ALGORITHM = "SHA1PRNG"; private static final String RANDOM_NUMBER_ALGORITHM_PROVIDER = "SUN"; /** * Generate a random IV. */ public static byte[] generateIv() { SecureRandom secureRandom; try { secureRandom = SecureRandom.getInstance(RANDOM_NUMBER_ALGORITHM, RANDOM_NUMBER_ALGORITHM_PROVIDER); } catch (Exception e) { LOGGER.error(null, e); secureRandom = new SecureRandom(); } byte[] iv = new byte[16]; secureRandom.nextBytes(iv); return iv; } /** * Generate a key from a password and salt. */ public static byte[] generateKey(String password, byte[] salt) { try { PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, 50, 256); SecretKeyFactory keyFactory = SecretKeyFactory .getInstance("PBEWithSHA256And256BitAES-CBC-BC", new BouncyCastleProvider()); SecretKeySpec secretKey = new SecretKeySpec(keyFactory.generateSecret(pbeKeySpec).getEncoded(), "AES"); return secretKey.getEncoded(); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { LOGGER.error(null, e); return new byte[0]; } } /** * Encrypt data using the provided key and IV data. */ public static String encrypt(byte[] key, byte[] iv, String encString) { try { return Aes.transform(key, iv, encString, true); } catch (Exception e) { LOGGER.error(null, e); return ""; } } /** * Decrypt data using the provided key and IV data. */ public static String decrypt(byte[] key, byte[] iv, String encString) { try { return Aes.transform(key, iv, encString, false); } catch (Exception e) { LOGGER.error(null, e); return ""; } } private static String transform(byte[] key, byte[] iv, String encString, boolean encrypt) throws Exception { // setup cipher parameters with key and IV KeyParameter keyParam = new KeyParameter(key); CipherParameters params = new ParametersWithIV(keyParam, iv); // setup AES cipher in CBC mode with PKCS7 padding BlockCipherPadding padding = new PKCS7Padding(); BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()), padding); cipher.reset(); cipher.init(encrypt, params); // create a temporary buffer to decode into (it'll include padding) byte[] encData = ByteUtilities.toByteArray(encString); byte[] buf = new byte[cipher.getOutputSize(encData.length)]; int len = cipher.processBytes(encData, 0, encData.length, buf, 0); len += cipher.doFinal(buf, len); // remove padding byte[] out = new byte[len]; System.arraycopy(buf, 0, out, 0, len); // return string representation of decoded bytes return ByteUtilities.toHexString(out); } }