带有AES / GCM / NoPadding的IvParameterSpec和GCMParameterSpec之间的区别 [英] Difference between IvParameterSpec and GCMParameterSpec with AES/GCM/NoPadding

查看:275
本文介绍了带有AES / GCM / NoPadding的IvParameterSpec和GCMParameterSpec之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 AES / GCM / NoPadding 算法对Android(API 19及更高版本)上的某些数据进行加密,然后再将其解密。

I am using AES/GCM/NoPadding algorithm to encrypt some data on Android (API 19 and onwards) and then later decrypt it back.

我使用的密钥大小为32个字节,并提供给我

The key size I use is 32 bytes and is provided to me

除了加密外,我也想知道我何时尝试解密并使用错误的密钥。这就是为什么我更喜欢使用GCM作为我的模式来获得验证完整性的好处(我相信可以安全地假设密文或密钥(无论哪个有问题)会导致不良的解密异常而不是乱码)

In addition to the encryption, I also want to know when I try to decrypt and use a wrong key. Which is why I prefer to use GCM as my mode to get the benefits of verifying integrity (I believe its safe to assume whether the ciphertext or the key whichever is faulty would result in a bad decrypt exception rather than garbled text)

我面临的问题是,在Android API 19中,使用上述算法并使用 GCMParameterSpec 初始化密码,我得到了 NoSuchAlgorithmException ,我自己未指定任何提供程序,允许Android为我选择一个可以支持我的算法的提供程序。在21+时可以使用算法。
这就是我初始化的方式(与解密类似),整个类都张贴在这篇文章的结尾。

The problem I face is that on Android API 19 using the algorithm above and initializing the cipher with GCMParameterSpec I get a NoSuchAlgorithmException, I do not specify any provider myself allowing Android to pick one for me which can support my algorithm. On 21+ the Algorithm is available. This is how I am initializing(similar for decryption), the entire class is posted at the end of this post.

cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));

但是,如果我使用 IvParameterSpec(iv)作为我的 AlgorithmParameters 而不是 GCMParameterSpec ,那么代码可以正常工作。

However, if I use IvParameterSpec(iv) as my AlgorithmParameters instead of GCMParameterSpec then the code works fine.

那么更改这些参数会发生什么?我还能获得GCM的所有相同好处吗?

So what happens by changing these parameters? Do I still get all the same benefits of GCM?

因为尝试使用错误的密钥时抛出的异常不同。在API 19+ AEADBADTagException 中,当使用 IvParameterSpec 时抛出 BadPaddingException 使用 GCMParameterSpec 时会抛出code>。

Because the exceptions thrown are different when attempting to use a wrong key. On API 19 BadPaddingException is thrown when IvParameterSpec is used, on API 21+ AEADBADTagException is thrown when GCMParameterSpec is used.

仅使用 IvParameterSpec 通过所有Android API级别,并通过 BadPaddingException 验证完整性?我不想为不同的平台使用不同的实现,所以我只想使用一个实现。

Is it correct and secure to use just the IvParameterSpec through all the Android API levels and verify the integrity through BadPaddingException? I do not want to have different implementations for different platforms so I would want to use one only.

此外,如果我使用<$ c $进行加密,请使用API​​ 21+ c> GCMParameterSpec ,然后使用 IvParameterSpec 解密就可以了!反之亦然。

Also, on API 21+, if I encrypt using GCMParameterSpec and then later use IvParameterSpec to decrypt it decrypts fine! and the same vice versa. How is that working?

如果在API 19上无法实现以上要求,那么我可以用作加密算法和策略的选择有哪些( AES / CBC / PKCS5Padding 与HMAC?)以验证密钥的完整性。

If the above is not possible on API 19 then what are my possible options to use as an encryption algorithm and a strategy to use(AES/CBC/PKCS5Padding with HMAC?) to verify the integrity of the key.

完整的类代码:

import android.util.Base64;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

final class Encryption {
    private static final String ALGORITHM = "AES/GCM/NoPadding";
    private static final int TAG_LENGTH_BIT = 128;
    private static final int IV_LENGTH_BYTE = 12;

    private final SecureRandom secureRandom;
    private Cipher cipher;
    private final Charset charset = StandardCharsets.UTF_8;

    public Encryption() {
        secureRandom = new SecureRandom();
    }

    public String encrypt(byte[] key, String rawData) throws Exception {
        try {
            byte[] iv = new byte[IV_LENGTH_BYTE];
            secureRandom.nextBytes(iv);

            cipher = Cipher.getInstance(ALGORITHM);
            //This is where I switch to IvParameterSpec(iv)
            cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));

            byte[] encrypted = cipher.doFinal(rawData.getBytes(charset));

            ByteBuffer byteBuffer = ByteBuffer.allocate(1 + iv.length + encrypted.length);
            byteBuffer.put((byte) iv.length);
            byteBuffer.put(iv);
            byteBuffer.put(encrypted);
            return Base64.encodeToString(byteBuffer.array(), Base64.NO_WRAP);
        } catch (Exception e) { //ignore this SO
            throw new Exception(e);
        }
    }


    public String decrypt(byte[] key, String encryptedData) throws Exception {
        try {
            ByteBuffer byteBuffer = ByteBuffer.wrap(Base64.decode(encryptedData, Base64.NO_WRAP));

            int ivLength = byteBuffer.get();
            byte[] iv = new byte[ivLength];
            byteBuffer.get(iv);
            byte[] encrypted = new byte[byteBuffer.remaining()];
            byteBuffer.get(encrypted);

            cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), new GCMParameterSpec(TAG_LENGTH_BIT, iv));
            byte[] decrypted = cipher.doFinal(encrypted);

            //Paranoia
            Arrays.fill(iv, (byte) 0);
            Arrays.fill(rawEncryptionKey, (byte) 0);
            Arrays.fill(encrypted, (byte) 0);

            return new String(decrypted, charset);
        } catch (Exception e) { //ignore this SO
            // On API 19 BadPaddingException is thrown when IvParameterSpec is used
            // On API 21+ AEADBADTagException is thrown
            throw new Exception("could not decrypt", e);
        }
    }
}

此外,请随时提出建议

推荐答案


我还想知道何时我尝试解密并使用错误的密钥。

I also want to know when I try to decrypt and use a wrong key.

没关系,但请注意,无效标签可能意味着标签本身就是

That's OK, but please understand that an invalid tag can mean that the tag itself was altered, the ciphertext was altered, the IV was altered, the AAD was altered or that indeed the key is incorrect.

您还可以使用密钥检查值或类似的方法来更改密文,IV,AAD或密钥确实不正确。在解密之前检查密钥大小是否正确。但是请注意,对手也可能更改该支票值。

You could also use a key check value or something similar to check if the key size is correct or not before decrypting. But note that an adversary could also alter that check value.


那么,更改这些参数会发生什么?我仍然可以获得GCM的所有相同好处吗?

So what happens by changing these parameters? Do I still get all the same benefits of GCM?

当然,但是GCM的改装方式使其在很大程度上兼容但仍具有更多配置选项(主要是标签大小)-如果需要进行配置。 AEADBADTagException BadPaddingException ,因此即使 AEADBADTagException 更具体。

For sure, but GCM was retrofitted in such a way that it was largely compatible but still has more configuration options (mainly the tag size) - if you require to configure that. The AEADBADTagException is a BadPaddingException so the code should work for each, even though AEADBADTagException is more specific.


仅使用 IvParameterSpec 遍历所有Android API级别,并通过 BadPaddingException 验证完整性?我不想为不同的平台使用不同的实现,所以我只想使用一个。

Is it correct and secure to use just the IvParameterSpec through all the Android API levels and verify the integrity through BadPaddingException? I do not want to have different implementations for different platforms so I would want to use one only.

当然。请注意,只有标记可以引发 BadPaddingException ,所以这样的异常确实可以正确识别身份验证问题。

For sure. Note that only the tag could throw a BadPaddingException, so such an exception does correctly identify a problem with the authentication.


此外,在API 21+上,如果我使用GCMParameterSpec进行加密,然后再使用IvParameterSpec对其进行解密,则可以解密!反之亦然。

Also, on API 21+, if I encrypt using GCMParameterSpec and then later use IvParameterSpec to decrypt it decrypts fine! and the same vice versa. How is that working?

您的代码正在为每种类型的参数规范运行,因为您指定的标签大小与默认值相同:128位。

Your code is running for each type of parameter specification because you specified the same tag size as the default: 128 bits. It wouldn't work with a smaller tag size.

代码注释:


  • 字符集应该是常量(静态最终值);

  • 键不应作为字节数组传递,而应作为 SecretKey 实例传递;

  • IV应该始终是12个字节,因此不需要传递IV大小;

  • 如果您确实传递了IV大小,则需要检查它是否为有效值,当前对手可以控制该字节(并让您创建较大的IV或抛出 ArrayIndexOutOfBounds 异常);

  • 在处理异常时,您需要区分在代码问题(GCM算法不可用)和输入相关问题(错误大小)之间-我在此处处写了一个小的引物作为答案;

  • 当前,您的代码适合小消息;某些类型的流式处理对于较大的消息将是很好的。

  • charset should be a constant (static final);
  • keys should not be passed as byte arrays but as SecretKey instances;
  • the IV should always be 12 bytes, so the IV size doesn't need to be communicated;
  • if you do communicate the IV size then you need to check if it is a valid value, currently an adversary can control that byte (and let you create a large IV or throw in ArrayIndexOutOfBounds exception);
  • when processing exceptions you will want to differentiate between code issues (GCM algorithm not available) and input related issues (bad size) - I wrote a small primer as answer here;
  • currently your code is OK for small messages; some kind of streaming would be nice for larger messages.

这篇关于带有AES / GCM / NoPadding的IvParameterSpec和GCMParameterSpec之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆