Last active
January 2, 2024 14:58
-
-
Save lry127/1fcc39c36bb468f3410e0dd28e53c3ae to your computer and use it in GitHub Desktop.
Java AES CBC encryption (decryption) util with example.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javax.crypto.*; | |
import javax.crypto.spec.IvParameterSpec; | |
import javax.crypto.spec.PBEKeySpec; | |
import javax.crypto.spec.SecretKeySpec; | |
import java.nio.charset.StandardCharsets; | |
import java.security.InvalidAlgorithmParameterException; | |
import java.security.InvalidKeyException; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.SecureRandom; | |
import java.security.spec.InvalidKeySpecException; | |
import java.security.spec.KeySpec; | |
import java.util.Arrays; | |
public class AESCBCCryptoWrapper { | |
private static final String CIPHER = "AES/CBC/PKCS5Padding"; | |
private static final String KEY_GEN_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA256"; | |
private static final int KEY_LENGTH = 256; | |
private static final int IV_LENGTH = 128; | |
private static final int KEY_GEN_ITER_COUNT = 65536; | |
private final SecretKeySpec secretKeySpec; | |
private final Cipher cipher; | |
private final SecureRandom secureRandom; | |
public AESCBCCryptoWrapper(String key, String salt) { | |
try { | |
secureRandom = new SecureRandom(); | |
SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_GEN_FACTORY_ALGORITHM); | |
KeySpec keySpec = new PBEKeySpec(key.toCharArray(), salt.getBytes(StandardCharsets.UTF_8), KEY_GEN_ITER_COUNT, KEY_LENGTH); | |
SecretKey secretKey = factory.generateSecret(keySpec); | |
secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES"); | |
cipher = Cipher.getInstance(CIPHER); | |
} catch (InvalidKeySpecException | NoSuchAlgorithmException | NoSuchPaddingException e) { | |
throw new CryptoException(e); | |
} | |
} | |
public static void main(String... args) { | |
final String key = "I'm the key to wisdom"; | |
final String salt = "I'm so salty"; | |
final String text1 = "Some of us get dipped in flat, some in satin, some in gloss.But every once in a while you find someone who’s iridescent, and when you do, nothing will ever compare."; | |
final String text2 = "有些人沦为平庸浅薄,金玉其外,而败絮其中。可不经意间,有一天你会遇到一个彩虹般绚丽的人,从此以后,其他人就不过是匆匆浮云。"; | |
final byte[] bytes1 = text1.getBytes(StandardCharsets.UTF_8); | |
final byte[] bytes2 = text2.getBytes(StandardCharsets.UTF_8); | |
AESCBCCryptoWrapper wrapper = new AESCBCCryptoWrapper(key, salt); | |
showBytes("original bytes 1 is", bytes1); | |
System.out.println("original text 1 is \"" + text1 + "\""); | |
showBytes("original bytes 2 is", bytes2); | |
System.out.println("original text 2 is \"" + text2 + "\""); | |
System.out.println(); | |
EncryptionResult encryptionResult1 = wrapper.encryptData(bytes1); | |
byte[] customIv = new byte[16]; | |
new SecureRandom().nextBytes(customIv); | |
EncryptionResult encryptionResult2 = wrapper.encryptData(bytes2, customIv); | |
showBytes("encrypted data 1, iv is", encryptionResult1.iv); | |
showBytes("encrypted data 1, encrypted data is", encryptionResult1.encryptedData); | |
System.out.println(); | |
showBytes("encrypted data 2, iv is", encryptionResult2.iv); | |
showBytes("encrypted data 2, encrypted data is", encryptionResult2.encryptedData); | |
System.out.println("generated iv is " + Arrays.toString(customIv) + " obtained iv is " + Arrays.toString(encryptionResult2.iv) + " . does they equals? " + Arrays.equals(customIv, encryptionResult2.iv)); | |
System.out.println(); | |
byte[] decrypted1 = wrapper.decryptData(encryptionResult1.encryptedData, encryptionResult1.iv); | |
String decrypted1Msg = new String(decrypted1, StandardCharsets.UTF_8); | |
byte[] decrypted2 = wrapper.decryptData(encryptionResult2.encryptedData, encryptionResult2.iv); | |
String decrypted2Msg = new String(decrypted2, StandardCharsets.UTF_8); | |
showBytes("decrypted 1 array is", decrypted1); | |
System.out.println("decrypted 1 array equals bytes 1? " + Arrays.equals(decrypted1, bytes1)); | |
System.out.println("decrypted 1 string is \"" + decrypted1Msg + "\" original.equals(decrypted)? " + text1.equals(decrypted1Msg)); | |
System.out.println(); | |
showBytes("decrypted 2 array is", decrypted2); | |
System.out.println("decrypted 2 array equals bytes 2? " + Arrays.equals(decrypted2, bytes2)); | |
System.out.println("decrypted 2 string is \"" + decrypted2Msg + "\" original.equals(decrypted)? " + text2.equals(decrypted2Msg)); | |
System.out.println(); | |
} | |
private static void showBytes(String prompt, byte[] data) { | |
System.out.println(prompt + " " + Arrays.toString(data)); | |
} | |
public EncryptionResult encryptData(byte[] data, byte[] iv) { | |
IvParameterSpec ivSpec = getIvSpec(iv); | |
try { | |
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); | |
byte[] encrypted = cipher.doFinal(data); | |
return new EncryptionResult(cipher.getIV(), encrypted); | |
} catch (InvalidKeyException | IllegalBlockSizeException | BadPaddingException | | |
InvalidAlgorithmParameterException e) { | |
throw new CryptoException(e); | |
} | |
} | |
public EncryptionResult encryptData(byte[] data) { | |
return encryptData(data, null); | |
} | |
public EncryptionResult encryptData(byte[] data, byte[] iv, int offset, int len) { | |
IvParameterSpec ivSpec = getIvSpec(iv); | |
try { | |
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec); | |
byte[] encrypted = cipher.doFinal(data, offset, len); | |
return new EncryptionResult(cipher.getIV(), encrypted); | |
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | | |
BadPaddingException e) { | |
throw new CryptoException(e); | |
} | |
} | |
public EncryptionResult encryptData(byte[] data, int offset, int len) { | |
return encryptData(data, null, offset, len); | |
} | |
public byte[] decryptData(byte[] encrypted, byte[] iv) { | |
if (encrypted == null || iv == null) { | |
throw new CryptoException("neither encrypted data or iv could be null"); | |
} | |
IvParameterSpec ivSpec = getIvSpec(iv); | |
try { | |
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); | |
return cipher.doFinal(encrypted); | |
} catch (Exception e) { | |
throw new CryptoException(e); | |
} | |
} | |
public byte[] decryptData(byte[] encrypted, byte[] iv, int offset, int len) { | |
if (encrypted == null || iv == null) { | |
throw new CryptoException("neither encrypted data or iv could be null"); | |
} | |
IvParameterSpec ivSpec = getIvSpec(iv); | |
try { | |
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec); | |
return cipher.doFinal(encrypted, offset, len); | |
} catch (Exception e) { | |
throw new CryptoException(e); | |
} | |
} | |
private IvParameterSpec getIvSpec(byte[] iv) { | |
if (iv == null) { | |
iv = new byte[IV_LENGTH / 8]; | |
secureRandom.nextBytes(iv); | |
} | |
return new IvParameterSpec(iv); | |
} | |
public record EncryptionResult(byte[] iv, byte[] encryptedData) { | |
} | |
public static class CryptoException extends RuntimeException { | |
public CryptoException() { | |
super(); | |
} | |
public CryptoException(String reason) { | |
super(reason); | |
} | |
public CryptoException(Throwable cause) { | |
super(cause); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment