Skip to content

JEP 249: OCSP Stapling for TLS | TLS 的 OCSP 装订

摘要

通过 TLS 证书状态请求扩展(RFC 6066 的第 8 节)和多证书状态请求扩展(RFC 6961)实现 OCSP stapling。

成功指标

在客户端和服务器模式下,实施必须与至少两个支持 OCSP stapling 的第三方 TLS 实现成功互操作。

动机

检查 X.509 证书的吊销状态是有效基于证书的身份验证的关键部分。然而,使用 OCSP 进行证书状态检查通常涉及每个要检查的证书的网络请求。由于额外的网络请求,将 OCSP 检查启用在客户端上对 TLS 性能会产生重大影响。

OCSP stapling 允许证书的展示者(而不是颁发证书的权威机构)承担提供 OCSP 响应的资源成本。在 TLS 上下文中,TLS 服务器有责任请求 OCSP 响应并在 SSL/TLS 握手期间将其发送给客户端。这也允许服务器缓存 OCSP 响应并将其提供给连接到它的所有客户端。这显著减轻了 OCSP 响应者的负载,因为响应可以被服务器缓存并定期刷新,而不是由每个客户端来刷新。

目前,可以在客户端上启用证书吊销状态检查。然而,这种传统方法面临几个挑战:

性能

如果客户端直接从 OCSP 响应者获取吊销状态,则对于连接到特定服务器的每个客户端,OCSP 响应者必须回复一个特定的证书状态。对于高流量的网站,OCSP 响应者很可能成为性能瓶颈。此外,吊销状态检查需要多次往返。如果在客户端上启用 OCSP 检查,将会有显著的性能影响。

安全性

亚当·兰利在 他的博客 中谈到了客户端应用程序在传统 OCSP 中面临的安全挑战。他描述了大多数浏览器实施的 “软故障” 行为,即无法联系 OCSP 响应者不会导致验证失败。这使得攻击者可以通过拦截或阻止来自客户端的 OCSP 请求,或对响应者本身发动拒绝服务攻击,使客户端绕过吊销检查。

单独使用 OCSP stapling 并不能完全解决这个问题,但它消除了客户端和响应者之间进行 OCSP 检查的需求。服务器仍然需要能够获取自己的 OCSP 响应并将其置于 TLS 握手的一部分中。

IETF 目前正在起草一项关于 “必须印章” 证书扩展的提案,该扩展要求在 TLS 握手期间附加 OCSP 响应。这实质上会覆盖客户端可能使用的任何软故障行为。这个提议的扩展超出了本 JEP 的范围,但如果这个扩展超出了起草,它未来可能是一个有用的补充。

最后,Java 客户端可能没有与浏览器相同的软故障默认需求。在某些情况下,客户端可能更喜欢采用硬故障方法,或者选择在无法收到 OCSP 响应时通过对话框向用户提供反馈。例如,Java 插件和 Java Web Start 的默认软故障方法是在加载签名的小程序时显示警告对话框。

OCSP 请求潜在的隐私损害

在正常的 OCSP 场景中,当客户端发送 OCSP 请求时,它会将服务器(通过服务器证书条目)和自身(至少通过 IP 地址)暴露给 OCSP 响应器,从而可能泄露客户端行为。OCSP stapling 解决了这个问题,因为客户端不再向 OCSP 响应器发出请求。

“蜗牛洞” 技术的局限性

“蜗牛洞” 技术会强制在网络上使用之前,HTTP 客户端下载一个特殊的网页,通常用于身份验证。在这种环境中,客户端无法检查 SSL/TLS 证书的 OCSP 状态,因为在成功进行身份验证之前,所有网络访问都被阻塞。

总结

通过使用 CRL 可以部分缓解上述问题,或者通过 OCSP stapling 更好地解决这些问题。

总之,通过减少 OCSP 响应者的性能瓶颈,OCSP stapling 可以帮助提高 TLS 的性能。它还可以防止 OCSP 请求潜在的隐私损害,并避免“蜗牛洞”技术的限制。

描述

此功能将在SunJSSE提供程序实现中实施。计划进行轻微的 API 更改,目标是尽量保持这些更改的最小化。实现将选择合理的默认值用于 OCSP 特定参数,并通过以下系统属性提供这些默认值的配置:

  • jdk.tls.client.enableStatusRequestExtension:默认情况下为 true。这启用 status_requeststatus_request_v2 扩展,并启用对服务器发送的 CertificateStatus 消息的处理。
  • jdk.tls.server.enableStatusRequestExtension:默认情况下为 false。这启用 OCSP stapling 的服务器端支持。
  • jdk.tls.stapling.responseTimeout:此属性控制服务器获取 OCSP 响应的最长时间,无论是从缓存中获取还是与 OCSP 响应者联系。已接收的响应将在 CertificateStatus 消息中发送,如果适用于正在执行的印章类型。该属性以毫秒为单位,取默认值为 5000。
  • jdk.tls.stapling.cacheSize:此属性控制缓存中的最大条目数。默认值为 256 个对象。如果缓存已满,并且需要缓存新响应,则最近未使用的缓存条目将被新条目替换。此属性的值小于或等于零意味着缓存不会对其所包含的响应数目设置上限。
  • jdk.tls.stapling.cacheLifetime:此属性控制缓存响应的最长生命周期。该值以秒为单位,并且默认值为 3600(1 小时)。如果响应具有比缓存生命周期更短的 nextUpdate 字段的到期时间,则响应的实际生命周期可能比此属性指定的值短。该属性的值小于或等于零将禁用缓存生命周期。如果对象没有 nextUpdate 并且已禁用缓存生命周期,则不会缓存响应。
  • jdk.tls.stapling.responderURI:此属性允许管理员在 TLS 使用的证书没有权威信息访问扩展的情况下设置默认 URI。除非设置了 jdk.tls.stapling.responderOverride 属性(参见下文),否则它不会覆盖 AIA 扩展值。此属性默认未设置。
  • jdk.tls.stapling.responderOverride:此属性允许通过 jdk.tls.stapling.responderURI 属性提供的 URI 覆盖任何 AIA 扩展值。默认值为 false
  • jdk.tls.stapling.ignoreExtensions:此属性禁用在 status_requeststatus_request_v2 TLS 扩展中指定的 OCSP 扩展的转发。默认值为 false

客户端和服务器端 Java 实现将能够支持 status_requeststatus_request_v2 TLS hello 扩展。status_request 扩展在 RFC 6066 中进行了描述。支持此扩展的服务器将在新的 TLS 握手消息(CertificateStatus)中包含一个证书的单个 OCSP 响应,该证书用于标识服务器。status_request_v2 扩展在 RFC 6961 中进行了描述。该扩展允许客户端请求服务器在 CertificateStatus 消息中提供单个 OCSP 响应(类似于 status_request)或请求服务器获取证书消息中提供的证书列表中每个证书的 OCSP 响应(下面引用为 ocsp_multi 类型)。

客户端端

  • OCSP stapling 将默认启用,并且可以通过设置系统属性来禁用。可以通过 jdk.tls.client.enableStatusRequestExtension 属性来完成此操作。

  • 默认情况下,客户端将在 ClientHello 握手消息中断言 status_requeststatus_request_v2 扩展。对于 status_request_v2 扩展,将同时断言 ocspocsp_multi 类型。

  • 创建 hello 扩展将需要在 sun.security.ssl 中创建新的类,类似于如何实现 ServerNameIndicatorRenegotiationInfoExtension 和其他扩展。

  • 为了使用新的扩展,ClientHello 类将定义额外的方法来添加这些扩展。这些方法将从 ClientHandshaker.clientHello() 中调用。

  • 需要在 HandshakeMessage 类中创建一个新的握手消息类来处理 CertificateStatus 消息的编码和解析。

  • ExtendedSSLSession 中的公共 API 更改是必要的,允许调用方在握手过程中获取到 OCSP 响应。新的方法是:

    java
    public List<byte[]> getStatusResponses();

服务器端

  • 默认情况下,服务器端实现将禁用 OCSP stapling,但可以通过 jdk.tls.server.enableStatusRequestExtension 系统属性启用。禁用 OCSP stapling 支持的服务器将忽略 status_requeststatus_request_v2 扩展。

  • 服务器端根据客户端对这些扩展的声明来填充 ServerHello 消息中的 status_requeststatus_request_v2 信息。一般而言,ClientHello 中的相同请求扩展将在 ServerHello 中返回,但有以下几个例外情况:

    • ClientHello 中同时收到 status_requeststatus_request_v2 扩展的服务器将在 ServerHello 中声明 status_request_v2

    • ClientHello 中收到 status_request_v2 扩展,并且包含 ocspocsp_multi 类型的服务器将在 ServerHello 消息中声明 status_request_v2,并在 CertificateStatus 消息中声明 ocsp_multi

    • 如果选择了 status_request_v2/ocsp_multi,将使用不同的线程获取每个响应。这将由 StatusResponseManager 进行管理,它将处理 OCSP 响应的获取和缓存。

  • 尽可能缓存 OCSP 响应。如果客户端的 status_request[_v2] 扩展中没有指定 nonce,则可能会收到缓存的响应。

    • 如果当前时间晚于 nextUpdate 字段,则不应使用缓存的响应。

    • 没有 nextUpdate 字段的缓存响应可以在缓存中保留一段预定的生命周期(见下文的 Tunables)。

    • 收到带有 nonce 扩展的 status_requests 的服务器不得在 CertificateStatus 消息中返回缓存的响应。

  • 通过上述系统属性可以调节服务器端的 stapling 支持。

  • StatusResponseManager 是在 SSLContext 实例化时创建的。属性值在 SSLContext 构造过程中进行采样。这些属性值可以更改,并且当创建新的 SSLContext 对象时,StatusResponseManager 将具有这些新值。

Stapling 和 X509ExtendedTrustManagers

开发人员在处理由 OCSP stapling 提供的响应方面有一定的灵活性。此 JEP 对证书路径检查和吊销检查涉及的当前方法论没有进行任何更改。这意味着可以同时使客户端和服务器断言 status_request 扩展,并通过 CertificateStatus 消息获取 OCSP 响应,并允许用户在处理吊销信息或缺乏吊销信息时具有灵活性。

与之前的 JDK 版本一样,如果调用方没有提供 PKIXBuilderParameters,则禁用吊销检查。如果调用方创建了 PKIXBuilderParameters 并使用 setRevocationEnabled 方法启用吊销检查,则将评估 OCSP 响应的 stapling。如果将 com.sun.net.ssl.checkRevocation 属性设置为 true,也会是如此。下表显示了一些不同的方法作为示例(假设客户端和服务器都启用了 OCSP stapling):

PKIXBuilderParameterscheckRevocation PropertyPKIXRevocationCheckerResult
defaultdefaultdefault吊销检查被禁用
defaulttruedefault吊销检查启用*,设置为 SOFT_FAIL
instantiateddefaultdefault吊销检查启用*,设置为 SOFT_FAIL
instantiateddefaultinstantiated, added to PKIXBuilderParameters吊销检查启用*,硬失败行为。

* 仅当 ocsp.enable 安全属性设置为 true 时,客户端的 OCSP 回退才会发生。

有关 PKIXBuilderParametersPKIXRevocationChecker 对象的配置及其与 JSSE 的关系的更多详细信息,请参阅“Java PKI API 程序员指南”和“JSSE 参考指南”。

测试

  1. OCSP Stapling 实现不能破坏向后兼容性。

  2. 客户端实现必须能够向支持的服务器发送 RFC 6066 风格的 status_request ClientHello 扩展。它必须能够正确解析 ServerHello 握手消息中的相同 hello 扩展,并正确解析随后的 CertificateStatus 握手消息内容。

  3. 客户端实现必须能够向支持的服务器发送 RFC 6961 风格的 status_request_v2 ClientHello 扩展。它必须能够在 hello 扩展中断言 ocspocsp_multi 类型(或两者都断言)。它必须能够正确解析 ServerHello 握手消息中的相同 hello 扩展,并正确解析随后的 CertificateStatus 握手消息内容。

  4. 服务器端实现必须能够接收 ClientHello 握手消息中的 status_requeststatus_request_v2 扩展,并查询相应的 OCSP 响应器。它必须能够将 OCSP 响应放置在 CertificateStatus TLS 握手消息中以返回给客户端。

  5. 服务器端实现必须能够缓存有效的 OCSP 响应,以便与不在其 status_request[_v2] hello 扩展中使用 nonce 发出请求的客户端重复使用。

  6. 客户端必须能够与至少两个能够执行 OCSP stapling 的不同 Web 服务器进行互操作(例如 Apache 2.4+)。

  7. 服务器必须能够与至少两个能够断言 status_requeststatus_request_v2 的不同客户端实现进行互操作。此时,大多数主流浏览器(如 Firefox、Chrome 等)可以生成 status_request hello 扩展,其他工具如 OpenSSL 的 s_client 也可以。为了进行自动化测试,可以创建使用 NSS 和 OpenSSL 库链接的小型应用程序,以与支持 OCSP stapling 的 TLS 连接。

风险和假设

此实现将使 Java 客户端默认情况下启用 OCSP stapling。然而,对于不能接受 status_requeststatus_request_v2 TLS 扩展的 TLS 服务器,可能存在互操作性问题。如果需要,可以定义系统或安全属性来禁用 OCSP stapling。