簡體   English   中英

使用AES / GCM / NoPadding的IvParameterSpec和GCMParameterSpec之間的差異

[英]Difference between IvParameterSpec and GCMParameterSpec with AES/GCM/NoPadding

我使用AES/GCM/NoPadding算法加密Android上的一些數據(API 19及以后版本),然后再將其解密。

我使用的密鑰大小是32字節,並提供給我

除了加密, 我還想知道我何時嘗試解密並使用錯誤的密鑰 這就是為什么我更喜歡使用GCM作為我的模式來獲得驗證完整性的好處(我相信可以安全地假設密文或密鑰中的哪一個是錯誤的會導致解密異常而不是亂碼文本)

我面臨的問題是在Android API 19上使用上面的算法並使用GCMParameterSpec初始化密碼我得到NoSuchAlgorithmException ,我沒有指定任何提供者自己允許Android為我選擇一個可以支持我的算法。 21+以上的算法可用。 這就是我初始化的方法(類似於解密),整個課程在本文末尾發布。

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

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

那么通過改變這些參數會發生什么? 我仍然可以獲得GCM的所有好處嗎?

因為在嘗試使用錯誤的密鑰時拋出的異常是不同的。 在API 19上,當使用IvParameterSpec時拋出BadPaddingException ,在API 21+上使用AEADBADTagException拋出GCMParameterSpec

通過所有Android API級別僅使用IvParameterSpec並通過BadPaddingException驗證完整性是否正確和安全? 我不想為不同的平台有不同的實現,所以我只想使用一個。

此外,在API 21+上,如果我使用GCMParameterSpec加密,然后使用IvParameterSpec解密它解密就好了! 反之亦然。 這怎么樣?

如果在API 19上無法實現上述目標,那么我可以選擇使用哪種加密算法和策略( AES/CBC/PKCS5Padding與HMAC?)來驗證密鑰的完整性。

全班代碼:

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

另外,請隨意建議改進所提供的課程以及您的答案,謝謝。

我也想知道我何時嘗試解密並使用錯誤的密鑰。

這沒關系,但請理解,無效標簽可能意味着標簽本身被更改,密文被更改,IV被更改,AAD被更改或確實密鑰不正確。

您還可以使用密鑰檢查值或類似的東西來檢查密鑰大小是否正確,然后再進行解密。 但請注意,對手也可以改變該檢查值。

那么通過改變這些參數會發生什么? 我仍然可以獲得GCM的所有好處嗎?

可以肯定的是,GCM的改造方式基本上是兼容的,但仍然有更多的配置選項(主要是標簽尺寸) - 如果你需要配置它。 AEADBADTagExceptionBadPaddingException因此代碼應該適用於每個,即使AEADBADTagException更具體。

通過所有Android API級別僅使用IvParameterSpec並通過BadPaddingException驗證完整性是否正確和安全? 我不想為不同的平台有不同的實現,所以我只想使用一個。

當然。 請注意,只有標記可能會拋出BadPaddingException ,因此這樣的異常會正確識別身份驗證問題。

此外,在API 21+上,如果我使用GCMParameterSpec加密,然后使用IvParameterSpec解密它解密就好了! 反之亦然。 這怎么樣?

您的代碼針對每種類型的參數規范運行,因為您指定的標記大小與默認值相同:128位。 它不適用於較小的標簽大小。


代碼評論:

  • charset應該是一個常數( static final );
  • 密鑰不應作為字節數組傳遞,而應作為SecretKey實例傳遞;
  • IV應始終為12個字節,因此不需要傳送IV大小;
  • 如果你確實傳達了IV大小,那么你需要檢查它是否是一個有效值,目前攻擊者可以控制該字節(讓你創建一個大的IV或拋出ArrayIndexOutOfBounds異常);
  • 處理異常時,您需要區分代碼問題(GCM算法不可用)和輸入相關問題(不良大小);
  • 目前您的代碼適用於小消息; 某種流式傳輸對於較大的消息會很好。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM