Skip to content

Instantly share code, notes, and snippets.

@lry127
Last active January 2, 2024 14:58
Show Gist options
  • Save lry127/1fcc39c36bb468f3410e0dd28e53c3ae to your computer and use it in GitHub Desktop.
Save lry127/1fcc39c36bb468f3410e0dd28e53c3ae to your computer and use it in GitHub Desktop.
Java AES CBC encryption (decryption) util with example.
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