Java TLS 1.2服务器:AES-GCM解密

时间:2016-07-18 12:35:02

标签: java aes tls1.2 aes-gcm

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

我现在正试图解密使用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 Section 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中放了一个short而不是一个字节,但结果仍然相同。我只得到Tag Mismatch错误......

编辑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字节长,减去8为explicit_nonce部分,减去MAC长度,对于SHA-256是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客户端时我再次得到Tag Mismatch。 Firefox& Safari只发给我protocol_version警报......

0 个答案:

没有答案