package io.kaif.token; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; class Decryptor { private static final int KEY_LENGTH = 16; /** * @see Encryptor#create(byte[]) for key algorithm */ public static Decryptor create(final byte[] key) { final byte[] iv = new byte[KEY_LENGTH]; Arrays.fill(iv, (byte) 0x00); return create(key, iv); } /** * @see Encryptor#create(byte[], byte[])) for key algorithm */ public static Decryptor create(final byte[] key, final byte[] iv) { try { final IvParameterSpec ivParameterSpec = new IvParameterSpec(iv); return new Decryptor(key, "AES/CBC/PKCS5Padding", ivParameterSpec); } catch (final GeneralSecurityException e) { e.printStackTrace(); throw new RuntimeException("unsupported type of encryption: AES"); } } private final Cipher decryptCipher; private final Key key; private Decryptor(final byte[] rawKeyData, final String algorithm, final IvParameterSpec ivParameterSpec) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { key = new SecretKeySpec(rawKeyData, "AES"); decryptCipher = Cipher.getInstance(algorithm); decryptCipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec); } public byte[] decrypt(final byte[] rawData) throws GeneralSecurityException { if (rawData == null) { return null; } return decryptCipher.doFinal(rawData); } public String decrypt(final String rawData) throws GeneralSecurityException { if (rawData == null) { return null; } try { return new String(decrypt(Base64.getUrlDecoder().decode(rawData)), "UTF-8"); } catch (final UnsupportedEncodingException e) { throw new RuntimeException(e); } } public void decryptInto(final ByteBuffer input, final ByteBuffer output) throws ShortBufferException, GeneralSecurityException { if (input == null) { output.clear(); return; } decryptCipher.doFinal(input, output); } public int estimateByteSize(final int inputByteSize) { return decryptCipher.getOutputSize(inputByteSize); } /** * reset cipher to initialized state, with updated IV * * @param iv * 16 bytes initial vector */ public void resetIvParameter(final byte[] iv, final int offset) { try { decryptCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv, offset, KEY_LENGTH)); } catch (InvalidKeyException | InvalidAlgorithmParameterException e) { throw new RuntimeException("unexpected", e); } } }