.net 安全地生成一致随机BigInteger
我想在[0,N]范围内安全地生成一个随机数,其中N是一个参数。但是,System.Security.Cryptography.RandomNumberGenerator仅提供一个GetBytes()方法来用随机值填充数组 (我需要在稍微修改过的SRP版本中使用的nonce的随机整数。“稍微修改过的”部分超出了我的控制,这也是我接触加密内容的唯一原因。) 我已经写了一个方法来做到这一点,但我正在寻找一个更好的方法,或者至少确认我做得对.net 安全地生成一致随机BigInteger,.net,security,random,cryptography,biginteger,.net,Security,Random,Cryptography,Biginteger,我想在[0,N]范围内安全地生成一个随机数,其中N是一个参数。但是,System.Security.Cryptography.RandomNumberGenerator仅提供一个GetBytes()方法来用随机值填充数组 (我需要在稍微修改过的SRP版本中使用的nonce的随机整数。“稍微修改过的”部分超出了我的控制,这也是我接触加密内容的唯一原因。) 我已经写了一个方法来做到这一点,但我正在寻找一个更好的方法,或者至少确认我做得对 using System.Numerics ///<s
using System.Numerics
///<summary>Generates a uniformly random integer in the range [0, bound).</summary>
public static BigInteger RandomIntegerBelow(this System.Security.Cryptography.RandomNumberGenerator source, BigInteger bound) {
Contract.Requires<ArgumentException>(source != null);
Contract.Requires<ArgumentException>(bound > 0);
Contract.Ensures(Contract.Result<BigInteger>() >= 0);
Contract.Ensures(Contract.Result<BigInteger>() < bound);
//Get a byte buffer capable of holding any value below the bound
var buffer = (bound << 16).ToByteArray(); // << 16 adds two bytes, which decrease the chance of a retry later on
//Compute where the last partial fragment starts, in order to retry if we end up in it
var generatedValueBound = BigInteger.One << (buffer.Length * 8 - 1); //-1 accounts for the sign bit
Contract.Assert(generatedValueBound >= bound);
var validityBound = generatedValueBound - generatedValueBound % bound;
Contract.Assert(validityBound >= bound);
while (true) {
//generate a uniformly random value in [0, 2^(buffer.Length * 8 - 1))
source.GetBytes(buffer);
buffer[buffer.Length - 1] &= 0x7F; //force sign bit to positive
var r = new BigInteger(buffer);
//return unless in the partial fragment
if (r >= validityBound) continue;
return r % bound;
}
}
使用系统数值
///生成范围为[0,界]的均匀随机整数。
public static BigInteger RandomInteger下面(此System.Security.Cryptography.RandomNumberGenerator源,BigInteger绑定){
Contract.Requires(source!=null);
合同要求(绑定>0);
Contract.Result(Contract.Result()>=0);
契约。确保(契约。结果()<绑定);
//获取一个字节缓冲区,该缓冲区能够保存低于界限的任何值
var buffer=(bound=validityBound)continue;
返回r%界;
}
}
该实现在生成范围内的无偏整数时看起来是正确的
另一种方法是将随机字节作为二进制分数0.xxxxxx…
中的无限位,乘以bound
,然后向下取整。实际上,它不需要无限位,只需要确定向下取整的结果是什么。我认为这更复杂
但是,对您的协议而言,生成全范围的数字可能是不必要的。第3.1节只需要256位专用指数。该数字来自哈希输出的位长度加倍,并且需要使用安全素数。RFC的早期草案参考了第4节第一段中的解释在那里
如果您喜欢更多的随机位,请将
范围
的位长度减去1。这就是OpenSSL(搜索BN_rand
及其前面的行)。这更容易生成,并且比全范围短两倍,如果这有区别,您应该使用更大的素数。您的代码看起来正确无偏。但是,如果您在追求性能,并且取决于您使用的随机源的速度,您可能需要对其进行一点更改。其目的是掩盖错误再多几位,使随机值r
小于2*bound
。如果bound的位长度为x
(长度不包括符号位),则生成一个n=((x+8)/8)
字节的缓冲区,并屏蔽上层(n*8-x)
位。在C#中,这应该如下所示:
var x = BitLength(bound);
var n = ((x + 8) / 8);
var buffer = new Byte[n];
var mask = 0xFF >> (8 * n - x);
while (true) {
source.GetBytes(buffer);
buffer[n - 1] &= mask;
var r = new BigInteger(buffer);
if (r < bound)
return r;
}
我使用的是
bound
编码的长度,以字节为单位,由ToByteArray()返回
,正是我需要的n
的值。我也很惊讶没有一个位长度成员。我并不特别担心Mod操作的成本,因为我必须对结果值使用ModPow。
var buffer = bound.ToByteArray();
var n = buffer.length;
var msb = buffer[n - 1];
var mask = 0;
while (mask < msb)
mask = (mask << 1) + 1;
while (true) {
source.GetBytes(buffer);
buffer[n - 1] &= mask;
var r = new BigInteger(buffer);
if (r < bound)
return r;
}