C# 使用RNGCryptoServiceProvider生成随机字符串
我用这段代码生成给定长度的随机字符串C# 使用RNGCryptoServiceProvider生成随机字符串,c#,random,C#,Random,我用这段代码生成给定长度的随机字符串 public string RandomString(int length) { const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; StringBuilder res = new StringBuilder(); Random rnd = new Random(); while (0 < lengt
public string RandomString(int length)
{
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
Random rnd = new Random();
while (0 < length--)
{
res.Append(valid[rnd.Next(valid.Length)]);
}
return res.ToString();
}
公共字符串RandomString(int-length)
{
常量字符串valid=“abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz1234567890”;
StringBuilder res=新的StringBuilder();
随机rnd=新随机();
而(0<长度--)
{
res.Append(有效[rnd.Next(有效.Length)]);
}
return res.ToString();
}
然而,我读到,
RNGCryptoServiceProvider
比Random
类更安全。如何实现此函数的RNGCryptoServiceProvider
。它应该像此函数一样使用valid
字符串。RNGCryptoServiceProvider以字节的形式返回随机数,因此您需要一种方法从中获取更方便的随机数:
public static int GetInt(RNGCryptoServiceProvider rnd, int max) {
byte[] r = new byte[4];
int value;
do {
rnd.GetBytes(r);
value = BitConverter.ToInt32(r, 0) & Int32.MaxValue;
} while (value >= max * (Int32.MaxValue / max));
return value % max;
}
然后,您可以在方法中使用:
public static string RandomString(int length) {
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
using (RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider()) {
while (length-- > 0) {
res.Append(valid[GetInt(rnd, valid.Length)]);
}
}
return res.ToString();
}
(我将该方法设置为静态,因为它不使用任何实例数据。)由于RNGRandomNumberGenerator只返回字节数组,因此必须这样做:
static string RandomString(int length)
{
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
StringBuilder res = new StringBuilder();
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
byte[] uintBuffer = new byte[sizeof(uint)];
while (length-- > 0)
{
rng.GetBytes(uintBuffer);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
res.Append(valid[(int)(num % (uint)valid.Length)]);
}
}
return res.ToString();
}
但是请注意,这有一个缺陷,62个有效字符等于595419631038687520880611235991756位(log(62)/log(2)),因此它不会在32位数字(uint)上平均分割
这有什么后果?
因此,随机输出将不均匀。值较低的字符出现的可能性更大(只是很小的一部分,但仍然会发生)
更准确地说,一个有效数组的前4个字符000000 144354999840239435286%的可能性更高
为了避免这种情况,您应该使用将平均分成64的数组长度(考虑在输出上使用,因为您可以将64位与6个字节完全匹配。您需要使用
RNGCryptoServiceProvider
生成随机字节
,并仅将有效的字节附加到返回的字符串中:
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static string GetRandomString(int length)
{
string s = "";
using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider())
{
while (s.Length != length)
{
byte[] oneByte = new byte[1];
provider.GetBytes(oneByte);
char character = (char)oneByte[0];
if (valid.Contains(character))
{
s += character;
}
}
}
return s;
}
您还可以使用模运算,以避免跳过无效的字节值,但每个字符的机会不会均等。请参阅
我相信我以前已经用一个安全的实现、无偏见和良好的性能回答了这个问题。如果是,请评论
看着Tamir的答案,我认为最好使用模运算,但去掉字节值中不完整的剩余部分。我现在也在写这个答案(可能再次),因为我需要将这个解决方案引用给对等方
方法1
- 支持不大于0-255的范围。但可以回退到接近2(速度稍慢)
- 每个值始终使用一个字节
- 截断不完整的余数
if(buffer[i]>=exclusiveLimit)
- 调整所需的量程大小。在超出排他限值后,模数保持完全平衡
- (使用位掩码而不是模数是一种较慢的方法)
- 如果你想要一个0-16的范围(即17个不同的值),那么17可以放入一个字节15次。有一个值必须丢弃[255],否则模数就可以了
方法1的代码
const string lookupCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
static void TestRandomString()
{
Console.WriteLine("A random string of 100 characters:");
int[] randomCharacterIndexes = new int[100];
SecureRangeOriginal(randomCharacterIndexes, lookupCharacters.Length);
var sb = new StringBuilder();
for (int i = 0; i < randomCharacterIndexes.Length; i++)
{
sb.Append(lookupCharacters[randomCharacterIndexes[i]]);
}
Console.WriteLine(sb.ToString());
Console.WriteLine();
}
static void SecureRangeOriginal(int[] result, int maxInt)
{
if (maxInt > 256)
{
//If you copy this code, you can remove this line and replace it with `throw new Exception("outside supported range");`
SecureRandomIntegerRange(result, 0, result.Length, 0, maxInt); //See git repo for implementation.
return;
}
var maxMultiples = 256 / maxInt; //Finding the byte number boundary above the provided lookup length - the number of bytes
var exclusiveLimit = (maxInt * maxMultiples); //Expressing that boundary (number of bytes) as an integer
var length = result.Length;
var resultIndex = 0;
using (var provider = new RNGCryptoServiceProvider())
{
var buffer = new byte[length];
while (true)
{
var remaining = length - resultIndex;
if (remaining == 0)
break;
provider.GetBytes(buffer, 0, remaining);
for (int i = 0; i < remaining; i++)
{
if (buffer[i] >= exclusiveLimit)
continue;
var index = buffer[i] % maxInt;
result[resultIndex++] = index;
}
}
}
}
const string lookupCharacters=“abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz1234567890”;
静态void TestRandomString()
{
WriteLine(“100个字符的随机字符串:”);
int[]randomCharacterIndex=新int[100];
SecureRangeOriginal(RandomCharacterIndex、lookupCharacters.Length);
var sb=新的StringBuilder();
for(int i=0;i256)
{
//如果复制此代码,则可以删除此行并将其替换为“抛出新异常”(“超出支持范围”)`
SecureRandomIntegerage(result,0,result.Length,0,maxInt);//有关实现,请参阅git repo。
返回;
}
var maxMultiples=256/maxInt;//查找提供的查找长度(字节数)上方的字节数边界
var exclusiveLimit=(maxInt*maxMultiples);//将该边界(字节数)表示为整数
变量长度=结果长度;
var resultIndex=0;
使用(var provider=new RNGCryptoServiceProvider())
{
var buffer=新字节[长度];
while(true)
{
剩余变量=长度-结果索引;
如果(剩余==0)
打破
GetBytes(缓冲区,0,剩余);
对于(int i=0;i<剩余;i++)
{
if(缓冲区[i]>=exclusiveLimit)
继续;
var指数=缓冲区[i]%最大值;
结果[resultIndex++]=索引;
}
}
}
}
方法2
- 技术范围从0到ulong。可以支持最大值
- 将RNGCryptoServiceProvider字节视为位流
- 计算每个数字所需的base2位长度
- 从随机比特流中获取下一个数字
- 如果该数字仍然大于所需范围,则放弃
结果:
- 有关测试线束的最新结果,请参见存储库
- 这两种方法似乎都有一个适当平衡的数字分布
- 方法1的速度更快[859ms],但它只对单个字节有效
- 方法2比方法1慢一点[3038ms],但它可以跨字节边界工作。它丢弃更少的位,这在随机流输入成为瓶颈(例如,不同的算法)时非常有用
- 这两种方法的混合实现了两个方面的最佳效果:
private string sifreuretimi(int sayı) //3
{
Random rastgele = new Random();
StringBuilder sb = new StringBuilder();
char karakter1 = ' ', karakter2 = ' ', karakter3 = ' ';
int ascii1, ascii2, ascii3 = 0;
for (int i = 0; i < sayı/3; i++)
{
ascii1 = rastgele.Next(48,58);
karakter1 = Convert.ToChar(ascii1);
ascii2 = rastgele.Next(65, 91);
karakter2 = Convert.ToChar(ascii2);
ascii3 = rastgele.Next(97, 123);
karakter3 = Convert.ToChar(ascii3);
sb.Append(karakter1);
sb.Append(karakter2);
sb.Append(karakter3);
}
return sb.ToString();
}
//note: using text as HEX makes the result longer
var crypt = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
crypt.GetBytes(buf);
//gives a "valid" range of: "0123456789ABCDEF"
foreach (byte b in buf)
sb.AppendFormat("{0:x2}", b); //applies "text as hex" encoding
//sb contains a RNGCryptoServiceProvider based "string"
//note: added + and / chars. could be any of them
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/";
var crypt = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
crypt.GetBytes(buf); //get the bytes
foreach (byte b in buf)
sb.Append(valid[b%64]);
var crypt = new RNGCryptoServiceProvider();
// = padding characters might be added to make the last encoded block
// contain four Base64 characters.
// which is actually an additional character
var buf = new byte[10];
crypt.GetBytes(buf);
//contains a RNGCryptoServiceProvider random string, which is fairly readable
//and contains max 65 different characters.
//you can limit this to 64, by specifying a different array length.
//because log2(64) = 6, and 24 = 4 x 6 = 3 x 8
//all multiple of 3 bytes are a perfect fit. (e.g.: 3, 6, 15, 30, 60)
string result = Convert.ToBase64String(buf);
private static string GenerateRandomSecret()
{
var validChars = Enumerable.Range('A', 26)
.Concat(Enumerable.Range('a', 26))
.Concat(Enumerable.Range('0', 10))
.Select(i => (char)i)
.ToArray();
var randomByte = new byte[64 + 1]; // Max Length + Length
using (var rnd = new RNGCryptoServiceProvider())
{
rnd.GetBytes(randomByte);
var secretLength = 32 + (int)(32 * (randomByte[0] / (double)byte.MaxValue));
return new string(
randomByte
.Skip(1)
.Take(secretLength)
.Select(b => (int) ((validChars.Length - 1) * (b / (double) byte.MaxValue)))
.Select(i => validChars[i])
.ToArray()
);
}
}
public static string RandomString(int length)
{
const string alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
var res = new StringBuilder(length);
using (var rng = new RNGCryptoServiceProvider())
{
int count = (int)Math.Ceiling(Math.Log(alphabet.Length, 2) / 8.0);
Debug.Assert(count <= sizeof(uint));
int offset = BitConverter.IsLittleEndian ? 0 : sizeof(uint) - count;
int max = (int)(Math.Pow(2, count*8) / alphabet.Length) * alphabet.Length;
byte[] uintBuffer = new byte[sizeof(uint)];
while (res.Length < length)
{
rng.GetBytes(uintBuffer, offset, count);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
if (num < max)
{
res.Append(alphabet[(int) (num % alphabet.Length)]);
}
}
}
return res.ToString();
}