3

I've been trying to write a simple helper method in C# that generates a cryptographically-secure random string with any given length and character set. I know that you shouldn't use the Random class if you want cryptographic security. In the past, the RNGCryptoServiceProvider class would always be suggested as an alternative to Random to provide cryptographic safety. That class is now obselete and RandomNumberGenerator is supposed to be used instead.

Now, what I'm confused about is that many answers in this famous question that are using the new RandomNumberGenerator class — such as this one — are using the GetBytes() method, and then doing lots of other stuff afterwards to make things work; but I don't understand why they're not using GetInt32() instead and then using the result as an index in the character set? It seems like that would make this much simpler.

This is what I'm talking about:

public string GenerateRandomString(int length, string charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
{
    char[] result = new char[length];

    for (int i = 0; i < length; i++)
        result[i] = charSet[RandomNumberGenerator.GetInt32(charSet.Length)];

    return new string(result);
}

My question is this: Is this way of generating random strings cryptographically sound or not? The fact that I never saw this being done makes me uncertain, which is why I'm asking this question. And if it isn't, could you please explain why?

Thank you.

Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
  • If it were me, I'd use `GetBytes` and then use the result to generate the string. When I need a random string, I generally don't care about string length, I care about entropy. So I just use GetBytes and convert it to a hex string. If you want to use an arbitrary character set, you could use GetBytes and then figure the length of the character set. If <16, use each nibble (with a modulus operation) to index into the character set. If >=16 and <256, use each byte, same with 64k and each 16 bit int. – Flydog57 Jul 25 '22 at 02:26

1 Answers1

3

From the documentation you have linked:

Cryptographic random number generators create cryptographically strong random values.

Okay, let's now look at GetInt32:

Generates a random integer between 0 (inclusive) and a specified exclusive upper bound using a cryptographically strong random number generator.

(emphasis mine)

Most importantly, it later on states:

This method uses a discard-and-retry strategy to avoid the low value bias that a simple modular arithmetic operation would produce.

So it can be used to reliably produce a random string, yes.

As for the reasoning, well, RNGCryptoServiceProvider did not have GetInt32, it only had GetBytes, so of course old code that used to use RNGCryptoServiceProvider and was then hastily modified to use RandomNumberGenerator instead to fix the obsolete warning won't use the new method.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
  • So the answer is yes? I actually read that part of the documentation, I understand that it means that the numbers `GetInt32` generates are in fact cryptographically-secure, but I don't know **if that means that random strings generated using `GetInt32` in this manner (in my code snippet)** would also be cryptographically-secure? – Arad Alvand Jul 24 '22 at 20:43
  • 1
    Yeah, picking a random number and using that as index is cryptographically secure, **assuming** you want to have a normal distribution of character values. This is not always the case (e.g. password generators that require you to at least have one symbol). – Maarten Bodewes Jul 24 '22 at 22:28