如何解决缓慢的Java SecureRandom?

如果你想在Java中使用密码强的随机数,你可以使用SecureRandom 。 不幸的是, SecureRandom可能非常慢。 如果它在Linux上使用/dev/random ,它会阻塞等待足够的熵build立。 你如何避免performance的惩罚?

有没有人使用罕见的math作为解决这个问题?

有人可以证实这个性能问题已经在JDK 6中解决了吗?

如果你想要真正的随机数据,那么不得不等待它。 这包括SecureRandom PRNG的种子。 不常见的math不能比SecureRandom更快地收集真正的随机数据,虽然它可以连接到互联网从特定网站下载种子数据。 我的猜测是,这可能不会比/dev/random更快。

如果你想要一个PRNG,做这样的事情:

 SecureRandom.getInstance("SHA1PRNG"); 

支持哪些string取决于SecureRandom SPI提供程序,但您可以使用Security.getProviders()Provider.getService()枚举它们。

Sun喜欢SHA1PRNG,所以它被广泛使用。 PRNGs并不是特别快,但PRNGs只是在计算数字,而不是阻碍熵的物理测量。

但是,如果您在获取数据之前没有调用setSeed() ,PRNG将在您第一次调用next()nextBytes() 。 它通常会使用来自系统的相当less量的真随机数据来做到这一点。 这个调用可能会阻止,但是会使得你的随机数字来源比任何“把当前时间和PID加在一起,加上27,希望最好”的变种更安全。 如果你所需要的只是一个游戏的随机数字,或者如果你想在将来使用相同的种子进行testing,那么一个不安全的种子仍然是有用的。

你应该能够在Linux上select速度更快但安全性更低的/ dev / urandom:

 -Djava.security.egd=file:/dev/urandom 

但是,这不适用于Java 5及更高版本( Java Bug 6202721 )。 build议的解决方法是使用:

 -Djava.security.egd=file:/dev/./urandom 

(注意多余的/./

在Linux上, SecureRandom的默认实现是NativePRNG (源代码在这里 ),这往往是非常慢的。 在Windows上,默认值是SHA1PRNG ,正如其他人指出的那样,如果您明确指定,也可以在Linux上使用。

NativePRNG不同于SHA1PRNG和Uncommons Maths的AESCounterRNG ,它不断地从操作系统接收熵(通过从/dev/urandom读取)。 其他PRNG在播种后不会获得额外的熵。

AESCounterRNG比SHA1PRNG快10倍,而IIRC本身比NativePRNG快两到NativePRNG

如果您需要更快的PRNG,在初始化之后获取熵,请参阅您是否可以findFortuna的Java实现。 Fortuna实现的核心PRNG与AESCounterRNG使用的核心PRNG相同,但是也有复杂的熵池和自动重新加载系统。

我在一个无头的Debian服务器上每次调用SecureRandom约25秒时遇到了类似的问题。 我安装了haveged守护进程,以确保/dev/random保持最佳状态,在无头的服务器上,您需要类似这样的东西来生成所需的熵。 我现在调用SecureRandom可能需要几毫秒。

如果你真的想要“密码强”的随机性,那么你需要一个强大的熵源。 /dev/random很慢,因为它必须等待系统事件收集熵(磁盘读取,networking数据包,鼠标移动,按键等)。

更快的解决scheme是硬件随机数发生器。 您可能已经有一个内置于您的主板; 请查看hw_random文档 ,了解是否拥有该文档以及如何使用它。 rng-tools软件包包含一个守护进程,它将硬件生成的熵提供给/dev/random

如果你的系统没有HRNG,并且你愿意为了性能而牺牲熵强度,那么你将需要从/dev/random一个好的PRNG,并让PRNG完成大部分的工作。 在SP800-90中列出了几个NIST认可的PRNG,它们很容易实现。

使用安全随机作为循环algorithm的初始化源; 那么你可以用一个Mersenne的捻线机来做批量工作,而不是UncommonMath中的那个,它已经存在了一段时间,并且certificate比其他的更好

http://en.wikipedia.org/wiki/Mersenne_twister

确保现在刷新用于初始化的安全随机,例如,每个客户端可以使用一个安全随机生成,每个客户端使用一个mersenne twister伪随机生成器,获得足够高的随机化程度

你提到的有关/dev/random的问题不是用SecureRandomalgorithm,而是用它所使用的随机性来源。 两者是正交的。 你应该弄清楚两者中的哪一个会让你放慢脚步。

您明确链接的“罕见math”页面提到他们没有处理随机性的来源。

您可以尝试不同的JCE提供程序(如BouncyCastle),以查看它们的SecureRandom实现是否更快。

一个简单的search也揭示了用Fortunareplace默认实现的Linux补丁。 对此我不甚了解,但欢迎您进行调查。

我还应该提到,虽然使用一个糟糕的SecureRandomalgorithm和/或随机源是非常危险的,但是您可以使用SecureRandomSpi的自定义实现来推出自己的JCE Provider。 您需要通过Sun的stream程才能让您的供应商签名,但实际上这非常简单; 他们只需要你给他们传真一份表格,说明你知道美国对encryption库的出口限制。

我的经验一直是PRNG的初始化很慢,而不是随机生成数据。 尝试一个更加渴望的初始化策略。 由于创build起来很昂贵,因此将其视为单例,并重用相同的实例。 如果一个实例的线程争用太多,请将其集中或使其成为线程本地。

不要在随机数字生成上妥协。 那里的弱点会危及你的所有安全。

我没有看到很多COTSprimefaces衰变发生器,但是如果你真的需要大量的随机数据,那么他们有几个计划。 约翰·沃克 ( John Walker)的Fourmilab是一个总是有趣的东西,包括HotBits。

我面临同样的问题 。 经过一些search词的search后,我在DigitalOcean上遇到了这篇不错的文章。

在不影响安全性的前提下,锻炼是一种潜在的解决scheme

我只是在这里引用文章的相关部分。

基于HAVEGE原理,之前基于其相关的库,hasged允许基于处理器上的代码执行时间的变化产生随机性。 由于即使在相同硬件的相同环境中,一块代码几乎不可能执行相同的确切时间,所以运行单个或多个程序的时间应当适合种植随机源。 经过重复执行循环之后,虚拟化实现使用处理器的时间戳计数器(TSC)中的差异对系统的随机源(通常是/ dev / random)进行种子处理

如何安装hasged

按照本文中的步骤操作。 https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

我已经在这里发布了

这听起来像你应该更清楚你的RNG要求。 最强的密码RNG要求(据我所知)将是,即使你知道用于生成它们的algorithm,并且你知道所有以前生成的随机数,你不能得到任何有用的信息未来,不用花费不切实际的计算能力。

如果你不需要这种随机性的充分保证,那么可能有适当的性能折衷。 我倾向于赞同丹·戴尔(Dan Dyer)对“非常规math”(Uncommons-Maths)或者Fortuna(其作者之一是密码学专家Bruce Schneier)的AESCounterRNG 的回应 。 我从来没有用过,但这些想法乍一看显得有声望。

如果你可以定期产生一个初始的随机种子(例如每天或每小时一次),你可以使用一个快速stream密码来从连续的stream中产生随机数(如果stream密码使用XOR,那么只是传入一个空值stream或直接获取XOR位)。 ECRYPT的eStream项目有很多很好的信息,包括性能基准。 这不会在你补充的时间点之间保持熵,所以如果有人知道你使用的随机数和algorithm之一,在技术上可能有很大的计算能力来破坏stream密码,猜测它的内部状态能够预测未来的随机数。 但是你必须决定这个风险及其后果是否足以certificate维持熵的成本。

编辑:这里有一些关于RNG的encryption课程笔记,我在与这个主题非常相关的networking上find了。

有一个工具(至less在Ubuntu上)会将人为的随机性join到你的系统中。 该命令很简单:

 rngd -r /dev/urandom 

你可能需要一个sudo在前面。 如果您没有rng-tools软件包,则需要安装它。 我尝试了这个,这绝对帮助了我!

来源: 亚光vs世界

使用Java 8,我发现在Linux上调用SecureRandom.getInstanceStrong()会给我NativePRNGBlockingalgorithm。 这往往会阻塞几秒钟来产生几个字节的盐。

我切换到明确要求NativePRNGNonBlocking而不是从名称预期,它不再被阻止。 我不知道这是什么安全的影响。 据推测,非阻塞版本不能保证熵的使用量。

更新 :好的,我发现这个优秀的解释 。

简而言之,为了避免阻塞,请使用new SecureRandom() 。 这使用/dev/urandom ,它不会阻塞,基本上和/dev/random一样安全。 在post中:“唯一一次你想调用/ dev / random是机器第一次启动时,熵还没有积累”。

SecureRandom.getInstanceStrong()给你绝对最强的RNG,但是在一堆阻塞不会影响你的情况下使用它是安全的。

我自己并没有反对这个问题,但是我在程序开始时会产生一个线程,立即尝试产生一个种子,然后死亡。 你调用randoms的方法会join到那个线程,如果它是活着的,所以第一个调用只会在程序执行的早期发生。

另外要看的是文件lib / security / java.security中的属性securerandom.source

使用/ dev / urandom而不是/ dev / random可能会带来性能方面的好处。 请记住,如果随机数的质量很重要,不要做出破坏安全性的妥协。

如果您的硬件支持,请尝试使用以下url提供的Java RdRand Utility: http : //code.google.com/p/lizalab-rdrand-util/

它基于英特尔的RDRAND指令,比SecureRandom快大约10倍,在大量实现时不会出现带宽问题。

充分披露,我是公用事业的作者。

你可以尝试Apache公共math项目,它有一些知名algorithm的实现:

https://commons.apache.org/proper/commons-math/userguide/random.html

但是,要注意性能。 RandomDataGenerator的默认构造函数创buildWell19937c的专用实例,这是一个非常昂贵的操作。

根据文档,这个类不是线程安全的,但是如果你可以保证只有一个线程可以访问这个类,那么你可以只为每个线程初始化一个实例。

问题陈述

这个库msprandom演示了一种为了encryption目的而生成随机数而不使用硬件生成器的技术。 encryption和签名需要一个质量好的随机数字。 生成一个没有硬件生成器的随机数(或随机字节序列)并不是一件容易的事情。 特别是对于那些随机数据源不存在或有限的小型设备来说,这个问题是实际的。 解决办法是将真随机种子保存在一个安全的文件(保险库)和密码中,这个密码可以产生具有良好随机特性的随机种子encryption的伪随机生成(PRNG)序列。

许多encryption库(例如BouncyCastle)使用SecureRandom类进行encryption和签名以获得随机数。 SecureRandom取决于操作系统的实施。 换句话说,随机引擎的实现是你无法控制的应用程序之外的。 为了避免使用不好的随机数,每次调用需要随机数据的encryption函数时,必须为SecureRandom生成器生成良好的随机数据。 或者你可以扩展SecureRandom类与你的实现,产生一个随机数字,你可以控制的质量。

理念

我们需要使用存储在安全数据仓库中的真随机数据。

一些步骤如何在您的应用程序msprandom :

  1. 在您的计算机或笔记本上生成一个真正的随机种子,并使用此库将其放到一个Vault中。
  2. 将带有随机种子的保pipe库(文件)放在您需要encryption和签名数据的设备,计算机或服务器上。
  3. 当您需要encryption或签署数据时,在程序开始时加载一次保pipe库。
  4. 从msprandom库中调用gen-rand函数,根据需要随机获取多个字节。

具有随机种子的保pipe库使用HMAC进行encryption和保护。 每次以不可预知的方式加载保pipe库时,保险库中的随机种子都会更新,因此HMAC也在变化。 如果攻击者可以在过去丰富您的Vault的某些副本,则会有意地更改Vault。

真随机数据发生器

为了生成一个真正的随机种子,在msprandom中使用一个人类input。 这里是收集随机数据的algorithm:

  1. 我们运行独立的线程,其中primefaces计数器从0..255以非常高的速度递增每个抽点。
  2. 等待人的无缓冲按键,并获得按下button的扫描码。
  3. 从Epoch开始取当前的纳秒值,并采用mod 256将其值转换为随机字节。
  4. XOR彼此之间的值:扫描码字节^电stream – 计数器值^纳秒以产生随机字节。
  5. 添加随机字节输出向量。 我们假设在这个随机字节中只有3个比特具有真正的随机性。 所以,要获得真正的随机32个字节,我们需要从用户input〜32 * 3button按下。
  6. 重复步骤2-5,直到获得所需数量的随机字节。 如果我们收集到所需数量的随机数据,则使用encryption强散列函数做最后一步 – >散列输出向量,以保证输出向量中的概率1和0位为0.5。 注意,这里使用的散列函数只是为了混合随机比特而不影响随机数据的质量。 所以哈希(随机数据)=随机数据。 使用这个algorithm,msprandom收集一个真正的512个随机比特作为种子,将被保存为一个金库。

为什么512个随机位就够了?

那么每个PRNG都需要一个真正的随机种子。 如果攻击者知道种子,那么它可以预测密钥生成等等。 256比特的初始随机种子远远足以保持毫克级的秘密。 我做了512,以确保没有人可以蛮力或猜测最初的随机种子。 所以,你可以自由地使用msprandom为PRNG或SecureRandom生成器播种。

许多Linux发行版(主要是基于Debian的)错误configurationOpenJDK使用/dev/random进行熵。

/dev/random被定义为慢(甚至可以阻塞)。 使用/dev/urandom会是谨慎的(例如,这是JDK在Solaris上使用的)。

要全局修复它,请在默认的Java安装中编辑文件jre/lib/security/java.security以使用/dev/urandom (由于另一个bug需要指定为/dev/./urandom )。

喜欢这个:

 #securerandom.source=file:/dev/random securerandom.source=file:/dev/./urandom 

那么你将永远不必在命令行上指定它。