1

我目前正在使用 Java TLS 服务器。(我几天前在这里发布了关于 KeyExchange 签名的信息)

我现在正在尝试解密使用 AES-GCM 编码的 TLS 消息。服务器已经处理了 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.

我迷失了发现我做错了什么来解密这条消息。“标签不匹配”错误对找出问题没有太大帮助,所以我希望这里有人可以帮助我:)

谢谢你,祝你有美好的一天 !

编辑 1: 我在 contentVersion 中放了一个短字节而不是一个字节,但是结果仍然相同。我只收到标签不匹配错误...

编辑2: 正如@dave_thompson_085 所建议的那样doFinal,现在调用(ciphered, 8, ciphered.length-8)而不是(ciphered, 0, ciphered.length)排除explicit_nonce。

我还仔细检查了 aad 和 nonce 值。

nonce 的explicit_nonce 部分对应于我在Wireshark 中看到的数据包。我现在担心 client_write_IV 没有正确生成:|

至于 aad,我发现有点奇怪:长度为 0。

我从客户端收到的消息是 40 字节长,explicit_nonce 部分减去 8,减去 SHA-256 的 MAC 长度 32(我正在尝试让 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 只需向我发送协议版本警报……</p>

4

0 回答 0