C# 如何在一个范围内生成加密安全的随机整数?

C# 如何在一个范围内生成加密安全的随机整数?,c#,random,cryptography,C#,Random,Cryptography,我必须在给定范围内为生成密码的程序生成一个统一、安全的随机整数。现在我用这个: RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); byte[] rand = new byte[4]; rng.GetBytes(rand); int i = BitConverter.ToUInt16(rand, 0); int result = i%max; //max is the range's upper bound (the

我必须在给定范围内为生成密码的程序生成一个统一、安全的随机整数。现在我用这个:

RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] rand = new byte[4];
rng.GetBytes(rand);
int i = BitConverter.ToUInt16(rand, 0);
int result = i%max;   //max is the range's upper bound (the lower is 0)

此方法用于加密目的是否安全?如果没有,我应该怎么做?

您可以查看CryptoRandom类,它是Stephen Toub和Shawn Farkas的原始版本。在这个类中,它们实现了几个似乎是加密安全的随机生成器

我在我的项目中使用了以下版本来生成随机整数

public class RandomGenerator
{
    readonly RNGCryptoServiceProvider csp;

    public RandomGenerator()
    {
        csp = new RNGCryptoServiceProvider();
    }

    public int Next(int minValue, int maxExclusiveValue)
    {
        if (minValue >= maxExclusiveValue)
            throw new ArgumentOutOfRangeException("minValue must be lower than maxExclusiveValue");

        long diff = (long)maxExclusiveValue - minValue;
        long upperBound = uint.MaxValue / diff * diff;

        uint ui;
        do
        {
            ui = GetRandomUInt();
        } while (ui >= upperBound);
        return (int)(minValue + (ui % diff));
    }

    private uint GetRandomUInt()
    {
        var randomBytes = GenerateRandomBytes(sizeof(uint));
        return BitConverter.ToUInt32(randomBytes, 0);
    }

    private byte[] GenerateRandomBytes(int bytesNumber)
    {
        byte[] buffer = new byte[bytesNumber];
        csp.GetBytes(buffer);
        return buffer;
    }
}

在公认的答案中有两个问题

  • 它不能正确处理一次性csp
  • 当minvalue等于maxvalue时,它会抛出错误(标准随机方法不会)
使用系统;
使用System.Security.Cryptography;
命名空间CovidMassTesting.Helpers
{
/// 
///安全随机发生器
/// 
/// https://stackoverflow.com/questions/42426420/how-to-generate-a-cryptographically-secure-random-integer-within-a-range
/// 
公共类随机生成器:IDisposable
{
专用只读RNGCryptoServiceProvider csp;
/// 
///建造师
/// 
公共随机生成器()
{
csp=新的RNGCryptoServiceProvider();
}
/// 
///获取随机值
/// 
/// 
/// 
/// 
public int Next(int minValue,int maxExclusiveValue)
{
如果(minValue==maxExclusiveValue)返回minValue;
if(最小值>最大排他值)
{
抛出新ArgumentOutOfRangeException($“{nameof(minValue)}必须低于{nameof(maxExclusiveValue)}”);
}
var diff=(长)maxExclusiveValue-最小值;
var上限=uint.MaxValue/diff*diff;
uint用户界面;
做
{
ui=GetRandomUInt();
}while(ui>=上限);
返回值(int)(minValue+(ui%diff));
}
私有uint GetRandomUInt()
{
var randomBytes=GenerateRandomBytes(sizeof(uint));
返回BitConverter.ToUInt32(随机字节,0);
}
专用字节[]GeneratorAndomBytes(int字节数)
{
var buffer=新字节[字节数];
GetBytes(缓冲区);
返回缓冲区;
}
私人住宅;
/// 
///消费者可调用的Dispose模式的公共实现。
/// 
公共空间处置()
{
处置(真实);
总干事(本);
}
/// 
///Dispose模式的受保护实现。
/// 
/// 
受保护的虚拟void Dispose(bool disposing)
{
如果(_)
{
返回;
}
如果(处置)
{
//处置托管状态(托管对象)。
csp?.Dispose();
}
_这是真的;
}
}
}
用法
//
///生成一个随机密码
///遵守给定的强度要求。
/// 
///有效的PasswordOptions对象
///包含密码强度要求。
///随机密码
公共静态字符串生成器域密码(PasswordOptions opts=null)
{
if(opts==null)opts=newpasswordoptions()
{
所需长度=10,
所需的唯一字符数=4,
RequireDigit=true,
RequireLowercase=true,
RequireNonAlphanumeric=真,
RequireUppercase=true
};
字符串[]随机字符=new[]{
“ABCDEFGHJKLMNOPQRSTUVWXYZ”,//大写
“abcdefghijkmnopqrstuvwxyz”,//小写
“0123456789”,//位
“!@$?\”//非字母数字
};
使用RandomGenerator rand=新的RandomGenerator();
列表字符=新列表();
如果(选择RequireUppercase)
字符插入(rand.Next(0,字符计数),
随机字符[0][rand.Next(0,随机字符[0].Length]);
if(opts.requirelLowercase)
字符插入(rand.Next(0,字符计数),
随机字符[1][rand.Next(0,随机字符[1].Length]);
如果(选择所需数字)
字符插入(rand.Next(0,字符计数),
随机字符[2][rand.Next(0,随机字符[2].Length]);
if(选择需求图表)
字符插入(rand.Next(0,字符计数),
随机字符[3][rand.Next(0,随机字符[3].Length]);
对于(int i=chars.Count;i
我注意到您正在生成一个4字节的随机序列,但随后将其转换为一个2字节的整数(
ushort
/
UInt16
)。可能会生成一个2字节序列或将4字节序列转换为
uint
/
UInt32
。我不确定此实例中的模是否会影响加密安全性。这篇文章似乎表明您所做的非常好:。或者按照这篇文章创建一个0到1之间的加密安全的
double
,然后将结果乘以上限?模不会导致加密安全的随机数。有很多关于o的例子