慢速SecureRandom初始化 [英] Slow SecureRandom initialization

查看:177
本文介绍了慢速SecureRandom初始化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设你做的很简单:

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

现在运行 http:// localhost as args [0]



完成需要 ~100毫秒



现在尝试 https:// localhost



需要 5000+毫秒



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




  • http: ~100毫秒

  • https: ~350毫秒



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



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



然而,有很多应用程序,这个最初的5秒挂起很重要,可能会令人沮丧......

解决方案

解释



原因是默认 SecureRandom provider。



在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,algori thm = 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 ,当使用 -Djava.security.debug运行JVM时,此实现将报告以下内容=provider,engine = SecureRandom

 提供商:SecureRandom.SHA1PRNG算法来自:SUN 
提供程序:无法使用操作系统种子生成器:java.io.IOException:此计算机上不可用的本机CryptoAPI功能
提供程序:使用默认线程种子生成器

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



你需要使用 SunMSCAPI 提供商。



解决方案1:配置



在配置中重新排序提供商:



编辑 jre / lib / security / java.security

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

我不是意识到这可以通过系统属性来完成。



或者是的,使用 -Djava.security.properties (未经测试,看到这一点



解决方案2:程序化



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

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

JVM现在报告以下( -Djava.security.debug =提供商, engine = SecureRandom):

 提供商:SecureRandom.Windows-PRNG算法来自:SunMSCAPI 

解决方案3:程序化v2



这个想法的启发,以下一段代码只插入一个 SecureRandom 服务,从现有的 SunMSCAPI 提供程序动态配置,而不依赖于 sun。* 类。这也避免了与 SunMSCAPI 提供商的所有服务不分青红皂白的优先顺序相关的潜在风险。

  public interface WindowsPRNG {

static void init(){
String provider =SunMSCAPI; //原始提供者
String type =SecureRandom; //服务类型
String alg =Windows-PRNG; // algorithm
String name = String.format(%s。%s,provider,type); //我们的提供者名称
if(Security.getProvider(name)!= null)return; //已注册
Optional.ofNullable(Security.getProvider(provider))//仅在Windows
.ifPresent(p-> Optional.ofNullable(p.getService(type,alg))//应该存在,但谁知道?
.ifPresent(svc-> Security.insertProviderAt(//使用单个SecureRandom服务插入我们的提供者
new Provider(name,p.getVersion(),null){{/ / NOSONAR
setProperty(String.format(%s。%s,type,alg),svc.getClassName());
}},1)));
}

}

效果



< 140毫秒(而不是 5000+毫秒



详细信息



拨打<$ c $当您使用 URL.openConnection(https:// ...) 新的SecureRandom()在调用堆栈的某处p>

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



这将返回它找到的第一个 SecureRandom 提供者。



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

  new SecureRandom()。 generateSeed(20); 

免责声明



我不知道提供商重新排序造成的任何负面影响。但是,可能有一些,特别是考虑默认提供商选择算法。



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


Suppose you do simple thing:

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);
    }
}

Now run it with http://localhost as args[0]

It takes ~100 msec to complete.

Now try https://localhost

It takes 5000+ msec.

Now run the same thing on linux or in docker:

  • http: ~100 msec
  • https: ~350 msec

Why is this? Why such a huge difference between platforms? What can you do about it?

For long-running application servers and applications with their own long and heavy initialization sequence, these 5 seconds may not matter.

However, there are plenty of applications where this initial 5sec "hang" matters and may become frustrating...

解决方案

Explanation

Reason for this is default SecureRandom provider.

On Windows, there are 2 SecureRandom providers available:

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

On Linux (tested in Alpine docker with Oracle JDK 8u162):

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

These are specified in jre/lib/security/java.security file.

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

By default, first SecureRandom provider is used. On Windows, the default one is sun.security.provider.Sun, and this implementation reports following when JVM is run with -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

And the default threaded seed generator is very slow.

You need to use SunMSCAPI provider.

Solution 1: Configuration

Reorder providers in configuration:

Edit jre/lib/security/java.security:

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

I am not aware this can be done via system properties.

Or maybe yes, using-Djava.security.properties (untested, see this)

Solution 2: Programmatic

Reorder providers programmatically:

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

JVM now reports following (-Djava.security.debug="provider,engine=SecureRandom"):

Provider: SecureRandom.Windows-PRNG algorithm from: SunMSCAPI

Solution 3: Programmatic v2

Inspired by this idea, following piece of code inserts only a single SecureRandom service, configured dynamically from existing SunMSCAPI provider without the explicit reliance on sun.* classes. This also avoids the potential risks associated with indiscriminate prioritization of all services of SunMSCAPI provider.

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) {{ //NOSONAR
                                    setProperty(String.format("%s.%s", type, alg), svc.getClassName());
                                }}, 1)));
    }

}

Performance

<140 msec (instead of 5000+ msec)

Details

There is a call to new SecureRandom() somewhere down the call stack when you use URL.openConnection("https://...")

It calls getPrngAlgorithm() (see SecureRandom:880)

And this returns first SecureRandom provider it finds.

For testing purposes, call to URL.openConnection() can be replaced with this:

new SecureRandom().generateSeed(20);

Disclaimer

I am not aware of any negative side effects caused by providers reordering. However, there may be some, especially considering default provider selection algorithm.

Anyway, at least in theory, from functional point of view this should be transparent to application.

这篇关于慢速SecureRandom初始化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆