xro*_*oss 7 java encryption aes-gcm
几天来我一直在试图解决这个问题。加密方法工作正常,但在解密测试期间我收到以下异常。特别是我正在使用:AES/GCM/NoPadding. 据我所知T_LEN应该是IV_LENGTH*8字节数组表示。该错误真正显示在ExampleCryptografer.java解密方法中: byte[] decryptedText = cipher.doFinal(decoded);
javax.crypto.AEADBadTagException: Tag mismatch!
at java.base/com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:623)
at java.base/com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1116)
at java.base/com.sun.crypto.provider.CipherCore.fillOutputBuffer(CipherCore.java:1053)
at java.base/com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:853)
at java.base/com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:446)
at java.base/javax.crypto.Cipher.doFinal(Cipher.java:2202)
at com.example.ExampleCryptografer.decrypt(ExampleCryptografer.java:61)
at com.example.ExampleCryptograferTest.decrypt_givenEncryptedExample_ShouldSucceed(ExampleCryptograferTest.java:21)
Run Code Online (Sandbox Code Playgroud)
这就是我的测试的样子:
public class ExampleCryptographerTest {
private ExampleCryptographer objectUnderTest = new ExampleCryptographer("knownKeyForTest=");
@Test
public void decrypt_givenEncryptedExample_ShouldSucceed() {
String example = "afasfdafafa=";
String encodedExample = objectUnderTest.encrypt(example);
String result = objectUnderTest.decrypt(encodedExample);
assertThat(result).isNotNull();
assertThat(result.length()).isEqualTo(48);
}
@Test
public void encrypt_givenExample_ShouldSucceed() {
String example = "afasfdafafa=";
String result = objectUnderTest.encrypt(example);
assertThat(result).isNotNull();
assertThat(result.length()).isEqualTo(48);
}
@Test
public void decrypt_givenEncryptedExampleWithOtherKey_ShouldFail() {
String example = "afasfdafafa=";
String encodedExample = new ExampleCryptographer("otherKeyForTest=").encrypt(example);
Throwable throwable = catchThrowable(() -> objectUnderTest.decrypt(encodedExample));
assertThat(throwable)
.isInstanceOf(IllegalArgumentException.class);
}
@Test(expected = InvalidKeyException.class)
public void encrypt_givenInvalidKey_ShouldFail() {
new ExampleCryptographer("invalid").encrypt("test");
}
Run Code Online (Sandbox Code Playgroud)
}
最后是实际代码:
public class ExampleCryptographer {
private static final String ALGORITHM = "AES";
private final Key key;
private static final int T_LEN = 96;
private static final int IV_LENGTH = 12;
private final Base64 base64 = new Base64(76, null, true);
@SneakyThrows
public ExampleCryptographer(@Value("${myKey}") String secretKey) {
this.key = new SecretKeySpec(secretKey.getBytes(), ALGORITHM);
}
@SneakyThrows
public String encrypt(@NonNull String text) {
byte[] iv = new byte[IV_LENGTH];
(new SecureRandom()).nextBytes(iv);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec ivSpec = new GCMParameterSpec(T_LEN, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
byte[] ciphertext = cipher.doFinal(text.getBytes(UTF_8));
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return base64.encodeAsString(encrypted);
}
@SneakyThrows
public String decrypt(@NonNull String encryptedText) {
byte[] decoded = base64.decode(encryptedText);
byte[] iv = Arrays.copyOfRange(decoded, 0, IV_LENGTH);
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec ivSpec = new GCMParameterSpec(T_LEN, iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
byte[] decryptedText = cipher.doFinal(decoded);
return new String(decryptedText);
}
Run Code Online (Sandbox Code Playgroud)
}
有人可以帮忙吗?我已经阅读了很多,但仍然找不到任何错误。
dav*_*085 13
T_LEN是 AUTHENTICATION TAG 的大小(以位为单位)。它应该足够大,使得(成功)伪造的风险不超过数据所有者可接受的范围,但与 IV 没有任何关系。如果您没有进行分析,认为少就足够,并且您没有处于资源受限的环境中(JavaSE 从来都不是),则只需使用最大值 128。
你的主要问题是在加密时你合理地连接IV+密文(对于Java来说包括标签),但在解密时你使用第一个字节作为IV,整个缓冲区作为密文,而它应该是Arrays.copyOfRange(decoded,IV_LENGTH,decoded.length)。
此外,AES 密钥必须正好是 16、24 或 32 字节,并且应该是随机位,这不能直接在 Java 中可靠地表示String。byte[]一般来说,如果您需要将其作为字符串编码传递或存储到十六进制或 base64(并从中解码),则应该使用and 。
最后,在加密时,您使用 UTF-8 进行编码getBytes(),但在解密时,您new String使用默认编码进行解码,默认编码因 JVM 而异,并且通常取决于环境,并且通常不是 UTF-8,在这种情况下,可能会返回“mojibake”(实际上是垃圾)不是您加密的数据。
哦,并且AEADBadTagException不是 的子类IllegalArgumentException。
| 归档时间: |
|
| 查看次数: |
22716 次 |
| 最近记录: |