GCMParameterSpec抛出InvalidAlgorithmParameterException:未知参数类型

时间:2017-03-21 13:18:55

标签: java android encryption cryptography aes-gcm

我正在做android数据加密以保存在SharedPreferences中。 GCMParameterSpec是在API 19中引入的,我用于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文档:

  

如果给定的算法参数不适合此密码,或者此密码需要算法参数且params为null,或者给定的算法参数意味着加密强度超出法定限制(根据配置的管辖区策略文件确定)

我的问题是:这种行为有特定原因吗?为什么Cipher的init方法没有识别params?

我甚至尝试过加密而不提供特定的IV:

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

一旦我尝试以同样的方式解密:

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

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

我尝试仅将GCMParameterSpec提供给解密,并且我获得了未知参数类型异常。

感谢任何帮助

3 个答案:

答案 0 :(得分:4)

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

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

由于您具有标准标记大小,因此这对您的实现没有任何问题。

如果标签大小不同,则解决方案变得更加复杂,因为验证将仅使用结果标签的最左侧字节。遗憾的是,标记生成和验证隐藏在Cipher类的API设计中。

答案 1 :(得分:1)

正如@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);
}

答案 2 :(得分:-3)

试试这段代码......

    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");
}