gpt4 book ai didi

java - SecureRandom 初始化缓慢

转载 作者:可可西里 更新时间:2023-11-01 12:42:49 25 4
gpt4 key购买 nike

假设你做简单的事情:

public class Main {
public static void main(String[] args) {
long started = System.currentTimeMillis();
try {
new URL(args[0]).openConnection();
} catch (Exception ignore) {
}
System.out.println(System.currentTimeMillis() - started);
}
}

现在用 http://localhost 运行它作为 args[0]

完成需要 ~100 毫秒

现在尝试 https://localhost

它需要 5000+ 毫秒

现在在 linux 或 docker 中运行相同的东西:

  • http:~100 毫秒
  • https:~350 毫秒

这是为什么?为什么平台之间会有如此巨大的差异?你能做些什么?

对于长时间运行的应用程序服务器和具有自己的长而繁重的初始化序列的应用程序,这 5 秒可能无关紧要。

但是,有很多应用程序最初的 5 秒“挂起”很重要,可能会变得令人沮丧......

最佳答案

(注意:另请参阅此答案末尾的最新更新)

解释

这是默认的原因 SecureRandom供应商。

在 Windows 上,有 2 个 SecureRandom提供者可用:

- provider=SUN, type=SecureRandom, algorithm=SHA1PRNG
- provider=SunMSCAPI, type=SecureRandom, algorithm=Windows-PRNG

在 Linux 上(使用 Oracle JDK 8u162 在 Alpine docker 中测试):

- provider=SUN, type=SecureRandom, algorithm=NativePRNG
- provider=SUN, type=SecureRandom, algorithm=SHA1PRNG
- provider=SUN, type=SecureRandom, algorithm=NativePRNGBlocking
- provider=SUN, type=SecureRandom, algorithm=NativePRNGNonBlocking

这些在 jre/lib/security/java.security 中指定文件。

security.provider.1=sun.security.provider.Sun
...
security.provider.10=sun.security.mscapi.SunMSCAPI

默认情况下,第一个 SecureRandom使用提供者。在 Windows 上,默认值为 sun.security.provider.Sun ,当 JVM 使用 -Djava.security.debug="provider,engine=SecureRandom" 运行时,此实现报告如下:

Provider: SecureRandom.SHA1PRNG algorithm from: SUN
provider: Failed to use operating system seed generator: java.io.IOException: Required native CryptoAPI features not available on this machine
provider: Using default threaded seed generator

而且默认的线程种子生成器非常慢。

您需要使用 SunMSCAPI供应商。

方案一:配置

重新排序配置中的供应商:

编辑 jre/lib/security/java.security :

security.provider.1=sun.security.mscapi.SunMSCAPI
...
security.provider.10=sun.security.provider.Sun

我不知道这可以通过系统属性来完成。

或者也许是,使用 -Djava.security.properties (未经测试,see this)

解决方案 2:程序化

以编程方式重新排序提供者:

Optional.ofNullable(Security.getProvider("SunMSCAPI")).ifPresent(p->{
Security.removeProvider(p.getName());
Security.insertProviderAt(p, 1);
});

JVM 现在报告以下 ( -Djava.security.debug="provider,engine=SecureRandom"):

Provider: SecureRandom.Windows-PRNG algorithm from: SunMSCAPI

解决方案 3:程序化 v2

灵感来自 this idea , 下面的一段代码只插入一个 SecureRandom服务,从现有动态配置 SunMSCAPI提供者没有明确依赖 sun.*类。这也避免了与 SunMSCAPI 的所有服务不加区别地确定优先级相关的潜在风险。供应商。

public interface WindowsPRNG {

static void init() {
String provider = "SunMSCAPI"; // original provider
String type = "SecureRandom"; // service type
String alg = "Windows-PRNG"; // algorithm
String name = String.format("%s.%s", provider, type); // our provider name
if (Security.getProvider(name) != null) return; // already registered
Optional.ofNullable(Security.getProvider(provider)) // only on Windows
.ifPresent(p-> Optional.ofNullable(p.getService(type, alg)) // should exist but who knows?
.ifPresent(svc-> Security.insertProviderAt( // insert our provider with single SecureRandom service
new Provider(name, p.getVersion(), null) {{
setProperty(String.format("%s.%s", type, alg), svc.getClassName());
}}, 1)));
}

}

性能

<140 msec (而不是 5000+ msec )

详情

有人调用new SecureRandom()当你使用 URL.openConnection("https://...") 时调用堆栈的某个地方

它调用getPrngAlgorithm() (参见 SecureRandom:880)

这首先返回 SecureRandom它找到的提供商。

出于测试目的,请调用 URL.openConnection()可以替换为:

new SecureRandom().generateSeed(20);

免责声明

我不知道提供商重新排序会导致任何负面影响。然而,可能有一些,特别是考虑到默认提供者选择算法。

无论如何,至少在理论上,从功能的角度来看,这对应用程序应该是透明的。

更新 2019-01-08

Windows 10(版本 1803):无法再在任何最新的 JDK 上重现此问题(从旧的 oracle 1.7.0_72 到 openjdk“12-ea”2019-03-19 都进行了测试)。

看起来这是 Windows 问题,已在最新的操作系统更新中修复。在最近的 JRE 版本中也可能发生也可能没有发生相关更新。但是,即使我最老的 JDK 7 update 72 安装也确实受到了影响,而且绝对没有以任何方式修补,我也无法重现原始问题。

使用此解决方案时仍有微小的性能提升(平均约 350 毫秒),但默认行为不再遭受无法忍受的 5 秒以上惩罚。

关于java - SecureRandom 初始化缓慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49322948/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com