using RNGCryptoServiceProvider to generate random

2019-02-01 22:51发布

I'm using this code to generate random strings with given length

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();
}

However, I read that RNGCryptoServiceProvideris more secure than Random class. How can I implement RNGCryptoServiceProvider to this function. It should use valid string just like this function.

标签: c# random
8条回答
叼着烟拽天下
2楼-- · 2019-02-01 23:37

The RNGCryptoServiceProvider returns random numbers in the form of bytes, so you need a way to get a more convenient random number from it:

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;
}

Then you can use that in your method:

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();
}

(I made the method static, as it doesn't use any instance data.)

查看更多
戒情不戒烟
3楼-- · 2019-02-01 23:40

note: I am aware of OP's use-case deviation, but I think it might help others who have a slightly different use-case

A lot can be said about the reasons to convert a cryptographic byte array into a string, but usually it is for some sort of serialization purposes; and hence, in that case: the selected character set is arbitrary.

So, if, and only if the former is true AND the length of the string doesn't required to be optimized; you can use a simple hexadecimal representation of the byte array like this:

//note: since the choice of characters [0..9a..zA...Z] is arbitrary, 
//limiting to [0..9,A..F] would seem to be a really big problem   if it can be compensated
//by the length.

var rnd = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
rnd.GetBytes(buf);

//gives a "valid" range of: "0123456789ABCDEF"   
foreach (byte b in buf)
    sb.AppendFormat("{0:x2}", b);

//sb contains a RNGCryptoServiceProvider based "string"

Now you'll say: but wait: these are only 16 characters where OP's sequence has 62. Your string will be a lot longer.

"Yes", I will answer, "and if that's a problem, why don't you pick 256 easy-to-read-and-serrializable-characters... or perhaps 64" ;-)

As @Guffa stated; using % is forbidden unless it doesn't alter the distribution. To make this happen, given a evenly distributed set, the subset must fit exactly x times in the original set.

So, expanding your initial valid set with 2 gives a valid result (because: 256 / 64 = 4).

The code would be:

//note: added + and / chars. could be any of them
const string valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/";

var rnd = new RNGCryptoServiceProvider();
var sb = new StringBuilder();
var buf = new byte[10]; //length: should be larger
rnd.GetBytes(buf); //get the bytes

foreach (byte b in buf)
    sb.Append(valid[b%64]);

Please note: in all the answers, including this one, the sub-set is smaller than the 256 possibilities of the byte. This means there is less information available. This means if you have your string with 4 chars, it's easier to crack the original 4 byte result of the RNGCryptoServiceProvider.

So... now you say: "Why not use 64 base encoding?", well, if it's suits you, but be careful with the trailing =, see Base64 on Wikipedia:

var rnd = new RNGCryptoServiceProvider();
var buf = new byte[60]; //length not randomly picked, see 64Base, wikipedia
rnd.GetBytes(buf);
string result = Convert.ToBase64String(buf);

Please note²: A typical use case is some url token. Please note that the + sign is invalid as such a character.

查看更多
登录 后发表回答