解密 TLS 1.2 AES-GCM 数据包
decrypt TLS 1.2 AES-GCM packet
我正在开发一个 Java 程序来解密使用 TLS_RSA_WITH_AES_128_GCM_SHA256
密码的 TLS 1.2 会话。我使用 wireshark 录制了一个测试会话。 Master Secret 已知。
No. Time Protocol Length Info
4 0.000124000 TLSv1.2 166 Client Hello
6 0.000202000 TLSv1.2 1074 Server Hello, Certificate, Server Hello Done
8 0.001071000 TLSv1.2 393 Client Key Exchange, Change Cipher Spec, Finished
9 0.003714000 TLSv1.2 301 New Session Ticket, Change Cipher Spec, Finished
11 6.443056000 TLSv1.2 116 Application Data
12 6.443245000 TLSv1.2 765 Application Data
15 6.443390000 TLSv1.2 103 Alert (Level: Warning, Description: Close Notify)
Packet 11
包含我正在尝试解密的 HTTP GET Request
。
握手数据:
Cipher: TLS_RSA_WITH_AES_128_GCM_SHA256
Client Random: 375f5632ba9075b88dd83eeeed4adb427d4011298efb79fb2bf78f4a4b7d9d95
Server Random: 5a1b3957e3bd1644e7083e25c64f137ed2803b680e43395a82e5b302b64ba763
Master Secret: 2FB179AB70CD4CA2C1285B4B1E294F8F44B7E8DA26B62D00EE35181575EAB04C
4FA11C0DA3ABABB4AF8D09ACB4CCC3CD
数据包 11 数据:
Direction is Client -> Server.
Secure Sockets Layer
TLSv1.2 Record Layer: Application Data Protocol: Application Data
Content Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 45
Encrypted Application Data: c91de005e2ae50a8a57abee55c183667b136343feef4a387cb7cf83030a47e230af268378c4f33c8b5bab3d26d
到目前为止我做了什么:
密钥派生:
我这里只需要客户端密钥,因为我想解密一个客户端->服务器包。我根据 RFC 扩展了服务器和客户端密钥和 IV。
Client Write Key: 4B119DFBFC930ABE130030BD53C3BF78
Client Write IV: 2029CAE2
随机数:
我从盐(=客户端写入 IV)和显式随机数(=加密数据的前 8 字节)创建 AES-GCM 随机数。
Salt: 2029CAE2
explicitNonce: C91DE005E2AE50A8
Nonce: 2029CAE2C91DE005E2AE50A8
附加身份验证数据 (AAD):
这显然是我卡住了的地方。 RFC5246 说:
additional_data = seq_num + TLSCompressed.type +
TLSCompressed.version + TLSCompressed.length;
where "+" denotes concatenation.
所以我做了这个:
byte[] aad = {0, 0, 0, 0, 0, 0, 0, 1, // seq_no uint64
0x17, // type 0x17 = Application Data
0x03, 0x03, // TLS Version 1.2
0, 45}; // 45 Bytes of encrypted data
我认为 seq_no
是 1。当发送 Change Cipher Spec
记录时,它会重置为零。 (Packet #8
) 那么加密的Finished
记录有seq_no = 0
。下一个客户端数据包是我们的 Packet #11
和 seq_no = 1
.
代码:
现在我将所有内容都输入 BouncyCastle:
AEADParameters parameters = new AEADParameters(new KeyParameter(clientWriteKey), 128, nonce, aad);
GCMBlockCipher gcmBlockCipher = new GCMBlockCipher(new AESFastEngine());
gcmBlockCipher.init(false, parameters);
byte[] plainText = new byte[gcmBlockCipher.getOutputSize(cipherText.length)];
try {
int decLen = gcmBlockCipher.processBytes(cipherText, 0, cipherText.length, plainText, 0);
decLen += gcmBlockCipher.doFinal(plainText, decLen);
} catch (InvalidCipherTextException e) {
System.out.println("MAC failed: " + e.getMessage());
}
这总是抛出 MAC 失败:mac 签入 GCM 失败。 但是解密输出是正确的:
byte[] decomp = decompress(plainText);
System.out.println(new String(decomp, "UTF-8"));
这会打印出 GET / HTTP/1.0\n
.
解压助手:
public static byte[] decompress(byte[] data) throws IOException, DataFormatException {
Inflater inflater = new Inflater(true);
inflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (inflater.getRemaining() > 0) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
inflater.end();
return output;
}
结论:
由于解密的输出是正确的,我可以放心地假设密钥推导和解密工作正常。只有身份验证失败。所以我想也许我对附加身份验证数据 (AAD) 做错了什么。
所以这个问题归结为:
如何正确组装附加验证数据 (AAD)?
谢谢!
GCM 模式根据消息、关联数据和 public 随机数计算 MAC,您讲得很好。
我认为你使用了错误的长度,它应该是加密和附加之前的明文长度MAC。尝试 45 - 8(显式随机数)- 16 (MAC) = 21.
我正在开发一个 Java 程序来解密使用 TLS_RSA_WITH_AES_128_GCM_SHA256
密码的 TLS 1.2 会话。我使用 wireshark 录制了一个测试会话。 Master Secret 已知。
No. Time Protocol Length Info
4 0.000124000 TLSv1.2 166 Client Hello
6 0.000202000 TLSv1.2 1074 Server Hello, Certificate, Server Hello Done
8 0.001071000 TLSv1.2 393 Client Key Exchange, Change Cipher Spec, Finished
9 0.003714000 TLSv1.2 301 New Session Ticket, Change Cipher Spec, Finished
11 6.443056000 TLSv1.2 116 Application Data
12 6.443245000 TLSv1.2 765 Application Data
15 6.443390000 TLSv1.2 103 Alert (Level: Warning, Description: Close Notify)
Packet 11
包含我正在尝试解密的 HTTP GET Request
。
握手数据:
Cipher: TLS_RSA_WITH_AES_128_GCM_SHA256
Client Random: 375f5632ba9075b88dd83eeeed4adb427d4011298efb79fb2bf78f4a4b7d9d95
Server Random: 5a1b3957e3bd1644e7083e25c64f137ed2803b680e43395a82e5b302b64ba763
Master Secret: 2FB179AB70CD4CA2C1285B4B1E294F8F44B7E8DA26B62D00EE35181575EAB04C
4FA11C0DA3ABABB4AF8D09ACB4CCC3CD
数据包 11 数据:
Direction is Client -> Server.
Secure Sockets Layer
TLSv1.2 Record Layer: Application Data Protocol: Application Data
Content Type: Application Data (23)
Version: TLS 1.2 (0x0303)
Length: 45
Encrypted Application Data: c91de005e2ae50a8a57abee55c183667b136343feef4a387cb7cf83030a47e230af268378c4f33c8b5bab3d26d
到目前为止我做了什么:
密钥派生:
我这里只需要客户端密钥,因为我想解密一个客户端->服务器包。我根据 RFC 扩展了服务器和客户端密钥和 IV。
Client Write Key: 4B119DFBFC930ABE130030BD53C3BF78
Client Write IV: 2029CAE2
随机数:
我从盐(=客户端写入 IV)和显式随机数(=加密数据的前 8 字节)创建 AES-GCM 随机数。
Salt: 2029CAE2
explicitNonce: C91DE005E2AE50A8
Nonce: 2029CAE2C91DE005E2AE50A8
附加身份验证数据 (AAD):
这显然是我卡住了的地方。 RFC5246 说:
additional_data = seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length; where "+" denotes concatenation.
所以我做了这个:
byte[] aad = {0, 0, 0, 0, 0, 0, 0, 1, // seq_no uint64
0x17, // type 0x17 = Application Data
0x03, 0x03, // TLS Version 1.2
0, 45}; // 45 Bytes of encrypted data
我认为 seq_no
是 1。当发送 Change Cipher Spec
记录时,它会重置为零。 (Packet #8
) 那么加密的Finished
记录有seq_no = 0
。下一个客户端数据包是我们的 Packet #11
和 seq_no = 1
.
代码:
现在我将所有内容都输入 BouncyCastle:
AEADParameters parameters = new AEADParameters(new KeyParameter(clientWriteKey), 128, nonce, aad);
GCMBlockCipher gcmBlockCipher = new GCMBlockCipher(new AESFastEngine());
gcmBlockCipher.init(false, parameters);
byte[] plainText = new byte[gcmBlockCipher.getOutputSize(cipherText.length)];
try {
int decLen = gcmBlockCipher.processBytes(cipherText, 0, cipherText.length, plainText, 0);
decLen += gcmBlockCipher.doFinal(plainText, decLen);
} catch (InvalidCipherTextException e) {
System.out.println("MAC failed: " + e.getMessage());
}
这总是抛出 MAC 失败:mac 签入 GCM 失败。 但是解密输出是正确的:
byte[] decomp = decompress(plainText);
System.out.println(new String(decomp, "UTF-8"));
这会打印出 GET / HTTP/1.0\n
.
解压助手:
public static byte[] decompress(byte[] data) throws IOException, DataFormatException {
Inflater inflater = new Inflater(true);
inflater.setInput(data);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[1024];
while (inflater.getRemaining() > 0) {
int count = inflater.inflate(buffer);
outputStream.write(buffer, 0, count);
}
outputStream.close();
byte[] output = outputStream.toByteArray();
inflater.end();
return output;
}
结论: 由于解密的输出是正确的,我可以放心地假设密钥推导和解密工作正常。只有身份验证失败。所以我想也许我对附加身份验证数据 (AAD) 做错了什么。 所以这个问题归结为:
如何正确组装附加验证数据 (AAD)?
谢谢!
GCM 模式根据消息、关联数据和 public 随机数计算 MAC,您讲得很好。
我认为你使用了错误的长度,它应该是加密和附加之前的明文长度MAC。尝试 45 - 8(显式随机数)- 16 (MAC) = 21.