package droidkit.crypto; import android.support.annotation.NonNull; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; 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; /** * @author Daniel Serdyukov */ public class Aes { private static final String TRANSFORMATION = "AES/CBC/PKCS7Padding"; private static final String KEY_FACTORY_ALG = "PBKDF2WithHmacSHA1"; private static final int KEY_SIZE = 256; private static final String AES = "AES"; private static final byte[] IV = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; private final String mTransformation; private final String mKeyFactory; private final int mKeySize; public Aes(@NonNull String transformation, @NonNull String keyFactory, int keySize) { mTransformation = transformation; mKeyFactory = keyFactory; mKeySize = keySize; } public static Aes getInstance() { return Holder.INSTANCE; } @NonNull public byte[] encrypt(@NonNull byte[] data, @NonNull String key) throws DigestException { return doFinal(Cipher.ENCRYPT_MODE, data, key); } @NonNull public byte[] decrypt(@NonNull byte[] data, @NonNull String key) throws DigestException { return doFinal(Cipher.DECRYPT_MODE, data, key); } @NonNull public String encryptString(@NonNull String data, @NonNull String key) throws DigestException { return Hex.toHexString(encrypt(data.getBytes(Hex.UTF_8), key)); } @NonNull public String decryptString(@NonNull String data, @NonNull String key) throws DigestException { return new String(decrypt(Hex.fromHexString(data), key), Hex.UTF_8); } @NonNull private SecretKey getSecretKey(@NonNull String key) throws DigestException { try { final SecretKeyFactory factory = SecretKeyFactory.getInstance(mKeyFactory); final KeySpec keySpec = new PBEKeySpec(key.toCharArray(), key.getBytes(Hex.UTF_8), 1024, mKeySize); return new SecretKeySpec(factory.generateSecret(keySpec).getEncoded(), AES); } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { throw new DigestException(e); } } @NonNull private byte[] doFinal(int mode, @NonNull byte[] data, @NonNull String key) throws DigestException { try { final Cipher cipher = Cipher.getInstance(mTransformation); cipher.init(mode, getSecretKey(key), new IvParameterSpec(IV)); return cipher.doFinal(data); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException | InvalidAlgorithmParameterException e) { throw new DigestException(e); } } private static final class Holder { public static final Aes INSTANCE = new Aes(TRANSFORMATION, KEY_FACTORY_ALG, KEY_SIZE); } }