我正在尝试使用AES加密算法加密文本,将此加密文本保存到文件中,然后重新打开并解密这些文本。以下是我的加密和解密逻辑
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] stringBytes = clear_text.getBytes();
byte[] raw = cipher.doFinal(stringBytes);
return Base64.encodeBase64String(raw);
这是解密逻辑
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
byte[] raw = Base64.decodeBase64(encText);
byte[] stringBytes = cipher.doFinal(raw);
String clear_text = new String(stringBytes, "UTF8");
return clear_text;
我收到了 BadPaddingSize异常。我的猜测是,通过使用SecureRandom类,这两种方法在加密或删除文本时使用不同的键。有没有办法可以在两个例程中使用相同的键?
答案 0 :(得分:1)
是的,您可以使用相同的密钥;它甚至需要使用相同的密钥。但是,您应该永远不要使用相同的密钥/ IV组合,因为它不安全。因此,IV通常会以密文为前缀。
请注意,以下实现向您展示了如何在没有SecureRandom
的情况下生成随机IV,但这有点不诚实,因为Cipher
类将在内部使用默认值来创建IV。对于CBC,攻击者可能知道IV,但攻击者不应该将其与随机数据区分开来。
在此示例中,关键数据仅存储在“常量”中。将密钥存储在源代码中可能无法提供足够的安全性。相反,它通常使用公钥,密码加密,存储在USB密钥上,存储在智能卡或HSM等中。然而,密钥管理是一个巨大的主题,所以我不会进一步讨论这个答案。
在Java中,您应该使用SecretKey
/ SecretKeySpec
来创建来自已知数据的密钥,并使用IvParameterSpec
来创建已知的IV(或Nonce)。
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESWithStaticKeyAndRandomIV {
private static byte[] KEY = new byte[] { (byte) 0x14, (byte) 0x0b,
(byte) 0x41, (byte) 0xb2, (byte) 0x2a, (byte) 0x29, (byte) 0xbe,
(byte) 0xb4, (byte) 0x06, (byte) 0x1b, (byte) 0xda, (byte) 0x66,
(byte) 0xb6, (byte) 0x74, (byte) 0x7e, (byte) 0x14 };
public static byte[] encrypt(byte[] plaintext) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey key = new SecretKeySpec(KEY, "AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = new byte[iv.length
+ cipher.getOutputSize(plaintext.length)];
System.arraycopy(iv, 0, ciphertext, 0, iv.length);
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
iv.length);
return ciphertext;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | InvalidParameterSpecException
| ShortBufferException | IllegalBlockSizeException
| BadPaddingException e) {
throw new IllegalStateException(
"CBC encryption with standard algorithm should never fail",
e);
}
}
public static byte[] decrypt(byte[] ciphertext) throws IllegalBlockSizeException,
BadPaddingException {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec key = new SecretKeySpec(KEY, "AES");
if (ciphertext.length < cipher.getBlockSize()) {
throw new IllegalArgumentException(
"Ciphertext too small to contain IV");
}
IvParameterSpec ivSpec = new IvParameterSpec(ciphertext, 0,
cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length
- cipher.getBlockSize())];
cipher.doFinal(ciphertext, cipher.getBlockSize(), ciphertext.length
- cipher.getBlockSize(), plaintext, 0);
return plaintext;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | ShortBufferException
| InvalidAlgorithmParameterException e) {
throw new IllegalStateException(
"CBC decryption with standard algorithm should be available",
e);
}
}
public static void main(String[] args) throws Exception {
byte[] plaintext = decrypt(encrypt("owlstead".getBytes(StandardCharsets.UTF_8)));
System.out.println(new String(plaintext, StandardCharsets.UTF_8));
}
}
使用密钥库(您现在必须使用JCEKS):
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyStore;
import java.security.KeyStore.ProtectionParameter;
import java.security.KeyStore.SecretKeyEntry;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CertificateException;
import java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESWithStaticKeyAndRandomIV {
private static final String KEY_ALIAS = "secret";
private static byte[] KEY = new byte[] { (byte) 0x14, (byte) 0x0b,
(byte) 0x41, (byte) 0xb2, (byte) 0x2a, (byte) 0x29, (byte) 0xbe,
(byte) 0xb4, (byte) 0x06, (byte) 0x1b, (byte) 0xda, (byte) 0x66,
(byte) 0xb6, (byte) 0x74, (byte) 0x7e, (byte) 0x14 };
private static ProtectionParameter PASSWORD = new KeyStore.PasswordProtection(
new char[] {'p', 'a', 's', 's', 'w', 'o', 'r', 'd'});
private final KeyStore store;
private AESWithStaticKeyAndRandomIV(KeyStore store) {
this.store = store;
}
public byte[] encrypt(byte[] plaintext) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey key;
try {
key = ((SecretKeyEntry) store.getEntry(KEY_ALIAS, PASSWORD))
.getSecretKey();
} catch (UnrecoverableEntryException | KeyStoreException e) {
throw new IllegalStateException("What key?", e);
}
cipher.init(Cipher.ENCRYPT_MODE, key);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] ciphertext = new byte[iv.length
+ cipher.getOutputSize(plaintext.length)];
System.arraycopy(iv, 0, ciphertext, 0, iv.length);
cipher.doFinal(plaintext, 0, plaintext.length, ciphertext,
iv.length);
return ciphertext;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | InvalidParameterSpecException
| ShortBufferException | IllegalBlockSizeException
| BadPaddingException e) {
throw new IllegalStateException(
"CBC encryption with standard algorithm should never fail",
e);
}
}
public byte[] decrypt(byte[] ciphertext) throws IllegalBlockSizeException,
BadPaddingException {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey key;
try {
key = ((SecretKeyEntry) store.getEntry(KEY_ALIAS, PASSWORD))
.getSecretKey();
} catch (UnrecoverableEntryException | KeyStoreException e) {
throw new IllegalStateException("What key?", e);
}
if (ciphertext.length < cipher.getBlockSize()) {
throw new IllegalArgumentException(
"Ciphertext too small to contain IV");
}
IvParameterSpec ivSpec = new IvParameterSpec(ciphertext, 0,
cipher.getBlockSize());
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] plaintext = new byte[cipher.getOutputSize(ciphertext.length
- cipher.getBlockSize())];
cipher.doFinal(ciphertext, cipher.getBlockSize(), ciphertext.length
- cipher.getBlockSize(), plaintext, 0);
return plaintext;
} catch (InvalidKeyException | NoSuchAlgorithmException
| NoSuchPaddingException | ShortBufferException
| InvalidAlgorithmParameterException e) {
throw new IllegalStateException(
"CBC decryption with standard algorithm should be available",
e);
}
}
public static KeyStore createStoreWithSecretKey() {
try {
KeyStore keyStore = KeyStore.getInstance("JCEKS");
keyStore.load(null);
SecretKey key = new SecretKeySpec(KEY, "AES");
keyStore.setEntry(KEY_ALIAS, new KeyStore.SecretKeyEntry(key), PASSWORD);
return keyStore;
} catch (KeyStoreException | NoSuchAlgorithmException
| CertificateException | IOException e) {
throw new IllegalStateException("Unable to create key store", e);
}
}
public static void main(String[] args) throws Exception {
AESWithStaticKeyAndRandomIV crypt = new AESWithStaticKeyAndRandomIV(
createStoreWithSecretKey());
byte[] plaintext = crypt.decrypt(crypt.encrypt("owlstead"
.getBytes(StandardCharsets.UTF_8)));
System.out.println(new String(plaintext, StandardCharsets.UTF_8));
}
}