一、背景描述

近期对接微信二维码支付的接口,发现一个奇怪的问题:
现象为有一工具类定义了一个变量UUID,linux机器初始化贼慢,window机器下很正常,linux执行记录为:


每次项目重启后,首次加载该类时,执行时间很久,但第二次之后就一切正常.....
怀着好奇的心情,记录下这次经过,方便以后查阅。

二、代码分析

QRCodeUtils.java
public class QRCodeUtils {
    //编码格式
    private static final String CHARSET = "utf-8"; 
    //图片格式
    private static final String FORMAT_NAME = "JPG";  
    //二维码尺寸 (默认) 
    public static final int QRCODE_SIZE = 100;  
    //二维码名称 (默认) 
    public static final String QRCODE_NAME = UUIDUtil.generateFormattedGUID();   //不能直接获取UUID,会造成项目重启后首次加载二维码图片很慢
    //LOGO宽度 (默认)
    private static final int WIDTH = 60;  
    //LOGO高度 (默认)
    private static final int HEIGHT = 60;  
    //二维码识别率(默认)H,Q,M,L容错率依次降低
    private static final ErrorCorrectionLevel ERROR_CORRECTION_LEVEL = ErrorCorrectionLevel.L;
    //logo是否压缩(默认)
    private static final boolean LOGO_NEED_COMPRESS = true;
    
    private static final Logger log = Logger.getLogger(QRCodeUtils.class);
    //....其他方法
}

UUIDUtil.java
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import org.apache.log4j.Logger;

/**
 * UUID工具类
 * @author zengdq
 * @date 2016年9月20日
 */
public class UUIDUtil {
	
	private static final Logger log = Logger.getLogger(UUIDUtil.class);
	
	private static SecureRandom rnd;

	static {
		Long t1 = System.currentTimeMillis();
		try {
			rnd = SecureRandom.getInstance("SHA1PRNG"); //linux = NativePRNG,windowx=SHA1PRNG
		} catch (NoSuchAlgorithmException e) {
			rnd = new SecureRandom(); 
		}

		byte[] seed = rnd.generateSeed(64);
		rnd.setSeed(seed);
		Long t2 = System.currentTimeMillis();
		log.info("UUIDUtil类初始化消耗时间="+(t2-t1)+"毫秒");
	}
	
	/**
	 * 获取UUID(格式已处理)
	 * @return
	 */
	public static String generateFormattedGUID() {
		String guid = generateGUID();
		return guid.substring(0, 8) + '-' + guid.substring(8, 12) 
			+ '-' + guid.substring(12, 16) + '-' + guid.substring(16, 20) + '-'+ guid.substring(20);
	}

	/**
	 * 获取UUID(32位)
	 * @return
	 */
	public static String generateGUID() {
		return new BigInteger(165, rnd).toString(36).toUpperCase();
	}
	
}

代码中SecureRandom.getInstance("SHA1PRNG")这行--->是问题产生的根源

三、知识普及

 如果你想在Java中,SecureRandom的强加密随机数。
不幸的是,SecureRandom的可能会非常缓慢。如果在Linux的/ dev /随机的,它可以阻止等待足够的熵建立。
你如何避免效果进行处罚?

1.如果你想真正的随机数据,那么很不幸,你必须等待它。这包括seed一个SecureRandom PRNG。
数学抓不到真正的随机数据的速度比SecureRandom的,虽然它可以连接到从特定网站下载的seed数据。
我的猜测是,这是不可能比的/ dev / random的地方那可得更快。 如果你想有一个PRNG,这样做:
SecureRandom.getInstance("SHA1PRNG");
支持哪些字符串取决于SecureRandom的SPI提供者,但你可以Security.getProviders()和Provider.getService()。 
太阳是喜欢SHA1PRNG的 CodeGo.net,所以它的广泛使用。它不是特别快的PRNG去,但PRNG可将只是捣弄数字,不堵的熵。 
唯一的异常是,如果你得到的数据之前没有调用过setSeed(),那么PRNG将调用next()或的nextBytes(seed本身一旦优先)。
它做一个相当小的量的从系统中真正的随机数据。这个调用可能会阻止,反而会使你的随机数源远高于任何变体更安全的“散列当前与PID在一起,加27,和最好的希望”。
如果你需要的是随机数了,虽然,或者如果您想流是可重复的seed用于测试目的,不安全的seed  

2. 你应该可以选择更快,但是,稍微不太安全的/ dev / urandom的上
-Djava.security.egd=file:/dev/urandom
然而,这并不与Java 5的工作和更高版本(Java的错误6202721)。建议的解决方法是
-Djava.security.egd=file:/dev/./urandom
(注意额外的'//') 

3. 在Linux上,默认为SecureRandom的是“NativePRNG”(这里的源代码),这往往是很慢的。
    在Windows中,默认是“SHA1PRNG”,这是别人指出你可以在Linux上,如果你明确地指定它。
 “NativePRNG”不同于“SHA1PRNG”和数学“AESCounterRNG,它接收来自操作系统(通过从/ dev / urandom的阅读)熵。
其他的PRNG播种后不会获得任何额外的熵。 AESCounterRNG是大约10倍比“SHA1PRNG”更快,这是IIRC本身不是“NativePRNG”两个或三个速度更快。 
如果你需要更快的PRNG的初始化后获得熵,看你能不能找到财神的一个Java.a个财神的核心PRNG是相同的由AESCounterRNG,但也有熵池和自动补种的一个复杂的系统。
 
4. 如果你想真正的“保密性强的”随机性,那么你需要一个强大的信息源。的/ dev /随机慢它必须等待系统事件收集熵(磁盘读取,网络数据包,按键等)。 
更快的解决方案是一个硬件随机数发生器。您可能已经有一个内置在你的主板;检查出hw_random就搞清楚的指示,如果你拥有了它,以及它是如何。
该RNG-tools软件包包括一个守护进程将feed生成的硬件熵送到/ dev /随机的。 如果HRNG无法使用您的系统上,你是愿意牺牲熵强度性能,你会想用seed从/ dev / random的数据了良好的PRNG,并让PRNG做大量的工作。有几个NIST批准的PRNG在SP800-90列出了被直白地

5. 关于你的/ dev /随机引用的问题是不是与SecureRandom的算法,但与随机性的来源,这两者是正交的。
你应该找出其中的两个一个是你放慢下来。 你挂,他们都没有解决随机源页面。 你可以尝试不同的JCE提供者,如BouncyCastle的,看到自己的SecureRandom的,
如果是快。 一个简短的搜索还揭示了Linux的补丁替换为财神默认。我不知道更多关于这一点,但你去查。 
我应该,虽然这是一个非常严重的SecureRandom算法和/或随机性源,你可以推出自己的JCE提供与SecureRandomSpi的自定义。
您将需要通过与Sun的过程,让您的提供商签约,但实际上它是非常简单的,他们只是需要你传真给一个表格,说明你是知道的加密库,美国的出口限制。
 
6. 使用作为初始来源为复发性算法的安全随机的,你那么梅森捻线机的大量工作,而不是在其中已经存在了一段时间,证明比其他更好的PRNG 确保现在再刷新安全的初始化,例如,你可以有一个安全随机每个客户端端生成的,使用捻线机伪随机数发生器每个客户端端,获得足够高的程度的随机化 

7. 我的经验是只与PRNG的初始化速度慢,不能与一代的随机数据之后。尝试更渴望初始化策略。因为他们是昂贵的创建,把它像一个单身主义者和实例。
如果有太多的线程争用一个实例,凝聚他们或使他们本地线程。 随机数生成唐'.a个弱点你所有的安全性。 我没有看到很多的COTSatomicity衰变为基础的generator,
但有几个计划在那里对他们来说,如果你真的需要大量的随机数据.a个网站,总是有有趣的事情来看待,包括HotBits,是约翰・沃克的Fourmilab。
 
8. 这听起来像你应该更清楚你的RNG的最强的加密RNG(据我所知)是,即使你知道产生他们,你知道所有生成的随机数,你不能得到任何关于任意随机的在未来产生的数字,而无需花费一个不切实际的电量。 如果您不需要随机性这充分保证那么有可能是适当的性能折衷。我倾向于同意与丹代尔的约AESCounterRNG或从财神(它的作者之一是布鲁斯,在密码学方面的专家)的响应。我已经不是但这些想法似乎有信誉的优先眼。 我认为,如果你能生成一个初始随机seed定期(例如每天一次或一小时或其他),你一个快速的流密码,以便从流的块生成随机数(如果流XOR那么就传递流空值或直接抢XOR位)。 ECRYPT的eStream项目有很多,包括业绩基准的。这不会保持点之间的熵在你补充它,所以如果知道的随机数,算法之一,在技术上有可能,有很多权力,打破流密码和猜测它的内部状态,以便能够来预测未来的随机数。但你必须决定风险及其后果是否足以证明维持熵的成本。 编辑:这里是在RNG加密课堂笔记,我发现了“净,看起来非常有关这个话题。 

9. 我没有撞到这个问题我自己,但我会产生一个线程在程序启动时,它试图产生一个seed,然后死。您拨打的偶合将加入到该线程,如果它是活的,因此优先次调用,如果仅阻止它在程序执行很早就出现。 

10. 别人看是属性securerandom.source文件中的lib /安全性/ java.security中 可能有性能优势的/ dev / urandom的,而不是为/ dev /随机的。如果随机数的质量是重要的,不作打破安全。 

11. 有一个工具(在Ubuntu至少),将feed人工随机进入你的系统。很简单:
rngd -r /dev/urandom
Logo

更多推荐