Crypto work: generating secure random bytes with C#

Posted: (EET/GMT+2)

 

Security work and cryptography often require real random bytes to work with. If such a need arises, the short version is: do not use System.Random.

The classic System.Random class is useful for simulations, test data, games, and other cases where repeatable pseudo-random values are acceptable. It is not meant for generating security keys, tokens, passwords, or other secrets.

For cryptographic use, use the class RandomNumberGenerator from the System.Security.Cryptography namespace instead. Let's take an example:

using System.Security.Cryptography;

byte[] key = RandomNumberGenerator.GetBytes(32);

This creates 32 random bytes, which equals to 256 bits of random data. Sometimes, that's all you need, but if it happens a text value would be more suitable, just, encode the bytes. For example, Base64:

using System.Security.Cryptography;

byte[] tokenBytes = RandomNumberGenerator.GetBytes(32);
string token = Convert.ToBase64String(tokenBytes);
Console.WriteLine(token);

Or hexadecimal:

using System.Security.Cryptography;

byte[] tokenBytes = RandomNumberGenerator.GetBytes(32);
string token = Convert.ToHexString(tokenBytes);

Console.WriteLine(token);

For older target frameworks, or when you already have the buffer, as the generator to fill the byte array instead:

using System.Security.Cryptography;

byte[] key = new byte[32];
RandomNumberGenerator.Fill(key);

The important point is not the exact syntax. The important point is that the random data comes from a cryptographically secure random number generator. So, do not do this for secrets:

Random random = new Random();
byte[] key = new byte[32];
random.NextBytes(key);

This is not appropriate for cryptographic keys or security tokens.

Pseudo-random number generators are deterministic algorithms. They can be very useful, but cryptographic use requires stronger properties, especially unpredictability. Wikipedia has good background reading on pseudo-random number generators (PRNGs) and cryptographically secure pseudo-random number generators.

If you need a random integer, use RandomNumberGenerator.GetInt32() instead of trying to build one manually from bytes.

using System.Security.Cryptography;

int value = RandomNumberGenerator.GetInt32(0, 100);

This returns a secure random integer from 0 to 99.

For a six-digit verification code you could do this:

using System.Security.Cryptography;

int code = RandomNumberGenerator.GetInt32(0, 1_000_000);
string codeText = code.ToString("D6");

Console.WriteLine(codeText);

Good defaults to keep in mind:

  • use RandomNumberGenerator.GetBytes for new random byte arrays
  • use RandomNumberGenerator.Fill when filling an existing buffer
  • use RandomNumberGenerator.GetInt32 for bounded secure integers
  • encode bytes with Base64 or hex when a text token is needed
  • use enough bytes for the security level you need.

For many application tokens, 32 random bytes is a reasonable starting point. But for encryption keys, use the key size required by the algorithm or library you are using.

Tip: if a cryptographic API can generate a key for you, prefer that API. For example, do not manually create AES keys unless you know what size and format the API expects.

For security-sensitive data, randomness is not a cosmetic detail. It is part of the security design.

Safer hacking!