如何生成在C#范围内的加密安全整数?我试图满足一个Veracode问题报告CWE-331:熵不足。显然,下面的代码并不是加密安全的。
Random rand = new Random();
return rand.Next(999999);我通常使用RNGCryptoServiceProvider类生成加密安全数据,但不确定如何安全地将其转换为范围内的数字。
有些帖子说你可以使用模块化运算符,但另一些人报告说它扭曲了随机结果的分布。
byte[] data = new byte[8];
ulong value;
do
{
rng.GetBytes(data);
value = BitConverter.ToUInt64(data, 0);
} while(value==0);
result = (int)(value%15+1);发布于 2019-08-05 14:47:11
当你创建随机数时,你需要问自己这些随机数是干什么用的。
例如,如果你写了第一人称射击,想要产生一些随机子弹传播,那么随机性不需要是高熵或均匀分布,只要它是“足够好”的游戏。
另一方面,如果您要编写具有某种密码用途的代码,那么您使用的随机数必须满足某些属性,这取决于它们的确切用途。早在2008年,Debian团队在他们的巨虫包中就有了一个OpenSSL,导致随机数的质量显著下降。讽刺的是,这一切都是由于代码质量工具的抱怨。
你提到你打算用你的随机数作为一个现在。它确实取决于现在的确切目的,但是一个现在应该实现两个属性:
第二个属性实际上是您的代码的全部内容。
System.Random不舒服?System.Random类的设计是为了快速提供看起来随机的数字。这些数字真的不是“高质量的”,而且像我前面展示的那样,被设计成游戏。
能够读取系统生成的足够随机数的攻击者可能能够预测系统生成的未来数字。这可能不是游戏中的问题,但如果您查看您的密码代码,并想象攻击者知道所有的非all,这很可能是一个问题。
System.Random的MSDN页面建议使用System.Security.Cryptography.RNGCryptoServiceProvider。顾名思义,它被设计用于加密应用程序。请注意,如果您已经在使用第三方密码库,它们可能会提供自己的接口来生成安全的随机数,这可能更方便用户,也不太容易出现实现错误。
通常,当开发人员生成随机数时,他们希望它们在一个特定的范围内,例如“0到9999之间”。这让位于偏置错误,由于模运算符的工作方式,使得某些值比其他值更有可能出现。
但是,由于你所关心的是不可预测性,你可以让你的生活变得简单,产生一个易于使用的大小,这意味着只需要按字节来生成它。
例如,下面的代码生成一个所需长度的值:
private static RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
public static byte[] GenerateNonce(uint nonceLength)
{
byte[] nonce = new byte[nonceLength];
rngCsp.GetBytes(nonce);
return nonce;
}为什么这段代码比其他代码更简单?因为您提供的代码是为了处理我前面提到的情况而设计的,在这种情况下,偏倚可能是一个问题。因为您可以生成“很好”的值,所以您将不会出现偏差问题,并且您的代码会变得容易得多。
是。好吧,差不多了。攻击者仍然可以猜到现在的情况,而且猜测正确的可能性很小。但这与考虑一个介于0到2^128-1之间的数字而不告诉你它是什么一样。您仍然可以猜测,但您猜对了的可能性是are...small。非常小。
事实上,它们变得越小越大。现在要多久才能使猜测变得不可行?一般的答案是它应该至少有80位的熵,所以至少有10个字节。如果您觉得您可以处理开销,您可以继续使用16字节(128位),您应该是好的。
发布于 2019-08-05 14:49:34
我认为像这样的事情应该能奏效。如果需要负值或64位整数,则需要对其进行修改。最大输出将是UInt32.MaxValue - 1。如果您需要完整的范围,您最好只做GetBytes和ToUInt32。
/// <summary>
/// Returns a random unsigned, 32-bit integer that is less than a specified limit.
/// </summary>
/// <param name="max">The exclusive max value of the result.</param>
/// <returns></returns>
public UInt32 GetRandomInt(UInt32 max = UInt32.MaxValue)
{
//might want to throw an exception or something instead
if (max < 2)
return 0;
//everything above this will skew distribution
UInt32 discardLimit = UInt32.MaxValue - (UInt32.MaxValue % max);
UInt32 ret = 0;
using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
{
byte[] retBuffer = new byte[4];
bool validResult = false;
//it is theoretically possible to loop forever here, maybe add a counter to throw an exception eventually
while (!validResult)
{
rng.GetBytes(retBuffer);
ret = BitConverter.ToUInt32(retBuffer, 0);
if (ret <= discardLimit)
{
validResult = true;
ret = ret % max;
}
}
}
return ret;
}可能值得保留一个随机数生成器的实例并使用它,而不是每次创建和处理它。
可能可以通过保存丢弃比特并将其转换为另一次尝试,或者使用更大的缓冲区来减少对随机数生成器的调用次数来进行优化,但这对于不频繁的数字生成有效。
如果您只需要一个nonce,那么可以看到另一个答案,只需在缓冲区上使用GetBytes。
https://security.stackexchange.com/questions/214688
复制相似问题