我已经想了好几天了。加密方法工作正常,但在解密测试期间,我得到了下面的异常。尤其是我在使用: 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)
我的测试是这样的:
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");
}
}
最后是实际代码:
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);
}
}
有人能帮忙吗?我已经读了很多关于这个的书,但仍然没有发现任何错误。
1条答案
按热度按时间yc0p9oo01#
T_LEN
是身份验证标记的大小(以位为单位)。它应该足够大,使(成功)伪造的风险不超过您的数据所有者可以接受的范围,但与iv没有任何关系。如果你没有一个足够的分析,并且你不是在一个资源受限的环境中(javase从来都不是),那么就使用最大值128。您的主要问题是在encrypt中,您合理地连接iv+密文(对于java来说,它包括标记),但是在decrypt中,您使用第一个字节作为iv,在应该使用的时候使用整个缓冲区作为密文
Arrays.copyOfRange(decoded,IV_LENGTH,decoded.length)
.而且,aes密钥必须正好是16、24或32字节,并且应该是随机位,这在java中不能可靠地直接表示
String
. 通常你应该使用byte[]
如果需要将其作为字符串传递或存储,请将其编码到(并从)hex或base64。最后,在加密时使用
getBytes()
作为utf-8,但在解密时使用new String
使用默认编码,这种编码因jvm而异,通常取决于环境,通常不是utf-8,在这种情况下,它可能返回“mojibake”(实际上是垃圾)而不是加密的数据。哦,还有
AEADBadTagException
不是的子类IllegalArgumentException
.