我们有一个使用 CA (Thawte) 证书签名的 Java Web Start 应用程序。该应用程序已分发给数百名客户。他们将其托管在服务器上,并通过互联网或内联网在客户端计算机上运行。现在它工作完美。问题是应用程序的签名没有时间戳。证书过期后,客户会怎样?他们应该能够启动该应用程序吗?如果没有,我们如何帮助他们?将他们的服务器 URL 添加到例外站点列表对他们有帮助吗?
我们尝试更改当地时间来假装证书过期。然后,由于安全原因,应用程序被阻止。将 URL 添加到例外站点列表没有帮助:
java.security.cert.CertPathValidatorException: Response is unreliable: its validity interval is out-of-date
此处的堆栈跟踪:
java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Response is unreliable: its validity interval is out-of-date
at com.sun.deploy.security.RevocationChecker.checkOCSP(Unknown Source)
at com.sun.deploy.security.RevocationChecker.check(Unknown Source)
at com.sun.deploy.security.TrustDecider.checkRevocationStatus(Unknown Source)
at com.sun.deploy.security.TrustDecider.getValidationState(Unknown Source)
at com.sun.deploy.security.TrustDecider.validateChain(Unknown Source)
at com.sun.deploy.security.TrustDecider.isAllPermissionGrantedInt(Unknown Source)
at com.sun.deploy.security.TrustDecider.isAllPermissionGranted(Unknown Source)
at com.sun.javaws.security.AppPolicy.grantUnrestrictedAccess(Unknown Source)
at com.sun.javaws.security.JNLPSignedResourcesHelper.checkSignedResourcesHelper(Unknown Source)
at com.sun.javaws.security.JNLPSignedResourcesHelper.checkSignedResources(Unknown Source)
at com.sun.javaws.Launcher.prepareResources(Unknown Source)
at com.sun.javaws.Launcher.prepareAllResources(Unknown Source)
at com.sun.javaws.Launcher.prepareToLaunch(Unknown Source)
at com.sun.javaws.Launcher.prepareToLaunch(Unknown Source)
at com.sun.javaws.Launcher.launch(Unknown Source)
at com.sun.javaws.Main.launchApp(Unknown Source)
at com.sun.javaws.Main.continueInSecureThread(Unknown Source)
at com.sun.javaws.Main.access$000(Unknown Source)
at com.sun.javaws.Main$1.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Suppressed: com.sun.deploy.security.RevocationChecker$StatusUnknownException
at com.sun.deploy.security.RevocationChecker.checkCRLs(Unknown Source)
... 19 more
Caused by: java.security.cert.CertPathValidatorException: Response is unreliable: its validity interval is out-of-date
at sun.security.provider.certpath.OCSPResponse.verify(Unknown Source)
at sun.security.provider.certpath.OCSP.check(Unknown Source)
at sun.security.provider.certpath.OCSP.check(Unknown Source)
at sun.security.provider.certpath.OCSP.check(Unknown Source)
at com.sun.deploy.security.RevocationChecker$2.run(Unknown Source)
at com.sun.deploy.security.RevocationChecker$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.deploy.security.RevocationChecker.doPrivilegedOCSPCheck(Unknown Source)
... 20 more
我们能做什么?当然,我们要求 Thawte 更新我们的证书,并要求我们的客户升级到已注销的应用程序。但我们无法涵盖所有这些。当他们向我们询问时,我们需要为他们提供一些快速建议。过期时间快到了,欢迎大家评论。
发生什么事?
WebStart 的行为很大程度上取决于它所属的 JRE 版本。
这些是我们对应用程序的测试结果,该应用程序使用官方证书颁发机构的有效证书进行签名,但在证书过期后没有时间戳。在 Windows 7 上使用 x64 JRE 进行测试,通过在不同版本中直接执行
javaws.exe
并更改系统时钟进行模拟:
我们注意到,从浏览器启动时,WebStart 会尝试使用系统上当前安装的最新版本。在浏览器中更改 JNLP 文件的应用程序是不够的 (Firefox)。有一个使用安装在
Programm Files\Java
文件夹中的 JRE 和 JDK 的查找策略。从命令行或 Windows 链接调用 javaws.exe
会真正执行要测试的版本。您可以在 Java 控制台(成功启动)或任务管理器命令行列(委托给另一个版本的 jp2launcher.exe
)中看到版本。
解决方法
http://myhost:12345/my/app/test.jnlp
时,例外站点 http://myhost:12345/
起作用。使用 myhost
的 IP 地址代替或 myhost.in-my-domain.com
将不匹配。请参阅 http://java.com/de/download/faq/exception_sitelist.xml。...\javaws.exe <jnlp-url>
可能是一种解决方法。带有时间戳和警告的签名
Oracle 声明,使用官方时间戳机构 (TSA) 的时间戳进行签名将防止签名过期。这使您可以防止未来版本中出现问题并提供更新版本。
请注意此警告: 即使在签名证书过期后,WebStart 也对带时间戳的签名感到满意。但是,当您的 TSA 证书过期时,它将阻止该申请并声明“证书已过期或尚未生效”。在我们的测试中,这是在 2020 年 3 月 16 日使用 TSA
http://tsa.starfieldtech.com/
。您可以在 Timestamp:
的输出中在 keytool -printcert -jarfile <your-signed.jar>
之后看到此到期日期。
时间戳只会让你在这颗定时炸弹的时钟上多活几年。根据您的应用程序类型,这可能不是问题,但对于必须运行未来 10 年的封闭环境中的嵌入式应用程序来说,这是一个杀手。 (使用j8u66测试)
2016-01-07 更新:Oracle Support 对于这个问题的最终答复是“没有 bug。该行为是预期的和有意的。肯定不会有任何改变。”。这意味着现在和将来都没有办法在没有过期的情况下签署应用程序。
我只是想补充一点,您在更改计算机时间时看到的异常与证书到期没有任何关系。 OCSP 是一种用于在证书是否吊销时调用服务器的协议。 OCSP 响应中有一个时间,如果您的计算机时钟与响应时间相差超过 900 秒,则会发生此异常。如果不对时钟进行操作,则不会发生此异常。
此片段来自 1.8u221 JRE sun.security.provider.certpath.OCSPResponse singleResponse 有来自 OCSP 服务器的响应。
/* 591 */ long l = (paramDate == null) ? System.currentTimeMillis() : paramDate.getTime();
/* 592 */ Date date1 = new Date(l + MAX_CLOCK_SKEW);
/* 593 */ Date date2 = new Date(l - MAX_CLOCK_SKEW);
/* 594 */ for (SingleResponse singleResponse : this.singleResponseMap.values()) {
/* 595 */ if (debug != null) {
/* 596 */ String str = "";
/* 597 */ if (singleResponse.nextUpdate != null) {
/* 598 */ str = " until " + singleResponse.nextUpdate;
/* */ }
/* 600 */ debug.println("OCSP response validity interval is from " + singleResponse
/* 601 */ .thisUpdate + str);
/* 602 */ debug.println("Checking validity of OCSP response on: " + new Date(l));
/* */ }
/* */
/* */
/* */
/* */
/* */
/* 609 */ if (date1.before(singleResponse.thisUpdate) || date2
/* 610 */ .after(
/* 611 */ (singleResponse.nextUpdate != null) ? singleResponse.nextUpdate : singleResponse.thisUpdate))
/* */ {
/* 613 */ throw new CertPathValidatorException("Response is unreliable: its validity interval is out-of-date");
/* */ }
/* */ }
/* */ }