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

我目前正在研究Java TLS服务器。 (几天前我发布了关于KeyExchange签名的post)

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

编辑2:正如@ dave_thompson_085所建议的,现在调用doFinal (ciphered, 8, ciphered.length-8)长度为(ciphered, 8, ciphered.length-8)而不是(ciphered, 0, ciphered.length)(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);之前aad.writeShort(fragment.readableBytes()-8-session.cipherSuite.mac.len);

在与chrome交谈时似乎运行良好(即使在我发送自己的加密消息之后它失败但如果我不能自己修复它将需要另一篇文章)但是当尝试使用open_ssl客户端时我得到一个Tag Mismatch再次。 Firefox和Safari只发给我protocol_version警报……