Java AES/CBC 解密后初始字节不正确

下面的例子有什么问题吗?

问题是,解密字符串的第一部分是无意义的。然而,其余部分是好的,我得到..。

Result: `£eB6O�geS��i are you? Have a nice day.
@Test
public void testEncrypt() {
try {
String s = "Hello there. How are you? Have a nice day.";


// Generate key
KeyGenerator kgen = KeyGenerator.getInstance("AES");
kgen.init(128);
SecretKey aesKey = kgen.generateKey();


// Encrypt cipher
Cipher encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
encryptCipher.init(Cipher.ENCRYPT_MODE, aesKey);


// Encrypt
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, encryptCipher);
cipherOutputStream.write(s.getBytes());
cipherOutputStream.flush();
cipherOutputStream.close();
byte[] encryptedBytes = outputStream.toByteArray();


// Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);


// Decrypt
outputStream = new ByteArrayOutputStream();
ByteArrayInputStream inStream = new ByteArrayInputStream(encryptedBytes);
CipherInputStream cipherInputStream = new CipherInputStream(inStream, decryptCipher);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = cipherInputStream.read(buf)) >= 0) {
outputStream.write(buf, 0, bytesRead);
}


System.out.println("Result: " + new String(outputStream.toByteArray()));


}
catch (Exception ex) {
ex.printStackTrace();
}
}
483748 次浏览

在我看来,你似乎没有正确处理你的初始向量(IV)。 我已经很久没有读到 AES,IV 和块链接了,但是你的台词

IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());

does not seem to be OK. In the case of AES, you can think of the initialization vector as the "initial state" of a cipher instance, and this state is a bit of information that you can not get from your key but from the actual computation of the encrypting cipher. (One could argue that if the IV could be extracted from the key, then it would be of no use, as the key is already given to the cipher instance during its init phase).

因此,您应该从加密结束时的密码实例获得以字节[]形式存在的 IV

  cipherOutputStream.close();
byte[] iv = encryptCipher.getIV();

你应该用这个字节[]在 DECRYPT_MODE中初始化你的 Cipher:

  IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);

那么,你的解密应该没问题。 希望这个能帮上忙。

包括我自己在内的很多人在完成这项工作时都会面临很多问题,比如遗漏了一些信息,忘记转换到 Base64,初始化向量,字符集等等。所以我想做一个功能齐全的代码。

希望这对你们有用: 要编译,您需要额外的 Apache Commons Codec jar,这里提供: Http://commons.apache.org/proper/commons-codec/download_codec.cgi

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


import org.apache.commons.codec.binary.Base64;


public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);


byte[] encrypted = cipher.doFinal(value.getBytes());
System.out.println("encrypted string: "
+ Base64.encodeBase64String(encrypted));


return Base64.encodeBase64String(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}


public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);


byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));


return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}


public static void main(String[] args) {
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV


System.out.println(decrypt(key, initVector,
encrypt(key, initVector, "Hello World")));
}
}

Here a solution without Apache Commons Codec's Base64:

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;


public class AdvancedEncryptionStandard
{
private byte[] key;


private static final String ALGORITHM = "AES";


public AdvancedEncryptionStandard(byte[] key)
{
this.key = key;
}


/**
* Encrypts the given plain text
*
* @param plainText The plain text to encrypt
*/
public byte[] encrypt(byte[] plainText) throws Exception
{
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);


return cipher.doFinal(plainText);
}


/**
* Decrypts the given byte array
*
* @param cipherText The data to decrypt
*/
public byte[] decrypt(byte[] cipherText) throws Exception
{
SecretKeySpec secretKey = new SecretKeySpec(key, ALGORITHM);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey);


return cipher.doFinal(cipherText);
}
}

用法例子:

byte[] encryptionKey = "MZygpewJsCpRrfOr".getBytes(StandardCharsets.UTF_8);
byte[] plainText = "Hello world!".getBytes(StandardCharsets.UTF_8);
AdvancedEncryptionStandard advancedEncryptionStandard = new AdvancedEncryptionStandard(
encryptionKey);
byte[] cipherText = advancedEncryptionStandard.encrypt(plainText);
byte[] decryptedCipherText = advancedEncryptionStandard.decrypt(cipherText);


System.out.println(new String(plainText));
System.out.println(new String(cipherText));
System.out.println(new String(decryptedCipherText));

印刷品:

Hello world!
դ;��LA+�ߙb*
Hello world!

用于解密的 IV 不正确。请替换此代码

//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(aesKey.getEncoded());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

With this code

//Decrypt cipher
Cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParameterSpec = new IvParameterSpec(encryptCipher.getIV());
decryptCipher.init(Cipher.DECRYPT_MODE, aesKey, ivParameterSpec);

这样你的问题就解决了。


下面包括一个简单的 Java AES 类示例。我不推荐在生产环境中使用这个类,因为它可能无法满足应用程序的所有特定需求。

import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;


public class AES
{
public static byte[] encrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
return AES.transform(Cipher.ENCRYPT_MODE, keyBytes, ivBytes, messageBytes);
}


public static byte[] decrypt(final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
return AES.transform(Cipher.DECRYPT_MODE, keyBytes, ivBytes, messageBytes);
}


private static byte[] transform(final int mode, final byte[] keyBytes, final byte[] ivBytes, final byte[] messageBytes) throws InvalidKeyException, InvalidAlgorithmParameterException
{
final SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
final IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
byte[] transformedBytes = null;


try
{
final Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");


cipher.init(mode, keySpec, ivSpec);


transformedBytes = cipher.doFinal(messageBytes);
}
catch (NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e)
{
e.printStackTrace();
}
return transformedBytes;
}


public static void main(final String[] args) throws InvalidKeyException, InvalidAlgorithmParameterException
{
//Retrieved from a protected local file.
//Do not hard-code and do not version control.
final String base64Key = "ABEiM0RVZneImaq7zN3u/w==";


//Retrieved from a protected database.
//Do not hard-code and do not version control.
final String shadowEntry = "AAECAwQFBgcICQoLDA0ODw==:ZtrkahwcMzTu7e/WuJ3AZmF09DE=";


//Extract the iv and the ciphertext from the shadow entry.
final String[] shadowData = shadowEntry.split(":");
final String base64Iv = shadowData[0];
final String base64Ciphertext = shadowData[1];


//Convert to raw bytes.
final byte[] keyBytes = Base64.getDecoder().decode(base64Key);
final byte[] ivBytes = Base64.getDecoder().decode(base64Iv);
final byte[] encryptedBytes = Base64.getDecoder().decode(base64Ciphertext);


//Decrypt data and do something with it.
final byte[] decryptedBytes = AES.decrypt(keyBytes, ivBytes, encryptedBytes);


//Use non-blocking SecureRandom implementation for the new IV.
final SecureRandom secureRandom = new SecureRandom();


//Generate a new IV.
secureRandom.nextBytes(ivBytes);


//At this point instead of printing to the screen,
//one should replace the old shadow entry with the new one.
System.out.println("Old Shadow Entry      = " + shadowEntry);
System.out.println("Decrytped Shadow Data = " + new String(decryptedBytes, StandardCharsets.UTF_8));
System.out.println("New Shadow Entry      = " + Base64.getEncoder().encodeToString(ivBytes) + ":" + Base64.getEncoder().encodeToString(AES.encrypt(keyBytes, ivBytes, decryptedBytes)));
}
}

请注意,AES 与编码没有任何关系,这就是为什么我选择单独处理它,而不需要任何第三方库。

It's often the good idea to rely on standard library provided solution:

private static void stackOverflow15554296()
throws
NoSuchAlgorithmException, NoSuchPaddingException,
InvalidKeyException, IllegalBlockSizeException,
BadPaddingException
{


// prepare key
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecretKey aesKey = keygen.generateKey();
String aesKeyForFutureUse = Base64.getEncoder().encodeToString(
aesKey.getEncoded()
);


// cipher engine
Cipher aesCipher = Cipher.getInstance("AES/ECB/PKCS5Padding");


// cipher input
aesCipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] clearTextBuff = "Text to encode".getBytes();
byte[] cipherTextBuff = aesCipher.doFinal(clearTextBuff);


// recreate key
byte[] aesKeyBuff = Base64.getDecoder().decode(aesKeyForFutureUse);
SecretKey aesDecryptKey = new SecretKeySpec(aesKeyBuff, "AES");


// decipher input
aesCipher.init(Cipher.DECRYPT_MODE, aesDecryptKey);
byte[] decipheredBuff = aesCipher.doFinal(cipherTextBuff);
System.out.println(new String(decipheredBuff));
}

这将打印“要编码的文本”。

解决方案是基于 Java 加密体系结构参考指南https://stackoverflow.com/a/20591539/146745的答案。

Online Editor Runnable version:-

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
//import org.apache.commons.codec.binary.Base64;
import java.util.Base64;


public class Encryptor {
public static String encrypt(String key, String initVector, String value) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));


SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);


byte[] encrypted = cipher.doFinal(value.getBytes());


//System.out.println("encrypted string: "
//      + Base64.encodeBase64String(encrypted));


//return Base64.encodeBase64String(encrypted);
String s = new String(Base64.getEncoder().encode(encrypted));
return s;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}


public static String decrypt(String key, String initVector, String encrypted) {
try {
IvParameterSpec iv = new IvParameterSpec(initVector.getBytes("UTF-8"));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes("UTF-8"), "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);


byte[] original = cipher.doFinal(Base64.getDecoder().decode(encrypted));


return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}


public static void main(String[] args) {
String key = "Bar12345Bar12345"; // 128 bit key
String initVector = "RandomInitVector"; // 16 bytes IV


System.out.println(encrypt(key, initVector, "Hello World"));
System.out.println(decrypt(key, initVector, encrypt(key, initVector, "Hello World")));
}
}

In this answer I choose to approach the "Simple Java AES encrypt/decrypt example" main theme and not the specific debugging question because I think this will profit most readers.

This is a simple summary of my 关于 Java 中 AES 加密的博客文章 so I recommend reading through it before implementing anything. I will however still provide a simple example to use and give some pointers what to watch out for.

在这个例子中,我将选择使用 认证加密伽罗瓦/计数器模式或 GCM模式。原因是,在大多数情况下,您需要 与保密性相结合的完整性和真实性(在 博客中阅读更多内容)。

AES-GCM Encryption/Decryption Tutorial

以下是使用 AES-GCMJava 密码体系结构(JCA)进行加密/解密所需的步骤。不要与其他例子混在一起,因为细微的差异可能使您的代码完全不安全。

1. 创建密钥

由于这取决于您的用例,我将假设最简单的情况: 随机密钥。

SecureRandom secureRandom = new SecureRandom();
byte[] key = new byte[16];
secureRandom.nextBytes(key);
SecretKey secretKey = SecretKeySpec(key, "AES");

重要提示:

2. 创造初始向量

使用 初始向量(IV)使得相同的密钥将创建不同的 密码短信

byte[] iv = new byte[12]; //NEVER REUSE THIS IV WITH SAME KEY
secureRandom.nextBytes(iv);

重要提示:

3. 用 IV 和密钥加密

final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec parameterSpec = new GCMParameterSpec(128, iv); //128 bit auth tag length
cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
byte[] cipherText = cipher.doFinal(plainText);

重要提示:

  • 使用16字节/128位 认证标签(用于验证完整性/真实性)
  • the authentication tag will be automatically appended to the cipher text (in the JCA implementation)
  • 由于 GCM 的行为类似于流密码,因此不需要填充
  • 加密大块数据时使用 CipherInputStream
  • 如果需要检查其他(非机密)数据是否被更改? 您可能需要使用 相关数据cipher.updateAAD(associatedData); 这里还有。

3. 序列化为单个消息

Just append IV and ciphertext. As stated above, the IV doesn't need to be secret.

ByteBuffer byteBuffer = ByteBuffer.allocate(iv.length + cipherText.length);
byteBuffer.put(iv);
byteBuffer.put(cipherText);
byte[] cipherMessage = byteBuffer.array();

Optionally encode with Base64 if you need a string representation. Either use 安卓的 or Java 8是内置的 implementation (do not use Apache Commons Codec - it's an awful implementation). Encoding is used to "convert" byte arrays to string representation to make it ASCII safe e.g.:

String base64CipherMessage = Base64.getEncoder().encodeToString(cipherMessage);

4. 准备解密: 反序列化

如果已经对消息进行了编码,请首先将其解码为字节数组:

byte[] cipherMessage = Base64.getDecoder().decode(base64CipherMessage)

Important:

5. Decrypt

Initialize the cipher and set the same parameters as with the encryption:

final Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
//use first 12 bytes for iv
AlgorithmParameterSpec gcmIv = new GCMParameterSpec(128, cipherMessage, 0, 12);
cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmIv);
//use everything from 12 bytes on as ciphertext
byte[] plainText = cipher.doFinal(cipherMessage, 12, cipherMessage.length - 12);

重要提示:

  • don't forget to add 相关数据 with cipher.updateAAD(associatedData); if you added it during encryption.

在这个要点中可以找到一个工作代码片段。


注意,最新的 Android (SDK21 +)和 Java (7 +)实现应该具有 AES-GCM。旧版本可能没有。我仍然选择这种模式,因为除了比类似的 然后加密 Mac模式(例如 AES-CBC + HMAC)更有效之外,它更容易实现。See this article on how to implement AES-CBC with HMAC.

这比公认的答案要好。

变化:

(1)使用随机 IV 并将其预置为加密文本

(2)使用 SHA-256从密码短语生成密钥

(3)不依赖 Apache Commons

public static void main(String[] args) throws GeneralSecurityException {
String plaintext = "Hello world";
String passphrase = "My passphrase";
String encrypted = encrypt(passphrase, plaintext);
String decrypted = decrypt(passphrase, encrypted);
System.out.println(encrypted);
System.out.println(decrypted);
}


private static SecretKeySpec getKeySpec(String passphrase) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
return new SecretKeySpec(digest.digest(passphrase.getBytes(UTF_8)), "AES");
}


private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
return Cipher.getInstance("AES/CBC/PKCS5PADDING");
}


public static String encrypt(String passphrase, String value) throws GeneralSecurityException {
byte[] initVector = new byte[16];
SecureRandom.getInstanceStrong().nextBytes(initVector);
Cipher cipher = getCipher();
cipher.init(Cipher.ENCRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
byte[] encrypted = cipher.doFinal(value.getBytes());
return DatatypeConverter.printBase64Binary(initVector) +
DatatypeConverter.printBase64Binary(encrypted);
}


public static String decrypt(String passphrase, String encrypted) throws GeneralSecurityException {
byte[] initVector = DatatypeConverter.parseBase64Binary(encrypted.substring(0, 24));
Cipher cipher = getCipher();
cipher.init(Cipher.DECRYPT_MODE, getKeySpec(passphrase), new IvParameterSpec(initVector));
byte[] original = cipher.doFinal(DatatypeConverter.parseBase64Binary(encrypted.substring(24)));
return new String(original);
}

另一个使用 java.util.Base64和 Spring Boot 的解决方案

加密类

package com.jmendoza.springboot.crypto.cipher;


import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;


@Component
public class Encryptor {


@Value("${security.encryptor.key}")
private byte[] key;
@Value("${security.encryptor.algorithm}")
private String algorithm;


public String encrypt(String plainText) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return new String(Base64.getEncoder().encode(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8))));
}


public String decrypt(String cipherText) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);
Cipher cipher = Cipher.getInstance(algorithm);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
}
}

加密控制器类

package com.jmendoza.springboot.crypto.controller;


import com.jmendoza.springboot.crypto.cipher.Encryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/cipher")
public class EncryptorController {


@Autowired
Encryptor encryptor;


@GetMapping(value = "encrypt/{value}")
public String encrypt(@PathVariable("value") final String value) throws Exception {
return encryptor.encrypt(value);
}


@GetMapping(value = "decrypt/{value}")
public String decrypt(@PathVariable("value") final String value) throws Exception {
return encryptor.decrypt(value);
}
}

应用性能

server.port=8082
security.encryptor.algorithm=AES
security.encryptor.key=M8jFt46dfJMaiJA0

例子

http://localhost:8082/cipher/encrypt/jmendoza

2h41HH8Shzc4BRU3hVDOXA = =

Http://localhost:8082/cipher/decrypt/2h41hh8shzc4bru3hvdoxa==

Jmendoza

Optimized version of the accepted answer.

  • 没有第三方的谎言

  • 加密信息中包含 IV (可以是公开的)

  • 密码可以是任意长度

密码:

import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Base64;


import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;


public class Encryptor {
public static byte[] getRandomInitialVector() {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
SecureRandom randomSecureRandom = SecureRandom.getInstance("SHA1PRNG");
byte[] initVector = new byte[cipher.getBlockSize()];
randomSecureRandom.nextBytes(initVector);
return initVector;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}


public static byte[] passwordTo16BitKey(String password) {
try {
byte[] srcBytes = password.getBytes("UTF-8");
byte[] dstBytes = new byte[16];


if (srcBytes.length == 16) {
return srcBytes;
}


if (srcBytes.length < 16) {
for (int i = 0; i < dstBytes.length; i++) {
dstBytes[i] = (byte) ((srcBytes[i % srcBytes.length]) * (srcBytes[(i + 1) % srcBytes.length]));
}
} else if (srcBytes.length > 16) {
for (int i = 0; i < srcBytes.length; i++) {
dstBytes[i % dstBytes.length] += srcBytes[i];
}
}


return dstBytes;
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}


return null;
}


public static String encrypt(String key, String value) {
return encrypt(passwordTo16BitKey(key), value);
}


public static String encrypt(byte[] key, String value) {
try {
byte[] initVector = Encryptor.getRandomInitialVector();
IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);


byte[] encrypted = cipher.doFinal(value.getBytes());
return Base64.getEncoder().encodeToString(encrypted) + " " + Base64.getEncoder().encodeToString(initVector);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}


public static String decrypt(String key, String encrypted) {
return decrypt(passwordTo16BitKey(key), encrypted);
}


public static String decrypt(byte[] key, String encrypted) {
try {
String[] encryptedParts = encrypted.split(" ");
byte[] initVector = Base64.getDecoder().decode(encryptedParts[1]);
if (initVector.length != 16) {
return null;
}


IvParameterSpec iv = new IvParameterSpec(initVector);
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");


Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);


byte[] original = cipher.doFinal(Base64.getDecoder().decode(encryptedParts[0]));


return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}


return null;
}
}

用法:

String key = "Password of any length.";
String encrypted = Encryptor.encrypt(key, "Hello World");
String decrypted = Encryptor.decrypt(key, encrypted);
System.out.println(encrypted);
System.out.println(decrypted);

输出示例:

QngBg+Qc5+F8HQsksgfyXg== yDfYiIHTqOOjc0HRNdr1Ng==
Hello World