GCMParameterSpec 抛出 InvalidAlgorithmParameterException:未知参数类型

GCMParameterSpec throws InvalidAlgorithmParameterException: unknown parameter type

我正在进行 android 数据加密以保存在 SharedPreferences 中。 GCMParameterSpec 是在 API 19 中的 Android 中引入的,我将其用于 AES/GCM/NoPadding 加密。这就是我实现它的方式:

Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
c.init(Cipher.ENCRYPT_MODE, getSecretKey(context),new GCMParameterSpec(128,Base64.decode(myGeneratedIV, Base64.DEFAULT)));

我的问题是,在 Android 4.4.2 (API 19) 中,我得到了提到的错误,但是从 API 21 开始它起作用了。

关于异常,来自 Android 文档:

if the given algorithm parameters are inappropriate for this cipher, or this cipher requires algorithm parameters and params is null, or the given algorithm parameters imply a cryptographic strength that would exceed the legal limits (as determined from the configured jurisdiction policy files).

我的问题是:这种行为是否有特定原因?为什么 Cipher 的 init 方法不能识别参数?

我什至尝试过在不提供特定 IV 的情况下进行加密:

c.init(Cipher.ENCRYPT_MODE, getSecretKey(context));

有一次我尝试用同样的方式解密:

c.init(Cipher.DECRYPT_MODE, getSecretKey(context));

它抛出相同的异常(InvalidAlgorithmParameterException),表示解密需要 GCMParameterSpec

我尝试只给解密GCMParameterSpec,结果出现未知参数类型异常

感谢任何帮助

试试这个代码...

    private static final String Key = "0123456789abcdef";

public static SecretKey generateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
    return new SecretKeySpec(Key.getBytes("UTF-8"), "AES");
}

public static byte[] encryptMsg(String message, SecretKey secret)
        throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidParameterSpecException, IllegalBlockSizeException, BadPaddingException, UnsupportedEncodingException {

    Cipher cipher;
    cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.ENCRYPT_MODE, secret);
    return cipher.doFinal(message.getBytes("UTF-8"));
}

public static String decryptMsg(byte[] cipherText, SecretKey secret)
        throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidParameterSpecException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException {

    Cipher cipher;
    cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secret);
    return new String(cipher.doFinal(cipherText), "UTF-8");
}

Android 中提供程序中的 CipherSpi 实现可能还不支持 GCMParameterSpec。定义 API 与在底层加密提供程序中为其提供支持不同。

您也可以使用为其他模式提供的标准 IvParameterSpec。只需将 GCMParamterSpec 的 (12) IV/nonce 字节直接用作您的 IV。

由于您拥有标准标签大小,因此这应该不会对您的实施造成任何问题。


如果标签大小不同,则解决方案将变得更加复杂,因为验证将仅使用结果标签的最左边的字节。不幸的是,标签生成和验证隐藏在 Cipher class.

的 API 设计中

正如@Maarten 所说,您可以将 IvParameterSpec 用于设备 运行 KitKat:

private static final int TAG_LENGTH_BYTES = 16;

public byte[] encrypt(...) {
    ...
    Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), getParams(iv));
    ...
}

public byte[] decrypt(...) {
    ...
    Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
    cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(rawEncryptionKey, "AES"), getParams(iv));
    ...
}

private static AlgorithmParameterSpec getParams(final byte[] iv) {
    return getParams(iv, 0, iv.length);
}

private static AlgorithmParameterSpec getParams(final byte[] buf, int offset, int len) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        // GCMParameterSpec should always be present in Java 7 or newer, but it's missing on
        // some Android devices with API level <= 19. Fortunately, we can initialize the cipher
        // with just an IvParameterSpec. It will use a tag size of 128 bits.
        return new IvParameterSpec(buf, offset, len);
    }
    return new GCMParameterSpec(TAG_LENGTH_BYTES * 8, buf, offset, len);
}