由于以下特定原因,没有完整性检查
- 从用例中看不出需要。
"AES/GCM/NoPadding"
模式仅在Java 7及以上版本中可用。
- 是否部署 HMAC 和/或 AESCMAC(建议)取决于用户。
- 这将需要至少一个额外的密钥和两个完全的传递。
如果你在双方都使用 GCM 模式的实现——例如在 Java 6 上使用 Bouncy Castle,请尽管使用它,因为它更加安全(只要“IV”确实是唯一的)。更改实现应该非常容易。
有关加密的实现注意事项
- 当在不受限制的客户端/服务器角色中使用时,此实现不安全,因为存在填充奇偶校验攻击(平均每字节需要 128 次尝试或更少,与算法或密钥大小无关)。在解密之前,您需要在加密数据上使用 MAC、HMAC 或 Signature,并验证它以部署在客户端/服务器模式下。
- 如果解密失败,则 Decrypt 将返回 null。这只能指示填充异常,应该足够处理它(我警告过填充奇偶校验攻击了吗?)
- 无效的密钥将返回为
InvalidArgumentException
。
- 所有其他与安全相关的异常都“被扫除在地”,因为这意味着 Java 运行时环境无效。例如,支持
"UTF-8"
和 "AES/CBC/PKCS5Padding"
对于每个 Java SE 实现都是必需的。
一些其他注意事项
- 请不要尝试相反的操作,直接将字节插入到加密方法的输入字符串中(例如使用
new String(byte[])
)。该方法可能会悄然失败!
- 针对可读性进行了优化。如果您更喜欢速度和更好的内存占用率,请选择 Base64 流和
CipherStream
实现。
- 您需要至少 Java 6 SE 或兼容版本才能运行此代码。
- 对于 AES 密钥大小超过 128 位的加密/解密,可能会失败,因为您可能需要无限制的加密策略文件(可从 Oracle 获得)。
- 在出口加密时要注意政府法规。
- 此实现使用十六进制密钥而不是 base64 密钥,因为它们足够小,并且十六进制更容易手动编辑/验证。
- 使用从 JDK 检索的十六进制和 base64 编码/解码,没有任何外部库。
- 非常简单易用,但当然不是很面向对象,在 encrypt/decrypt 中未使用对象实例的缓存。随意重构。
好的,这里有一些代码...
public static String encrypt(final String plainMessage,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] encodedMessage = plainMessage.getBytes(Charset
.forName("UTF-8"));
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
final byte[] ivData = new byte[blockSize];
final SecureRandom rnd = SecureRandom.getInstance("SHA1PRNG");
rnd.nextBytes(ivData);
final IvParameterSpec iv = new IvParameterSpec(ivData);
cipher.init(Cipher.ENCRYPT_MODE, symKey, iv);
final byte[] encryptedMessage = cipher.doFinal(encodedMessage);
final byte[] ivAndEncryptedMessage = new byte[ivData.length
+ encryptedMessage.length];
System.arraycopy(ivData, 0, ivAndEncryptedMessage, 0, blockSize);
System.arraycopy(encryptedMessage, 0, ivAndEncryptedMessage,
blockSize, encryptedMessage.length);
final String ivAndEncryptedMessageBase64 = DatatypeConverter
.printBase64Binary(ivAndEncryptedMessage);
return ivAndEncryptedMessageBase64;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during encryption", e);
}
}
public static String decrypt(final String ivAndEncryptedMessageBase64,
final String symKeyHex) {
final byte[] symKeyData = DatatypeConverter.parseHexBinary(symKeyHex);
final byte[] ivAndEncryptedMessage = DatatypeConverter
.parseBase64Binary(ivAndEncryptedMessageBase64);
try {
final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
final int blockSize = cipher.getBlockSize();
final SecretKeySpec symKey = new SecretKeySpec(symKeyData, "AES");
final byte[] ivData = new byte[blockSize];
System.arraycopy(ivAndEncryptedMessage, 0, ivData, 0, blockSize);
final IvParameterSpec iv = new IvParameterSpec(ivData);
final byte[] encryptedMessage = new byte[ivAndEncryptedMessage.length
- blockSize];
System.arraycopy(ivAndEncryptedMessage, blockSize,
encryptedMessage, 0, encryptedMessage.length);
cipher.init(Cipher.DECRYPT_MODE, symKey, iv);
final byte[] encodedMessage = cipher.doFinal(encryptedMessage);
final String message = new String(encodedMessage,
Charset.forName("UTF-8"));
return message;
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(
"key argument does not contain a valid AES key");
} catch (BadPaddingException e) {
return null;
} catch (GeneralSecurityException e) {
throw new IllegalStateException(
"Unexpected exception during decryption", e);
}
}
使用方法:
String plain = "Zaphod's just zis guy, ya knöw?";
String encrypted = encrypt(plain, "000102030405060708090A0B0C0D0E0F");
System.out.println(encrypted);
String decrypted = decrypt(encrypted, "000102030405060708090A0B0C0D0E0F");
if (decrypted != null && decrypted.equals(plain)) {
System.out.println("Hey! " + decrypted);
} else {
System.out.println("Bummer!");
}
KeyGenerator kgen
从未被使用过。它在那里有什么作用?KeyGenerator
用于选择一个新的随机密钥。但是看起来你想重用一个已经存储在String
中的现有密钥(可能是Base-64编码的)。 - erickson