Сервер Java TLS 1.2: расшифровка AES-GCM

В настоящее время я работаю на сервере TLS Java. (Я разместил здесь несколько дней назад относительно подписи KeyExchange)

Я сейчас пытаюсь расшифровать сообщение TLS, закодированное с помощью AES-GCM. Сервер уже обрабатывает CBC, но, поскольку он уязвим для POODLE, мы хотели бы вместо этого использовать GCM. Я постараюсь объяснить как можно лучше:)

Для этого кода мы используем Java 8u91, Netty 3.9.0. Мы не используем BouncyCastle и не намерены, мы хотели бы придерживаться JDK.

Код!

/**
 * Deciphers the fragment and returns the deciphered version of it
 * 
 * @param fragment
 *            to decrypt
 * @return the decrypted fragment
 * @throws InvalidKeyException
 * @throws InvalidAlgorithmParameterException
 * @throws IllegalBlockSizeException
 * @throws BadPaddingException
 */
    private ChannelBuffer decodeCiphered(ChannelBuffer fragment, short contentType, byte[] version) throws InvalidKeyException,
    InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
    if (readCipher != null) {
        byte[] ciphered = fragment.array();
        if (session.cipherSuite.cipher.type == CipherType.stream) {
            /* ... */
        } else if (session.cipherSuite.cipher.type == CipherType.block) {
            /* ... */
        } else if (session.cipherSuite.cipher.type == CipherType.aead) {
            byte[] nonce = concat(clientWriteIV.getIV(), fragment.copy(0, 8).array());
            // ClientWriteIV + explicit nonce (first 8 bytes of encrypted data)

            readCipher.init(Cipher.DECRYPT_MODE, clientWriteKey,
                new GCMParameterSpec(session.cipherSuite.cipher.block * 8, nonce));

            ChannelBuffer aad = ChannelBuffers.buffer(13);
            //seq_num(8B) + TLSCompressed.type(1B) + TLSCompressed.version(2B) + TLSCompressed.length(2B)

            aad.writeLong(writeSequence.longValue());
            aad.writeByte(contentType);
            aad.writeBytes(version);
            aad.writeShort(fragment.readableBytes() - 8 - 16);
            readCipher.updateAAD(aad.array());
            ciphered = readCipher.doFinal(ciphered, 8, ciphered.length - 8);
            fragment = ChannelBuffers.wrappedBuffer(ciphered);
        }
    return fragment;
}

/**
 * Generates the different needed keys.
 */
private void generateKeys() {
    byte[] keyBlock = null;
    if (session.cipherSuite.cipher.type == CipherType.aead) {
        keyBlock = new byte[2 * session.cipherSuite.cipher.key
                    + 8];

        try {
            prf(keyBlock, session.masterSecret, KEY_EXPANSION_LABEL, concat(serverRandom, clientRandom));
        } catch (GeneralSecurityException e) {
        }
        clientWriteKey = new SecretKeySpec(keyBlock,0 , session.cipherSuite.cipher.key, session.cipherSuite.cipher.kalgo);
        serverWriteKey = new SecretKeySpec(keyBlock, session.cipherSuite.cipher.key, session.cipherSuite.cipher.key, session.cipherSuite.cipher.kalgo);
        clientWriteIV = new IvParameterSpec(keyBlock, 2 * session.cipherSuite.cipher.key, 4);
        serverWriteIV = new IvParameterSpec(keyBlock, 2 * session.cipherSuite.cipher.key + 4, 4);

    } else {
        /* ... 
            For CBC
        */
    }
}

Я постоянно получаю следующую ошибку:

javax.crypto.AEADBadTagException: Tag mismatch!
at com.sun.crypto.provider.GaloisCounterMode.decryptFinal(GaloisCounterMode.java:524)
at com.sun.crypto.provider.CipherCore.finalNoPadding(CipherCore.java:1023)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:960)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2223)
at com.seemy.codec.tls.TlsCodec.decodeCiphered(TlsCodec.java:525)

Обычно это означает, что AAD не прав. Но, насколько я могу судить из RFC 5246, раздел 6.2.3.3, AAD должен быть следующим:

The additional authenticated data, which we denote as additional_data, is defined as follows:

  additional_data = seq_num + TLSCompressed.type +
                    TLSCompressed.version + TLSCompressed.length;
where "+" denotes concatenation.

Я не могу понять, что я делаю неправильно, чтобы расшифровать это сообщение. Ошибка "Tag Mismatch" не очень помогает найти, что не так, поэтому я надеюсь, что кто-то здесь может мне помочь:)

Спасибо и хорошего дня!

Изменить 1: я поместил короткое вместо байта в contentVersion, однако результат остается тем же. Я получаю только ошибки Tag Mismatch...

Редактировать 2: как предложено @dave_thompson_085 doFinal сейчас вызывается (ciphered, 8, ciphered.length-8) вместо (ciphered, 0, ciphered.length) исключить явную_нонце.

Я также проверил значения aad и nonce с большей тщательностью.

Часть visible_nonce одноразового номера соответствует пакету, который я вижу в Wireshark. Теперь я беспокоюсь, что client_write_IV генерируется неправильно:|

Что касается AAD, я обнаружил что-то немного странное: длина равна 0.

Сообщение, которое я получаю от клиента, имеет длину 40 байт, минус 8 для части очевидно_nonce, минус длина MAC, равная 32 для SHA-256(я пытаюсь заставить работать tls_ecdhe_rsa_with_aes_128_gcm_sha256).

Следовательно, если я попробую tls_ecdhe_rsa_with_aes_256_gcm_sha384, длина будет равна -16. Это кажется мне неправильным.

Редактировать 3: снова я сделал так, как предложил @dave_thompson_085, и изменил мой код так, чтобы он имел следующую длину: aad.writeShort(fragment.readableBytes() - 8 - 16); тогда как раньше я aad.writeShort(fragment.readableBytes()-8-session.cipherSuite.mac.len);

Кажется, что это хорошо работает при разговоре с Chrome (даже если после отправки моего собственного зашифрованного сообщения произойдет сбой, но для этого потребуется еще один пост позже, если я сам не смогу это исправить), но при попытке с клиентом open_ssl я получаю несоответствие тега снова. Firefox и Safari просто отправьте мне оповещения protocol_version…

0 ответов

Другие вопросы по тегам