Secure Random Number Generation for App Developers
By Sergey Nosov
May 31, 2025
Let us review generating random numbers securely in our applications, especially for cryptographic use cases like token or key generation. Randomness is deceptively tricky: if done wrong, it can break security in subtle ways. We'll first clarify pseudo-random versus true random generation, then look at specialized hardware sources (like Intel RDRAND or TPMs), followed by software-based generators (cryptographic PRNGs). We'll survey cloud services (Azure, AWS) that offer secure random bytes, and finally cover best practices in C# and JavaScript for tasks like generating tokens, password reset codes, or cryptographic keys.
Pseudo-Random vs True Random
Not all "random" numbers are created equal. A pseudorandom number generator (PRNG) is an algorithmic method that produces a sequence of numbers that only appear random. By definition, a PRNG's output is completely determined by its initial seed. In fact, a PRNG is "an algorithm for generating a sequence of numbers whose properties approximate…random numbers" but is not truly random, because it's entirely determined by that seed. For example, common programming libraries (like Python or Excel) use the Mersenne Twister algorithm, which is very fast and has good statistical properties, but if an attacker knows the seed they can predict all output. Cryptographic uses require unpredictability, so typical PRNGs must be cryptographically secure (CSPRNGs) to prevent any prediction.
By contrast, a true random number generator (TRNG) relies on physical, unpredictable events. For instance, it might measure electronic noise, radioactivity, or other quantum effects. WolfSSL describes that "for truly random numbers, the computer must use some external physical variable that is unpredictable, such as radioactive decay of isotopes or airwave static". These physical processes (like thermal or atmospheric noise) are inherently unpredictable at the quantum level, making TRNG output non-deterministic.
Key differences include:
- Predictability: PRNGs are deterministic given their seed, whereas TRNGs are not reproducible. In a PRNG "the computer uses an algorithm… not secure because [it's] deterministic, predictable" if the seed is known. True random values have no algorithmic pattern.
- Reproducibility: PRNGs allow repeating sequences by reusing the seed (useful for simulations). True random sources cannot be replayed exactly.
- Examples: PRNG examples include algorithms like the Mersenne Twister or linear congruential generators. CSPRNG examples include NIST-approved generators like AES-CTR DRBG or SHA-256 HMAC-DRBG. TRNG examples are physical/hardware sources – e.g. thermal noise circuits, radioactive decay, or hardware RNGs built into CPUs or security chips.
In practice, cryptographers often seed a fast PRNG with entropy from a TRNG to get the best of both worlds. But for critical uses (keys, nonces), always assume you need unpredictability.
Hardware Random Number Generators
Modern hardware often includes dedicated true random number generators. These are typically silicon circuits that harvest noise. Some notable examples:
- Intel RDRAND / RDSEED: Intel (and AMD) CPUs provide the
RDRAND
instruction, which returns random numbers from an on-chip hardware RNG seeded by thermal noise. Wikipedia explains: "RDRAND
(for 'read random') is an instruction for returning random numbers from an Intel on-chip hardware random number generator which has been seeded by an on-chip entropy source". AMD's instruction set also supportsRDRAND
, and newer CPUs includeRDSEED
, which gives lower-level access to raw entropy. In other words, when you callRDRAND
orRDSEED
, the CPU hardware is generating (and conditioning) random bits using physical processes inside the chip. These instructions comply with standards and are meant for cryptographic use. - Trusted Platform Module (TPM): A TPM is a secure chip on the motherboard, used for storing cryptographic keys. It also contains a built-in TRNG. Research on TPMs notes that each TPM "also contains a true random number generator designed to be used for cryptographic purposes". In practice, TPMs typically use thermal noise or clock jitter internally to produce randomness. On Linux systems, the TPM can often feed
/dev/random
or use tpm-tools to add entropy. - Hardware Security Modules (HSMs): These are standalone devices (or cloud instances) built for cryptography. Both Azure Key Vault Managed HSM and AWS CloudHSM provide hardware-based key storage and can generate keys internally using their own RNG. For example, the Azure-managed HSM has an API to get random bytes directly from the HSM.
- Specialized RNG chips: Some systems include dedicated RNG chips or microcontrollers (e.g. on IoT devices). These also harvest noise (like avalanche noise in transistors) to provide random bits.
In summary, hardware RNGs (CPU instructions, TPMs, HSMs, etc.) provide high-quality entropy. They are generally fast and compliant with security standards. For instance, Intel's RNG (part of "Intel Secure Key") conditions raw noise through an AES-based PRNG to output 128-bit samples.
Software-Based (Cryptographic) Random Generators
If special hardware is unavailable, software libraries and OS features can still generate secure random numbers. The key is to use a cryptographically secure PRNG (CSPRNG) seeded from some entropy source. The CSPRNG must be unpredictable even if an attacker observes many outputs.
Common software approaches include:
- Operating system random APIs:
- On Linux/Unix,
/dev/urandom
is a kernel CSPRNG seeded by various entropy sources (timing jitter, device interrupts, etc.). It continuously drips out random bytes. - On Windows, CryptoAPI (e.g.
CryptGenRandom
or newerBCryptGenRandom
) provides secure random bytes from the OS. - These OS APIs typically mix system entropy and may incorporate hardware RNGs if available. For example, Windows' CryptoAPI can use Intel RDRAND under the hood if supported.
- On Linux/Unix,
- NIST SP 800-90A DRBGs: Standards-based deterministic generators, such as AES-CTR_DRBG, HMAC_DRBG, or Hash_DRBG (SHA-256). These are deterministic algorithms but are designed to be cryptographically secure. For instance, wolfSSL's FIPS module uses a SHA-256-based Hash_DRBG as its core RNG. NIST SP 800-90A specifies these DRBGs explicitly for generating keys, IVs, nonces, etc.
- Open-source libraries: Many cryptography libraries implement their own CSPRNGs. For example, OpenSSL's
RAND_bytes()
, libsodium'srandombytes_buf()
, or languages' standard libraries (e.g. Python'ssecrets
module) all provide CSPRNG functions. Internally they often use a DRBG or mix multiple entropy sources. - PRNG algorithms: Sometimes one sees algorithms like Fortuna or Blum–Blum–Shub. Fortuna collects entropy into pools and uses AES to generate output. Blum–Blum–Shub is a number-theoretic generator based on factoring (but it's slow). These are more specialized and not as common as the above.
In short, any software method must use an approved CSPRNG. As Wikipedia notes, a cryptographically secure pseudorandom number generator (CSPRNG) is simply "a pseudorandom number generator with properties that make it suitable for use in cryptography". In practice that means its output is unpredictable without knowing the internal state or seed. Common CSPRNG designs (e.g. those in NIST standards) are widely used in crypto libraries.
Randomness in Azure and AWS
Both Azure and AWS offer managed ways to get high-quality random data, often backed by HSMs:
- Azure Key Vault / Managed HSM: Azure Key Vault's Managed HSM provides a
GetRandomBytes
REST API. You can ask it for N random bytes, and the HSM will produce them using its hardware entropy source. This is essentially a cloud HSM RNG call. For example, the REST endpoint isPOST {vaultBaseUrl}/rng?api-version=7.4
with a{ "count": <N> }
body. The response is cryptographically strong random data. - AWS KMS: AWS Key Management Service has a
GenerateRandom
API. CallingGenerateRandom
returns a cryptographically secure random byte string. By default, KMS itself (a managed HSM under the hood) produces the randomness. You can also specify that the generation be done in a linked AWS CloudHSM cluster (CustomKeyStoreId
) if you want dedicated hardware. AWS CLI example:
This returns 64 random bytes (with no key involved). The AWS docs emphasize this is cryptographically secure random data.aws kms generate-random --number-of-bytes 64
- AWS CloudHSM: If you manage a CloudHSM instance, it too has a built-in hardware RNG. You'd typically use KMS or the HSM SDK to access it.
In practice, if you already use Key Vault or KMS for keys, these services allow you to pull random data without managing seeds. This is useful for e.g. generating symmetric keys or nonces within the cloud environment.
Best Practices in C# and JavaScript
Finally, let's tie this to code. When writing C# or JavaScript, always use the built-in cryptographic RNG, not the general-purpose one:
C# (.NET)
- Use
RandomNumberGenerator
. In .NET (especially .NET 6+), the recommended approach is to use the staticSystem.Security.Cryptography.RandomNumberGenerator
class. For example,RandomNumberGenerator.GetBytes(32)
gives you 32 secure random bytes. The olderRNGCryptoServiceProvider
is now obsolete and should be avoided. As Microsoft docs state, "RNGCryptoServiceProvider is obsolete. To generate a random number, use one of the RandomNumberGenerator static methods instead.". - Generate random integers safely. If you need a random integer (e.g. an OTP or code), use
RandomNumberGenerator.GetInt32
. This method generates a randomint
in a range, using a CSPRNG internally. For example,RandomNumberGenerator.GetInt32(100000, 1000000)
yields a random six-digit number. The docs confirm this is cryptographically strong: "Generates a random integer between 0 (inclusive) and a specified exclusive upper bound using a cryptographically strong random number generator.". - Avoid
System.Random
for security. The built-inRandom
class is not suitable for cryptography. Always use the crypto RNG. - Key generation: When generating cryptographic keys (AES keys, RSA/ECDSA keys), typically you use the library's key generation which in turn uses a secure RNG internally. But if you ever generate salt or nonces, use
RandomNumberGenerator
for entropy. - Example: A secure token generation snippet in C# might look like:
This gives you a random 32-byte token encoded as a string.byte[] tokenBytes = RandomNumberGenerator.GetBytes(32); // 256-bit token
string token = Convert.ToBase64String(tokenBytes);
JavaScript (Browser and Node.js)
- Browser (Web Crypto API): Use
window.crypto.getRandomValues()
. This fills a typed array with secure random values. For example:
The MDN docs explicitly warn that "Math.random() does not provide cryptographically secure random numbers. … Use the Web Crypto API instead, and more precisely the Crypto.getRandomValues() method.". Indeed,let array = new Uint8Array(16);
window.crypto.getRandomValues(array);
// array now contains 16 cryptographically secure random bytesgetRandomValues
"lets you get cryptographically strong random values". Modern browsers also supportcrypto.randomUUID()
to get a random UUID string. - Node.js: Use the built-in
crypto
module. For example,crypto.randomBytes(32)
returns a Buffer of 32 cryptographically secure random bytes. If you need a random integer, Node.js providescrypto.randomInt(min, max)
(synchronously or asynchronously) which yields a uniform random integer in the given range. GeeksforGeeks notes thatcrypto.randomBytes()
generates "cryptographically strong pseudo-random data, essential for creating secure keys, tokens, and identifiers". Example:
Always check the documentation, but by default these use OpenSSL CSPRNG under the hood.const crypto = require('crypto');
let token = crypto.randomBytes(16).toString('hex'); // 32 hex chars
let code = crypto.randomInt(100000, 1000000); // a 6-digit code - Avoid
Math.random()
in JS: For all cryptographic needs, never useMath.random()
. It's not designed to be secure. MDN cautions: "Do not use [Math.random] for anything related to security.". - Libraries: If you're using third-party libraries (e.g. JWT libraries, auth frameworks), ensure they use a secure RNG internally. Many frameworks already use
crypto.getRandomValues
or Node'scrypto
.
Token/password/key generation tips: Use sufficient length of randomness. For instance, at least 128 bits (16 bytes) of entropy for tokens, and 256 bits for high-value keys. Encode binary data safely (e.g. Base64 or hex). Treat these values as secrets (don't log them). When generating things like password reset tokens, also consider time limits and one-use policies, but at the core the random bits must come from a CSPRNG as shown above.
To summarize:
- Use the right tool: hardware RNGs (RDRAND, TPM, HSM) if available; otherwise OS-provided CSPRNG.
- Use high-quality libraries/APIs: Azure Key Vault or AWS KMS for cloud scenarios,
RandomNumberGenerator
in .NET,crypto.randomBytes
orcrypto.getRandomValues
in JS. - Never roll your own PRNG: Always rely on proven cryptographic RNGs.
- Beware of pitfalls: Don't seed a PRNG with time or use non-crypto PRNGs for security tasks.
By following these practices, your app will generate tokens, keys, and codes that are unpredictable to attackers.
References
WolfSSL, "True Random vs. Pseudorandom Number Generation," WolfSSL, July 13, 2021 (updated Oct. 14, 2024), https://www.wolfssl.com/true-random-vs-pseudorandom-number-generation/.
Wikipedia contributors, "Pseudorandom number generator," Wikipedia, last modified Feb. 22, 2025, https://en.wikipedia.org/wiki/Pseudorandom_number_generator.
MDN Web Docs (Mozilla), "Math.random() – JavaScript," accessed May 30, 2025, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random.
MDN Web Docs (Mozilla), "Crypto.getRandomValues() – Web APIs," accessed May 30, 2025, https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues.
Amazon Web Services, "GenerateRandom – AWS Key Management Service (KMS) API Reference," accessed May 30, 2025, https://docs.aws.amazon.com/kms/latest/APIReference/API_GenerateRandom.html.
Microsoft, "Get Random Bytes – REST API (Azure Key Vault)," accessed May 30, 2025, https://learn.microsoft.com/rest/api/keyvault/keys/get-random-bytes.
Wikipedia contributors, "RDRAND," Wikipedia, last modified May 18, 2025, https://en.wikipedia.org/wiki/RDRAND.
Suciu, Alin, and Tudor Carean. "Benchmarking the True Random Number Generator of TPM Chips." arXiv (July 2010), https://arxiv.org/pdf/1008.2223.pdf.
GeeksforGeeks, "Node crypto.randomBytes() Method," last updated May 18, 2025, https://www.geeksforgeeks.org/node-js-crypto-randombytes-method/.
GeeksforGeeks, "Node.js crypto.randomInt() Method," last updated June 8, 2022, https://www.geeksforgeeks.org/node-js-crypto-randomint-method/.
Wikipedia contributors, "Cryptographically secure pseudorandom number generator," Wikipedia, last modified Apr. 16, 2025, https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator.
Microsoft, "RNGCryptoServiceProvider Class (System.Security.Cryptography)," accessed May 30, 2025, https://learn.microsoft.com/dotnet/api/system.security.cryptography.rngcryptoserviceprovider.
Microsoft, "RandomNumberGenerator.GetInt32 Method (System.Security.Cryptography)," accessed May 30, 2025, https://learn.microsoft.com/dotnet/api/system.security.cryptography.randomnumbergenerator.getint32.