代码之家  ›  专栏  ›  技术社区  ›  Roman Nikitchenko

我可以在Bouncy Castle中使用任何具有SkippingCipher接口的东西解密GCM AES流吗?

  •  1
  • Roman Nikitchenko  · 技术社区  · 7 年前

    CipherInputStream 类)。我知道GCM模式可以被视为CTR模式,所以若我不需要身份验证,我应该能够从随机位置解密流(若我知道位置),但什么呢 mode 我可以使用密码来解密AES/GCM流并 SkippingCipher

    任何相关的代码示例都会更好。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Roman Nikitchenko    7 年前

    import

    方法 createGcmStreamDecryptor() GCMUtil

    // AES block size in bytes.
    private static final int AES_BLOCK_SIZE = 16;
    
    // Default (recommended) GCM IV size.
    private static final int GCM_DEFAULT_IV_SIZE = 12;
    
    
    // Perform 'inc32' operation on CTR counter.
    private static byte inc32(byte[] counter) {
        for (int i = counter.length - 1; i >= 0; i--) {
            if (++counter[i] != 0) {
                return 0;
            }
        }
        return 1;
    }
    
    // Get GCM gHASH function result.
    private static void gHASHPartial(
            final GCMMultiplier multiplier, byte[] Y, byte[] b, int off, int len) {
        GCMUtil.xor(Y, b, off, len);
        multiplier.multiplyH(Y);
    }
    
    // Get GCM gHASH function result.
    private static void gHASHBlock(
            final GCMMultiplier multiplier, byte[] Y, byte[] b) {
        GCMUtil.xor(Y, b);
        multiplier.multiplyH(Y);
    }
    
    // Get GCM gHASH function result.
    private static void gHASH(
            final GCMMultiplier multiplier, byte[] Y, byte[] b, int len) {
        for (int pos = 0; pos < len; pos += AES_BLOCK_SIZE)
        {
            final int num = Math.min(len - pos, AES_BLOCK_SIZE);
            gHASHPartial(multiplier, Y, b, pos, num);
        }
    }
    
    // Convert GCM initialization vector into appropriate CTR one
    // so our CTR-based 'GCM decryptor' works.
    // This is based on Bouncy Castle GCM block cipher implementation
    // in accordance with NIST 800-38D Nov 2007 document.
    private static byte[] createGcmStreamDecryptorIv(
            final AESEngine aes,
            byte[] gcmIv) {
    
        final byte [] J0 = new byte[AES_BLOCK_SIZE];
        if (gcmIv.length == GCM_DEFAULT_IV_SIZE) {
    
            // In case of 12 bytes IV ieverything is simple.
            System.arraycopy(gcmIv, 0, J0, 0, gcmIv.length);
            J0[AES_BLOCK_SIZE - 1] = 0x01;
    
        } else {
    
            // For other sizes it is much more complex.
    
            // We need to init GCM multiplier based on given
            // (already initialized) AES cipher.
            // Pay attention GCMMultiplier tables don't change
            // unless the key changes.
            final byte [] H = new byte[AES_BLOCK_SIZE];
            aes.processBlock(H, 0, H, 0);
    
            final GCMMultiplier multiplier = new Tables4kGCMMultiplier();
            multiplier.init(H);
    
            final byte [] nonce = new byte[AES_BLOCK_SIZE];
            System.arraycopy(gcmIv, 0, nonce, 0, gcmIv.length);
    
            gHASH(multiplier, J0, nonce, nonce.length);
            final byte[] X = new byte[AES_BLOCK_SIZE];
            Pack.longToBigEndian((long)gcmIv.length * 8, X, 8);
            gHASHBlock(multiplier, J0, X);
        }
        inc32(J0);
        return J0;
    }
    
    /**
     * Create streaming block cipher to decrypt AES/GCM data.
     * Actually we are taking parameters of AES/GCM encryption
     * and construct CTR (SIC) cipher with converted IV to get stream
     * skipping ability.
     * @param key Decrypted file encryption key.
     * @param iv GCM cipher initialization vector.
     * @return Streaming (actually AES/CTR) cipher to decrypt file stream 
     */
    public static StreamBlockCipher createGcmStreamDecryptor(
            final SecretKey key,
            final byte[] iv) {
    
        try {
            // AES cipher is required both as basis for SIC/CTR cipher
            // and for IV conversion.
            final AESEngine aes = new AESEngine();
            aes.init(true, new KeyParameter(key.getEncoded()));
    
            // We convert GCM IV into appropriate CTR IV.
            byte[] ctrIv = createGcmStreamDecryptorIv(aes, iv);
    
            // Now resulting SIC cipher can be created and initialized.
            StreamBlockCipher c = new SICBlockCipher(aes);
            c.init(false, new ParametersWithIV(null, ctrIv));
            return c;
    
        } catch (final Exception e) {
            throw new RuntimeException(e);
        }
    }