diff --git a/jans-core/util/pom.xml b/jans-core/util/pom.xml index 805c3d2ad61..e3b3d572c09 100644 --- a/jans-core/util/pom.xml +++ b/jans-core/util/pom.xml @@ -99,6 +99,12 @@ jakarta.xml.bind jakarta.xml.bind-api + + + org.testng + testng + + \ No newline at end of file diff --git a/jans-core/util/src/main/java/io/jans/util/security/AESUtil.java b/jans-core/util/src/main/java/io/jans/util/security/AESUtil.java new file mode 100644 index 00000000000..bdb6737fe84 --- /dev/null +++ b/jans-core/util/src/main/java/io/jans/util/security/AESUtil.java @@ -0,0 +1,68 @@ +package io.jans.util.security; + +import javax.crypto.*; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Base64; + +public class AESUtil { + + public static final int IV_LENGTH = 16; + public static final int GCM_TAG_LENGTH = 16; + + private AESUtil() { + } + + public static SecretKey generateKey(int n) throws NoSuchAlgorithmException { + KeyGenerator keyGenerator = KeyGenerator.getInstance("AES"); + keyGenerator.init(n); + return keyGenerator.generateKey(); + } + + public static SecretKey getKeyFromPassword(String password, String salt) + throws NoSuchAlgorithmException, InvalidKeySpecException { + SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + KeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), 65536, 256); + return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES"); + } + + public static IvParameterSpec generateIv() { + return generateIv(IV_LENGTH); + } + + public static IvParameterSpec generateIv(int size) { + byte[] iv = new byte[size]; + new SecureRandom().nextBytes(iv); + return new IvParameterSpec(iv); + } + + public static GCMParameterSpec generateGcmSpec() { + return new GCMParameterSpec(GCM_TAG_LENGTH * 8, generateIv().getIV()); + } + + public static String encrypt(String algorithm, String input, SecretKey key, AlgorithmParameterSpec spec) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.ENCRYPT_MODE, key, spec); + byte[] cipherText = cipher.doFinal(input.getBytes()); + return Base64.getEncoder().encodeToString(cipherText); + } + + public static String decrypt(String algorithm, String cipherText, SecretKey key, AlgorithmParameterSpec spec) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + Cipher cipher = Cipher.getInstance(algorithm); + cipher.init(Cipher.DECRYPT_MODE, key, spec); + byte[] plainText = cipher.doFinal(Base64.getDecoder().decode(cipherText)); + return new String(plainText); + } +} diff --git a/jans-core/util/src/test/java/io/jans/util/security/AESUtilTest.java b/jans-core/util/src/test/java/io/jans/util/security/AESUtilTest.java new file mode 100644 index 00000000000..08d100bae53 --- /dev/null +++ b/jans-core/util/src/test/java/io/jans/util/security/AESUtilTest.java @@ -0,0 +1,49 @@ +package io.jans.util.security; + +import org.testng.annotations.Test; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import static io.jans.util.security.AESUtil.generateGcmSpec; +import static org.testng.Assert.assertEquals; + +/** + * @author Yuriy Z + */ +public class AESUtilTest { + + @Test + public void cbc_whenEncryptAndDecrypt_shouldProduceCorrectOutput() throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + String input = "textToEncrypt"; + SecretKey key = AESUtil.generateKey(128); + IvParameterSpec ivParameterSpec = AESUtil.generateIv(); + + String algorithm = "AES/CBC/PKCS5Padding"; + String cipherText = AESUtil.encrypt(algorithm, input, key, ivParameterSpec); + String plainText = AESUtil.decrypt(algorithm, cipherText, key, ivParameterSpec); + + assertEquals(input, plainText); + } + + @Test + public void gcm_whenEncryptAndDecrypt_shouldProduceCorrectOutput() throws NoSuchAlgorithmException, IllegalBlockSizeException, InvalidKeyException, BadPaddingException, InvalidAlgorithmParameterException, NoSuchPaddingException { + String input = "textToEncrypt"; + SecretKey key = AESUtil.generateKey(128); + + String algorithm = "AES/GCM/NoPadding"; + final GCMParameterSpec gcmSpec = generateGcmSpec(); + + String cipherText = AESUtil.encrypt(algorithm, input, key, gcmSpec); + String plainText = AESUtil.decrypt(algorithm, cipherText, key, gcmSpec); + + assertEquals(input, plainText); + } +}