---
title: "Beyond the Random Class: Cryptographic Randomness in .NET 6+ — Ivan Kahl"
description: "Security-sensitive code needs true randomness. Here, .NET's RandomNumberGenerator class is used to produce cryptographically secure random values instead of Random."
url: https://ivankahl.com/blog/beyond-the-random-class-cryptographic-randomness-in-net-6/
published: 2025-01-06T00:00:00.000Z
tags:
  - "Guides"
  - "C#"
  - ".NET"
  - ".NET 6"
  - "Security"
---

06 Jan 2025 · 7 min read

# Beyond the Random Class: Cryptographic Randomness in .NET 6+

Security-sensitive code needs true randomness. Here, .NET's RandomNumberGenerator class is used to produce cryptographically secure random values instead of Random.

We live in a naturally unpredictable world. Shuffling a deck of cards, picking lottery numbers, and weather patterns are all random. Software often needs to mimic this unpredictability, such as spawning enemies at random intervals in a game, selecting a random sample of data for training a machine learning model, or generating a random OTP code when authenticating a user. Computers, however, are deterministic machines. Given a particular input, the computer will always produce the same output. This makes replicating the unpredictability we live in a challenge in code.

C# offers a couple of APIs for generating random numbers, with the `Random` class being a common choice. However, while useful in many scenarios, its implementation isn’t always appropriate, especially in security contexts where its predictability can be exploited. In those cases, alternatives like the `RandomNumberGenerator`, which offer a higher degree of unpredictability, are better suited.

In this article, you will learn how the `Random` class functions in C#, when it’s appropriate to use, when and why it’s not always suitable, and what alternatives to consider.

## Why Computers Aren’t Truly Random

Computers follow a strict set of instructions to perform a task. Given the same input, the process will yield the same output. This is great for computers, and it’s what’s needed for them to perform computations accurately.

When it comes to randomness, the computer has to cheat by relying on algorithms that generate seemingly random numbers. These algorithms are called Pseudo-Random Number Generators (PRNGs) and consist of mathematical formulas to produce a sequence of numbers that appear random but are still deterministic. PRNGs start with a *seed* value, which is the starting point for the sequence generation. Given the same seed value, a PRNG will always produce the same sequence of numbers. While not completely random, this predictability can sometimes be helpful where reproducibility is desired, like in unit tests or where reproducing the sequence carries little risk.

![Animation illustrating how PRNGs can repeat the same sequence with the same seed.](https://ivankahl.com/_astro/random.BKZQ2Wjn_Z26SC3i.webp)

However, there are cases where you don’t want this reproducibility. If an attacker can figure out the seed used to generate the number sequence, they can “predict” the next numbers. In specific contexts, like security, this can result in a system getting compromised. For example, if you use a PRNG to generate OTP codes for authentication, an attacker could compromise the system by figuring out the seed and generating the same codes. This is why true randomness, which is unpredictable, is essential for security-sensitive tasks.

> **Did you know?**
> 
> Cloudflare, one of the world’s leading CDN providers, generates completely random encryption keys using a wall of 100 lava lamps. A camera mounted to capture all the lava lamps takes regular pictures of the lamps and sends them to Cloudflare servers, where the image pixels are used to generate the next encryption key. Read more about it on [their blog](https://www.cloudflare.com/learning/ssl/lava-lamp-encryption/).

## The Random Class: “Good-Enough” Randomness

The `Random` class is the de facto way of generating random numbers in most applications. It’s a PRNG that’s been around since the beginning of the .NET Framework. Although its underlying implementation has changed over the years, its API has remained relatively unchanged. It’s intuitive, efficient, and works well in most cases.

### Generating Random Integers

To use it, all you need to do is create a new instance of the class and then get the next random numbers:

```csharp
var random = new Random();
Console.WriteLine(random.Next());
Console.WriteLine(random.Next());

// Output:
// 1472324096
// 2138638347
```

The `Next()` method generates a non-negative random integer. You can also pass in parameters to restrict the result if necessary:

```csharp
var random = new Random();

// Generate random integer no larger than 9
Console.WriteLine(random.Next(10));

// Generate random integer between 10 and 19 (inclusive)
Console.WriteLine(random.Next(10, 20));

// Output:
// 7
// 14
```

When using the overloads, the start number is inclusive (if specified), and the end number is exclusive.

The `Random` class also [has methods to generate random bytes, doubles, longs, and even pick random items from an array](https://learn.microsoft.com/en-us/dotnet/api/system.random?view=net-9.0#methods).

### Seeding the Generator

You’ll notice that no seed value was specified when instantiating the `Random` class above. When one isn’t specified, .NET will generate the seed for you. How that seed is generated has changed over the years. In .NET Framework, the current time is used to create the seed. However, .NET Core and 5+ have improved on this by making an interop call to the operating system to generate a random seed using hardware, which is much less predictable.

You can pass in a seed using one of the constructor overloads. This is useful if you want to control the sequence of numbers generated. The snippet below demonstrates this:

```csharp
// Use 78 as the seed
var random = new Random(78);

Console.WriteLine(random.Next());
Console.WriteLine(random.Next());

// Output:
// 1020951901
// 1511780237
```

You should get the same output as the snippet if you run the code below.

### When To Use It

The `Random` class is simple and very efficient since all the numbers are generated by software (and not hardware, as you’ll see in the next section). The generated numbers are spread across your desired range and will seem random enough for most applications. An end user shouldn’t pick up any patterns.

You’ll want to avoid it, though, if being able to predict values will compromise the security of your application or the integrity of your data. For example, generating password salts, nonces, and OTPs should not use the `Random` class since predicting those values will be disastrous. Similarly, you might not want to use the `Random` class for applications like gambling and picking winners for a draw since predicting the numbers could ruin the integrity of the game.

### Warning on Instantiation and Thread Safety

The official Microsoft documentation advises [not to re-instantiate the `Random` class every time you need a number](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-random#avoid-multiple-instantiations). This makes sense, especially if you’re working with .NET Framework, since seeds are generated using the system time. Two generators can have the same seed value if they are instantiated close together.

However, even if you’re working in modern .NET (i.e., .NET 5 and up), re-instantiating the generator doesn’t make sense. The generator is [guaranteed to choose from a range of numbers with equal probability](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-random), meaning no number is more likely to be generated than another. This is good! But you lose that guarantee if you re-instantiate the `Random` class every time.

Also, keep in mind that the `Random` class [**is not thread-safe**](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-random#thread-safety). To overcome this challenge, the [documentation demonstrates how you can use locks](https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-random#thread-safety) to make sure the `Random` object is never called illegally.

## The RandomNumberGenerator Class: Cryptographically Secure Randomness

Since computers are deterministic, generating truly random numbers is difficult. You need to incorporate [entropy](https://en.wikipedia.org/wiki/Entropy) into the algorithm. This entropy typically comes from something in real life that exhibits random, unpredictable behaviour. Cloudflare gets entropy from a wall of lava lamps; C# gets entropy from the operating system, which, in turn, relies on underlying hardware for randomness. You can access this more secure random number generator using the `RandomNumberGenerator` class.

Microsoft [recommends using the static implementation of the class](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.randomnumbergenerator?view=net-9.0#remarks), which will be demonstrated in this article.

### Generating Random Integers

Unlike the `Random` class above, you need to specify the upper bound for the number you want to generate with the `RandomNumberGenerator` class. Another overload also lets you choose the lower bound:

```csharp
// Get a cryptographically random number between 0 and 9
Console.WriteLine(RandomNumberGenerator.GetInt32(10));

// Get a cryptographically random number between 10 and 19
Console.WriteLine(RandomNumberGenerator.GetInt32(10, 20));

// Output:
// 8
// 16
```

Notice how the output is similar to our previous example, where we used the `Random` class. However, these are far less predictable, and because of entropy, it’s practically impossible to repeat the sequence of numbers generated.

### Generating Random Strings (.NET 8+)

The release of .NET 8 introduced several new helper methods to the `RandomNumberGenerator` class. One such method lets you provide a source string and desired length and then generates a random string of that length using the characters in the source string:

```csharp
var availableCharacters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";

// Get a cryptographically random string 20 characters long
Console.WriteLine(RandomNumberGenerator.GetString(availableCharacters, 20));

// Output:
// "5LZFSEVZTPI7LTFTPIJJ"
```

This is useful if you need to create passwords or OTPs.

### Generating Random Hexadecimal Strings (.NET 8+)

The [hexadecimal number system](https://www.techtarget.com/whatis/definition/hexadecimal) lets you represent numbers in base 16. It does this using characters `0-9` and `A-F`. From .NET 8 and up, you can generate random hexadecimal numbers using the `GetHexString` static method on the `RandomNumberGenerator` class:

```csharp
// Get a cryptographically random hex string 5 characters long with 
// uppercase characters
Console.WriteLine(RandomNumberGenerator.GetHexString(5));

// Get a cryptographically random hex string 8 characters long with
// lowercase characters.
Console.WriteLine(RandomNumberGenerator.GetHexString(8, lowercase: true));

// Output:
// "F23BB"
// "7c92dfa5"
```

### When To Use It

After seeing `RandomNumberGenerator` in action, you might wonder why you would use the `Random` class. But wait! As great as the `RandomNumberGenerator` class is, it has additional overhead. Whenever it generates a number, it calls the operating system to add entropy. This call does make the `RandomNumberGenerator` much slower, which you’ll see in this [benchmark](https://khalidabuhakmeh.com/creating-random-numbers-with-dotnet-core#the-problem-with-randomnumbergenerator).

The additional overhead means you should only use the `RandomNumberGenerator` when you actually need cryptographically random numbers. For any other use case, stick to the `Random` class.

## Conclusion

This article explored the intricacies of generating random numbers in code, focusing specifically on the `Random` and `RandomNumberGenerator` classes in C#. You first learned how computers are deterministic and need to use PRNGs to generate seemingly random numbers. You then saw how to pick random numbers from the `Random` class, a PRNG generator. While simple and efficient, the `Random` class has limitations, especially regarding cryptographic operations.

You were then introduced to the `RandomNumberGenerator`, which uses entropy from the underlying operating system to generate cryptographically secure numbers which are practically impossible to predict. The article also demonstrated newer methods added to the class in .NET 8 and up, simplifying generating random strings and hexadecimal numbers.

As I looked through the source code for these classes, it’s clear that very clever engineers have done much of the heavy lifting for us. However, it’s still essential to understand how these classes work to use them effectively in your code base.

If you have any comments or feedback, please leave a comment below.

Tags: [Guides](https://ivankahl.com/blog/tags/guides/), [C#](https://ivankahl.com/blog/tags/csharp/), [.NET](https://ivankahl.com/blog/tags/dotnet/), [.NET 6](https://ivankahl.com/blog/tags/dotnet-6/), [Security](https://ivankahl.com/blog/tags/security/)
