Могу ли я расшифровать поток GCM AES в Bouncy Castle с помощью чего-либо, имеющего интерфейс SkippingCipher? - PullRequest
0 голосов
/ 29 октября 2018

У меня есть рабочее решение AES GCM с Bouncy Castle (нативный API), предоставляющее интерфейс потока (класс CipherInputStream). Я знаю, что режим GCM можно рассматривать как режим CTR, поэтому, если мне не нужна аутентификация, я смогу расшифровать поток с произвольного места (если я знаю положение), но какой шифр mode я могу использовать, чтобы он мог расшифровать AES / GCM поток и есть SkippingCipher интерфейс?

Любой связанный пример кода будет еще лучше.

1 Ответ

0 голосов
/ 06 ноября 2018

Вот фрагмент кода, который я привел на основе советов и примеров, упомянутых в вопросе и вокруг него. Я не публикую import раздел, но это тривиально. Также я не имею дело с тегом аутентификации данных (последние 16 байтов потока), так как клиент знает длину контента. Да, я знаю, что игнорировать тег - это плохо, но мне нужен как потоковый, так и произвольный доступ. В конце концов, никто не мешает мне использовать другой метод расшифровки, когда мне не нужен произвольный доступ (что на самом деле имеет место).

Метод createGcmStreamDecryptor() (общедоступный) блочный шифр результатов, который на самом деле является шифрованием CTR AES В качестве входных данных он принимает IV, предназначенный для шифра GCM, и соответствующим образом преобразует его. В моем случае длина IV составляет 16 байт, но она будет работать на всем, где работает подход Bouncy Castle. Я использовал БК как можно чаще, включая 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);
    }
}
...