【问题标题】:AES-GCM with BouncyCastle throws "mac check in GCM failed" when used with IV与 IV 一起使用时,带有 BouncyCastle 的 AES-GCM 会抛出“GCM 中的 MAC 检查失败”
【发布时间】:2016-06-04 03:37:58
【问题描述】:

我对开发加密技术还比较陌生。现在我正在尝试编写一个使用带有 AES-GCM 的 BouncyCastle 加密和解密字符串的类。我阅读了实施加密时必须考虑的事项。其中之一是您应该始终使用随机静脉注射。 问题是,每次我尝试使用 IV 初始化我的密码时,它都无法正确解密我的文本。
它只是抛出以下异常:

javax.crypto.AEADBadTagException: mac check in GCM failed
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher$AEADGenericBlockCipher.doFinal(Unknown Source)
at org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher.engineDoFinal(Unknown Source)
at javax.crypto.Cipher.doFinal(Cipher.java:2165)
at BouncyCastleEX.decrypt(BouncyCastleEX.java:78)
at BouncyCastleEX.main(BouncyCastleEX.java:43)

我正在使用以下方法来加密和解密我的数据。

private static final String fallbackSalt = "ajefa6tc73t6raiw7tr63wi3r7citrawcirtcdg78o2vawri7t";
private static final int iterations = 2000;
private static final int keyLength = 256;
private static final SecureRandom random = new SecureRandom();

public byte[] encrypt(String plaintext, String passphrase, String salt)
        throws Exception {
    SecretKey key = generateKey(passphrase, salt);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    cipher.init(Cipher.ENCRYPT_MODE, key, generateIV(cipher),random);
    return cipher.doFinal(plaintext.getBytes());
}

public String decrypt(byte[] encrypted, String passphrase, String salt)
        throws Exception {
    SecretKey key = generateKey(passphrase, salt);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    cipher.init(Cipher.DECRYPT_MODE, key, generateIV(cipher),random);
    return new String(cipher.doFinal(encrypted));
}

private SecretKey generateKey(String passphrase, String salt)
        throws Exception {
    PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(),
            salt.getBytes(), iterations, keyLength);
    SecretKeyFactory keyFactory = SecretKeyFactory
            .getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
    return keyFactory.generateSecret(keySpec);
}

private IvParameterSpec generateIV(Cipher cipher) throws Exception {
    byte[] ivBytes = new byte[cipher.getBlockSize()];
    random.nextBytes(ivBytes);
    return new IvParameterSpec(ivBytes);
}

如果我从 cipher.init(...) 中删除“generateIV(cipher)”,一切都会完美无缺。但据我所知,它极大地削弱了加密。

知道我无法弄清楚这是代码中的一个小错误还是其他我一无所知的错误。

非常感谢您的帮助,非常感谢!

【问题讨论】:

  • 这不是代码中的错误,您必须在解密时使用与加密时相同的 IV。是否可以公开发送 IV 取决于使用 IV 所需的效果。

标签: java encryption bouncycastle initialization-vector aes-gcm


【解决方案1】:

您必须使用相同的 IV 进行加密和解密。它不必是秘密的,而对于 AES-GCM 来说只有 唯一(从技术上讲,它是一个 nonce)。一种常见的方法是在密文前面加上 IV 并在解密之前将其删除。

使用消息计数器而不是随机生成 IV 也很常见。如果更改了密钥,则应将 IV 重置为初始值并重新开始计数。在某些数量的消息中,您需要一个新密钥,因为 AES-GCM 的安全保证会失效。该数字介于 248 和 264 条消息之间。

【讨论】:

  • 现在一切正常。谢谢!如果有人遇到同样的问题,我使用 BouncyCastle 的 Arrays.concatenate(...) 和 Arrays.copyOfRange(...) 方法。只需确保您的范围的第二个索引不包含在结果数组中。这意味着您的第二个数组是从索引 12 到数组的大小!直到大小 - 1。
  • 对不起。我是该网站的新手。我刚刚发布了我的最终代码。它似乎工作得很好。
【解决方案2】:

这是我在 Artjom 的帮助下编写的代码的最终版本。 它似乎工作得很好,但如果您发现任何错误或削弱安全性的事情,请告诉我。

import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Arrays;
import com.sun.org.apache.xml.internal.security.utils.Base64;

public class BouncyCastleEX {

private static final int iterations = 2000;
private static final int keyLength = 256;
private static final SecureRandom random = new SecureRandom();
private static BouncyCastleEX instance = null;

public String encryptString(String plaintext, String passphrase, String salt)
        throws Exception {
    return Base64.encode(encrypt(plaintext, passphrase, salt));
}

public String decryptString(String encrypted, String passphrase, String salt)
        throws Exception {
    return decrypt(Base64.decode(encrypted), passphrase, salt);
}

private BouncyCastleEX() {
    Security.addProvider(new BouncyCastleProvider());
}

public static BouncyCastleEX getInstance() {
    if (instance == null) {
        instance = new BouncyCastleEX();
    }
    return instance;
}

private byte[] encrypt(String plaintext, String passphrase, String salt)
        throws Exception {
    SecretKey key = generateKey(passphrase, salt);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    byte[] ivBytes = generateIVBytes(cipher);
    cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(ivBytes),
            random);
    return Arrays
            .concatenate(ivBytes, cipher.doFinal(plaintext.getBytes()));
}

private String decrypt(byte[] encrypted, String passphrase, String salt)
        throws Exception {
    SecretKey key = generateKey(passphrase, salt);
    Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
    cipher.init(Cipher.DECRYPT_MODE, key,
            new IvParameterSpec(Arrays.copyOfRange(encrypted, 0, 12)),
            random);
    return new String(cipher.doFinal(Arrays.copyOfRange(encrypted, 12,
            encrypted.length)));
}

private SecretKey generateKey(String passphrase, String salt)
        throws Exception {
    PBEKeySpec keySpec = new PBEKeySpec(passphrase.toCharArray(),
            salt.getBytes(), iterations, keyLength);
    SecretKeyFactory keyFactory = SecretKeyFactory
            .getInstance("PBEWITHSHA256AND256BITAES-CBC-BC");
    return keyFactory.generateSecret(keySpec);
}

private byte[] generateIVBytes(Cipher cipher) throws Exception {
    byte[] ivBytes = new byte[12];
    random.nextBytes(ivBytes);

    return ivBytes;
}

}

【讨论】:

  • 你可能应该随机生成盐并写在密文前面。基本上,盐的处理方式与 IV 完全相同。
猜你喜欢
  • 1970-01-01
  • 2014-05-12
  • 1970-01-01
  • 2021-05-29
  • 2021-10-08
  • 2016-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多