【发布时间】:2019-03-18 19:03:39
【问题描述】:
我正在查看 QUIC 传输协议(transport 和 TLS)的最新 Internet 草案,并想知道如何在 Java(或其他 JVM 语言)中实现它,假设我不想重新实现同时支持 TLS 1.3。
TLS 通常基于 TCP(或其他具有类似服务的协议),TLS 本身有两层:
+--------------+--------------+--------------+
| Handshake | Alerts | Application |
| Layer | | Data |
| | | |
+--------------+--------------+--------------+
| |
| Record Layer |
| |
+--------------------------------------------+
| |
| TCP |
| |
+--------------------------------------------+
图改编自internet draft, section 2.1
在 Java 中,我们可以使用 javax.net.ssl 中的类来实现这一点,或者使用 SSLEngine 仅用于没有 I/O 的 TLS(应用程序或框架需要插入网络,例如使用 NIO) ,或SSLSocket(或 SSLServerSocket),用于通过通常的 InputStream/OutputStream 阻塞 I/O 进行 TCP 上的 TLS。
QUIC 仅使用 TLS 1.3 的握手部分来协商会话密钥,同时使用其自己的数据包格式和加密(“数据包保护”)而不是 TLS 的记录层(以及整个事情是基于 UDP,而不是 TCP):
+--------------+--------------+ +-------------+
| TLS | TLS | | QUIC |
| Handshake | Alerts | | Applications|
| | | | (h2q, etc.) |
+--------------+--------------+-+-------------+
| |
| QUIC Transport |
| (streams, reliability, congestion, etc.) |
| |
+---------------------------------------------+
| |
| QUIC Packet Protection |
| |
+---------------------------------------------+
| |
| UDP |
| |
+---------------------------------------------+
所以现在我的问题是:给定的 TLS 实现(从 Java 11 开始,我们包含 TLS 1.3)只有 TLS in a box,即一起记录 + 握手 + 警报,而不是 握手只有 版本。 SSLEngine 似乎是最接近的,但它仍然只有两个 wrap 和 unwrap 方法,它们创建/读取密文和读取/生成纯文本(尽管我可能只进行握手而不传输任何实际数据)。
有没有简单的方法可以去掉记录层?或者我可以使用其他实现方式吗?
我还需要从中获取实际的密钥(或者更确切地说,是主密钥)。
【问题讨论】:
-
请注意,握手的身份验证部分是在握手中先前消息的串联之上。据我所知,这并没有改变。所以握手依赖于记录的结构。因此,将其抽象出来并没有多大意义,除非您明确地为这样的变化进行设计。这使得问题的答案很可能是“否”。
-
@MaartenBodewes 那么 QUIC 应该如何工作?
-
如果我正确阅读了草稿,握手和以前一样,只是之后 QUIC 协议接管了。但你是对的,你应该能够获取生成的会话密钥来完成这项工作。可能使用
SSLSession.getValue()调用有效。诀窍是知道获取密钥的密钥:P 也许您可以通过注册SSLSessionBindingListener或通过在 Java 中启用 TLS 的调试输出来找到它们。 (只是在这里思考)。 -
@MaartenBodewes 再次阅读 TLS RFC,transcript hash is over handshake "messages", which is different than "records" (one layer above)。这样 QUIC 就可以只接收这些消息并将它们嵌入到自己的协议中,而无需记录头。