Safeguard
Application Security•

How do you generate secure random numbers in Kotlin?

On the JVM, Kotlin code should use java.security.SecureRandom for tokens and keys. Learn how it seeds, when to use getInstanceStrong, and what to avoid.

Safeguard Security Team
4 min read

In Kotlin on the JVM, use java.security.SecureRandom for tokens, keys, salts and nonces. Do not use kotlin.random.Random or java.util.Random for secrets. Create one SecureRandom, reuse it, call nextBytes to fill a byte array, and encode the bytes for where they will travel.

Why not Kotlin's own Random?

Kotlin has its own Random class in the standard library, and the JVM has java.util.Random. Both are meant for general purpose uses like shuffling a list or picking a test value. Neither is documented as a source for cryptographic secrets, so treat them as unsafe for that job and confirm in the docs of your Kotlin version.

SecureRandom is the class the Java platform provides for security use, and Kotlin calls it directly with no wrapper.

val random = SecureRandom()
val bytes = ByteArray(32)
random.nextBytes(bytes)

The nextBytes method fills the array you give it. It throws a NullPointerException if the array is null, which Kotlin's non null types already prevent.

How does SecureRandom get its seed?

The Oracle Javadoc says a newly created PRNG based SecureRandom is not seeded, unless you construct it with a seed. The first call to nextBytes seeds it from an implementation specific entropy source. This self seeding does not happen if you call setSeed first.

That detail is the source of a classic bug. setSeed supplements the existing seed rather than replacing it, so repeated calls never reduce randomness. But a developer who writes SecureRandom().apply { setSeed(System.currentTimeMillis()) } before first use can end up with an instance seeded from a guessable value instead of the system source. Do not call setSeed with predictable data. Let the default seeding run.

The docs also describe reseed(), which pulls fresh entropy from the entropy source. Not every implementation supports it, and an unsupported call throws UnsupportedOperationException.

When do you need getInstanceStrong?

SecureRandom.getInstanceStrong() returns an instance chosen from the algorithms and providers listed in the securerandom.strongAlgorithms security property. Every Java platform must provide at least one. It throws NoSuchAlgorithmException if none is available, and it has existed since Java 1.8.

The Javadoc warns that generateSeed, reseed and nextBytes may block while entropy is gathered, for example when the source is /dev/random. That makes getInstanceStrong a fit for rare, high value operations such as generating a long lived key, and a poor fit for a request path that creates a token on every call. For per request tokens, a default instance is the usual choice.

Is it safe to share one instance?

Yes. The Javadoc says SecureRandom objects are safe for concurrent use by multiple threads. If a provider does not advertise thread safety, the class synchronizes access to the underlying methods. Heavy contention is possible under load, so measure before you build a pool of instances. In a Kotlin project, a top level private val or an object holding one instance is enough.

A constructed review example

A mobile backend written in Kotlin creates invite codes with (1..8).map { chars.random() }.joinToString(""). The random() call on a collection uses the default Kotlin Random. The codes look random, but they come from a generator not meant for secrets, and eight characters from a 36 symbol alphabet is a modest space.

The fix: draw 16 bytes with SecureRandom, encode them with Base64.getUrlEncoder().withoutPadding(), store a hash, and limit attempts per account.

What to do

  1. Search for Random(, .random(), nextInt and java.util.Random near token, key, nonce and salt.
  2. Create one shared SecureRandom and fill byte arrays with nextBytes.
  3. Never call setSeed with a timestamp or an ID.
  4. Use getInstanceStrong only for rare key generation, and test for blocking.
  5. Encode bytes with Base64.getUrlEncoder() or hex, and store digests of tokens.
  6. Use a vetted library such as the JDK's javax.crypto with an authenticated mode, and a fresh nonce per message.

Safeguard's SAST scan can flag weak randomness in source code. See the SAST code scan demo.

Quick check

Is it safe to call setSeed with the current time?

No. Per the Javadoc, calling setSeed before first use prevents self seeding, so a predictable value can weaken the output. Let the default seeding run.

Can multiple threads share one SecureRandom?

Yes. The Javadoc says SecureRandom objects are safe for concurrent use by multiple threads.

Sources

Last reviewed: 18 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.