JEP 249:TLS 的 OCSP 装订
概述
成功指标
该实现(客户端模式和服务器模式)必须能够与至少两个支持 OCSP 捆绑的第三方 TLS 实现成功互操作。
动机
检查 X.509 证书的吊销状态是基于有效证书的身份验证的关键部分。但是,使用 OCSP 进行证书状态检查通常涉及针对每个被检查证书的网络请求。由于这些额外的网络请求,在客户端为 TLS 启用 OCSP 检查可能会对性能产生显著影响。
OCSP 装订允许证书的提供者(而非签发证书的证书颁发机构 (CA))承担提供 OCSP 响应的资源成本。在 TLS 环境中,TLS 服务器负责请求 OCSP 响应,并在 SSL/TLS 握手期间将其发送给客户端。这还允许服务器缓存 OCSP 响应,并将它们提供给所有正在连接的客户端。由于响应可以由服务器进行缓存并定期刷新,而不需要每个客户端单独处理,因此这大大降低了 OCSP 响应器的负载。
目前,可以在客户端启用证书吊销状态检查。然而,这种经典方法面临几个挑战:
性能
如果客户端直接从 OCSP 响应器获取吊销状态,那么对于每个连接到特定服务器的客户端,OCSP 响应器都必须返回一个特定的证书状态。对于高流量的网站来说,OCSP 响应器很可能会成为性能瓶颈。此外,吊销状态检查涉及多次往返通信。如果在客户端启用了 OCSP 检查,则会对性能产生显著影响。
安全性
亚当·兰利在他的其中一篇博客中谈到了客户端应用程序在传统 OCSP 面临的安全挑战。他描述了大多数浏览器所实施的“软失败”行为,这是一种即使无法联系 OCSP 响应程序也不会导致吊销检查失败的策略。这使得攻击者能够通过拦截或阻止来自客户端的 OCSP 请求,或者通过对响应程序本身发起拒绝服务攻击,来让客户端绕过吊销检查。
OCSP 装订本身并不能完全缓解这一挑战,但它消除了客户端与响应程序之间进行 OCSP 检查的必要性。服务器仍然必须能够获取自己的 OCSP 响应,并将其放入 TLS 握手的带内部分。
目前在 IETF 中以草案形式存在的一项提案是 "must-staple" 证书扩展,该提案要求在 TLS 握手期间附加 OCSP 响应。这将基本上覆盖客户端可能采用的任何软失败行为。这一拟议的扩展超出了本 JEP 的范围,但如果该扩展从草案阶段取得进展,未来可能会成为一个有用的补充。
最后,Java 客户端对软失败默认值的需求可能与浏览器不同。在某些情况下,客户端可能更倾向于硬失败方法,或者选择在无法接收 OCSP 响应时通过对话框获取用户反馈。例如,Java 插件和 Java Web Start 的默认软失败方法是在加载已签名的小程序时显示警告对话框。
OCSP 请求可能存在的隐私问题
在常规的 OCSP 场景中,当客户端发送 OCSP 请求时,它会通过服务器证书条目暴露服务器,并至少通过 IP 地址暴露自身,从而可能泄露客户端的行为。而 OCSP 装订解决了这个问题,因为客户端不再向 OCSP 响应器发起请求。
“ captive portal ” 技术的局限性
“强制门户”技术迫使网络上的 HTTP 客户端下载一个特殊的网页,通常用于认证目的,然后才能正常使用网络。在这样的环境中,客户端无法检查 SSL/TLS 证书的 OCSP 状态,因为所有网络访问都会被阻塞,直到认证成功为止。
概述
上述问题可以通过使用 CRL(证书吊销列表)来部分缓解,或者更好地通过 OCSP(在线证书状态协议) stapling 来解决。
总结来说,OCSP 装订可以通过减少 OCSP 响应程序的性能瓶颈来帮助提升 TLS 的性能。它还可以防止 OCSP 请求潜在的隐私损害,并避免“强制门户”技术的限制。
描述
此功能将在 SunJSSE
提供程序实现中实施。计划进行一些小的 API 更改,目标是尽可能将这些更改保持在最小范围。实现将为 OCSP 特定参数选择合理的默认值,并通过以下系统属性提供这些默认值的配置:
jdk.tls.client.enableStatusRequestExtension
:此属性默认为 true。它启用status_request
和status_request_v2
扩展,并启用对服务器发送的CertificateStatus
消息的处理。jdk.tls.server.enableStatusRequestExtension
:此属性默认为 false。它启用服务器端对 OCSP 装订的支持。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 的证书没有 Authority Info Access 扩展的情况下设置默认 URI。除非设置了 jdk.tls.stapling.responderOverride 属性(见下文),否则它不会覆盖 AIA 扩展值。此属性默认未设置。jdk.tls.stapling.responderOverride
:此属性允许通过 jdk.tls.stapling.responderURI 属性提供的 URI 覆盖任何 AIA 扩展值。默认为 false。jdk.tls.stapling.ignoreExtensions
:此属性禁用转发在status_request
或status_request_v2
TLS 扩展中指定的 OCSP 扩展。默认为 false。
客户端和服务器端的 Java 实现将能够支持 status_request
和 status_request_v2
TLS 握手扩展。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_request
和status_request_v2
两个扩展。对于status_request_v2
扩展,将同时声明ocsp
和ocsp_multi
类型。 -
创建这些 hello 扩展需要在
sun.security.ssl
中创建新的类,类似于ServerNameIndicator
、RenegotiationInfoExtension
和其他扩展的实现方式。 -
为了使用这些新扩展,
ClientHello
类将定义额外的方法以添加这些扩展。这些方法将从ClientHandshaker.clientHello()
调用。 -
需要在
HandshakeMessage
类中创建一个新的握手消息类,用于处理CertificateStatus
消息的编码和解析。 -
在
ExtendedSSLSession
中需要进行一项公共 API 的更改,以便调用者能够获取在握手过程中接收到的 OCSP 响应。新方法如下:public List<byte[]> getStatusResponses();
服务器端
-
服务端实现默认将禁用 OCSP 装订,但可以通过系统属性
jdk.tls.server.enableStatusRequestExtension
启用。禁用 OCSP 装订支持的服务器将忽略status_request
和status_request_v2
扩展。 -
服务端在
ServerHello
消息中填充status_request
或status_request_v2
信息的方式取决于客户端如何声明这些扩展。通常情况下,ClientHello
中的相同请求扩展会原样返回到ServerHello
中,但以下情况例外:-
如果服务器在
ClientHello
中同时收到status_request
和status_request_v2
扩展,则会在ServerHello
中声明status_request_v2
。 -
如果服务器在
ClientHello
中收到包含ocsp
和ocsp_multi
类型的status_request_v2
扩展,则会在ServerHello
消息中声明status_request_v2
,并在CertificateStatus
消息中声明ocsp_multi
。 -
如果选择了
status_request_v2
/ocsp_multi
,则会使用不同的线程来获取每个响应。这将由StatusResponseManager
管理,它负责处理 OCSP 响应的获取和缓存。
-
-
应尽可能缓存 OCSP 响应。未在其
status_request[_v2]
扩展中指定随机数的客户端可能会收到缓存的响应。-
如果当前时间晚于
nextUpdate
字段,则不应使用缓存的响应。 -
没有
nextUpdate
字段的缓存响应可以在缓存中保留一段预定义的生命周期(参见下文的可调参数)。 -
接收到带有随机数扩展的
status_requests
的服务器不得在CertificateStatus
消息中返回缓存的响应。
-
-
服务端的装订支持可以通过上述系统属性进行调整。
-
StatusResponseManager
是作为SSLContext
实例化的一部分创建的。在SSLContext
构造期间会采样这些属性值。这些属性值可以更改,当创建新的SSLContext
对象时,StatusResponseManager
将使用这些新值。
装订和 X509ExtendedTrustManagers
开发人员在处理通过 OCSP 捆绑提供的响应时有一定的灵活性。此 JEP 不会对当前涉及证书路径检查和吊销检查的方法做出任何更改。这意味着可以同时让客户端和服务器声明 status_request
扩展,通过 CertificateStatus
消息获取 OCSP 响应,并让用户灵活决定如何对吊销信息(或缺乏吊销信息)作出反应。
与之前的 JDK 发行版本一样,如果调用者未提供 PKIXBuilderParameters
,则吊销检查会被禁用。如果调用者创建了一个 PKIXBuilderParameters
并使用 setRevocationEnabled
方法启用了吊销检查,那么将会评估 OCSP 快速响应。同样地,如果将 com.sun.net.ssl.checkRevocation
属性设置为 true
也会进行评估。下表展示了几个不同的方法作为示例(假设客户端和服务器都启用了 OCSP 快速响应):
PKIX 构建参数
checkRevocation 属性
PKIX 撤销检查器
结果
默认
默认
默认
吊销检查已禁用
默认
正确
默认
启用吊销检查*,设置为 SOFT_FAIL
实例化
默认
默认
启用了吊销检查*,设置了 SOFT_FAIL
实例化
默认
实例化后,添加到 PKIXBuilderParameters 中
启用吊销检查*,硬失败行为。
* 仅当 ocsp.enable
安全属性被设置为 true 时,才会发生客户端 OCSP 回退。
有关 PKIXBuilderParameters
和 PKIXRevocationChecker
对象的配置及其与 JSSE 的关系的更多详细信息,请参阅《Java PKI API 程序员指南》和《JSSE 参考指南》。
测试
-
OCSP 装订的实现不得破坏向后兼容性。
-
客户端实现必须能够向支持的服务器发送符合 RFC 6066 标准的
status_request
ClientHello
扩展。它必须能够正确解析ServerHello
握手消息中的相同扩展,并正确解析随后的CertificateStatus
握手消息内容。 -
客户端实现必须能够向支持的服务器发送符合 RFC 6961 标准的
status_request_v2
ClientHello
扩展。它必须能够在该扩展中声明ocsp
或ocsp_multi
类型(或两者)。它还必须能够正确解析ServerHello
握手消息中的相同扩展,并正确解析随后的CertificateStatus
握手消息内容。 -
服务器实现必须能够接收
ClientHello
握手消息中的status_request
和status_request_v2
扩展,并查询相应的 OCSP 响应器。它必须能够将 OCSP 响应放入CertificateStatus
TLS 握手消息中,以返回给客户端。 -
服务器实现必须能够缓存有效的 OCSP 响应,以便与那些未在
status_request[_v2]
扩展中使用随机数扩展的客户端重复使用。 -
客户端必须能够与至少两个支持 OCSP 装订的不同 Web 服务器(例如 Apache 2.4+)进行互操作。
-
服务器必须能够与至少两个支持声明
status_request
或status_request_v2
的客户端实现进行互操作。目前,大多数主流浏览器(如 Firefox、Chrome 等)都可以生成status_request
扩展,其他工具如 OpenSSL 的s_client
也可以。为了自动化测试的目的,可以创建一些小型应用程序,这些程序链接到 NSS 和 OpenSSL 库,以建立带有 OCSP 装订功能的 TLS 连接。
风险与假设
在此实现中,JDK 中的 Java 客户端将默认启用 OCSP 装订功能。然而,对于无法接受 status_request
或 status_request_v2
TLS 扩展的 TLS 服务器,可能存在潜在的互操作性问题。如果需要,已定义了一个系统或安全属性来禁用 OCSP 装订。