Random 64 character string generator

Sixty-four characters from a 62-symbol alphabet: 381 bits, far past any cryptographic requirement.

G
More options
up to 10,000

Constraints

381 bits, stated honestly

Sixty-four alphanumeric characters give 64 x log2(62) = 381.069 bits. There is no useful attack figure to quote, because no attack of this kind exists at this size and inventing a number would only pretend otherwise.

It is worth being direct about what that means. Nothing in mainstream cryptography needs 381 bits of secret. AES-256 uses 256. A SHA-256 output is 256 bits. NIST mandates a minimum random-bit-generator security strength of 112 bits. A 64-character alphanumeric string is chosen for reasons other than resistance to guessing, and it is better to know which reason applies to you than to assume longer is safer.

Where 64 characters is genuinely required

Almost always because the alphabet is hexadecimal, not alphanumeric. Sixty-four hexadecimal characters is exactly 256 bits, at 4 bits per character with no rounding. A SHA-256 digest is 64 hexadecimal characters. A key sized to match SHA-256 for HMAC is 32 bytes, also written as 64 hexadecimal characters. When a configuration field asks for a 64-character secret, it usually means that value and will reject anything containing a letter above f.

The other genuine reasons are structural rather than cryptographic. A column defined as CHAR(64). A rotation that has to be drop-in against an existing secret of that exact width. A format an upstream service documented and will not change. In each case 64 is a compatibility requirement and the correct response is to match it exactly, alphabet included.

If none of those apply and you simply want a secret nobody will ever guess, the length you want is shorter. Twenty-two alphanumeric characters is 130.99 bits, already past the point where brute force is the threat, and 32 characters at 190.53 bits is the common default for a reason.

If a specification asks for 256 bits, generate 64 hexadecimal characters, not 64 alphanumeric characters. Only one of those is exactly 256 bits, and only one of them will pass a hex validator.

What long values actually cost

Nothing computationally. The costs at this length are handling costs, and they are real. Long secrets get wrapped by terminals and copied back with a newline in the middle. They get truncated by a form field with a maxlength nobody checked, or by a VARCHAR narrower than the value, sometimes without an error. They get line-broken by email clients and by chat applications. They get partially selected on a double click, because a mixed-case alphanumeric run is one word to some UIs and several to others.

The defence is to test the round trip rather than to trust the generator. Generate the value, put it through the exact path it will travel, read it back at the far end, and compare the whole string rather than a prefix. A secret that is silently truncated to 32 characters still works, which is why the failure is usually found much later and by someone else.

A 64-character value also removes any remaining excuse for storing it in plaintext where a person will see it. At this size nobody is memorising or retyping it, so it belongs in a secret manager or an environment variable and nowhere else.

Doing this in your own code

JavaScript
const ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";  // 62 characters

function randomString(length = 64) {
  // Largest multiple of 62 that fits in a uint32. Anything at or above it
  // is discarded, which is what keeps the mapping uniform.
  const limit = Math.floor(2 ** 32 / ALPHABET.length) * ALPHABET.length;
  const buf = new Uint32Array(1);
  let out = "";
  for (let i = 0; i < length; i++) {
    do { crypto.getRandomValues(buf); } while (buf[0] >= limit);
    out += ALPHABET[buf[0] % ALPHABET.length];
  }
  return out;
}

randomString();  // 64 characters

Without the rejection step, 2^32 does not divide evenly by 62 and the first four characters of the alphabet come up slightly more often.

Python 3.6+
import secrets
import string

ALPHABET = string.ascii_letters + string.digits  # 62 characters

"".join(secrets.choice(ALPHABET) for _ in range(64))

secrets.choice goes through randbelow, which rejects out-of-range values internally. random.choice is unbiased as well, but it runs on the Mersenne Twister and must not be used here.

Go
import (
	"crypto/rand"
	"math/big"
)

const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

func RandomString(n int) (string, error) {
	b := make([]byte, n)
	max := big.NewInt(int64(len(alphabet)))
	for i := range b {
		v, err := rand.Int(rand.Reader, max)
		if err != nil {
			return "", err
		}
		b[i] = alphabet[v.Int64()]
	}
	return string(b), nil
}

RandomString(64)

crypto/rand.Int already does rejection sampling, so the result is uniform. math/rand is not a substitute at any size.

PHP 7+
function random_string(int $length): string {
    $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $max = strlen($alphabet) - 1;
    $out = '';
    for ($i = 0; $i < $length; $i++) {
        $out .= $alphabet[random_int(0, $max)];
    }
    return $out;
}

random_string(64);

random_int is CSPRNG-backed and unbiased. rand(), mt_rand() and array_rand() are none of those things.

Java
import java.security.SecureRandom;

static final String ALPHABET =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
static final SecureRandom RNG = new SecureRandom();

StringBuilder sb = new StringBuilder(64);
for (int i = 0; i < 64; i++) {
    sb.append(ALPHABET.charAt(RNG.nextInt(ALPHABET.length())));
}
sb.toString();

nextInt(bound) rejects and redraws when the bound does not divide the range evenly, so no bias is introduced here.

C# / .NET
using System.Security.Cryptography;

const string Alphabet =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

var chars = new char[64];
for (int i = 0; i < chars.Length; i++)
{
    chars[i] = Alphabet[RandomNumberGenerator.GetInt32(Alphabet.Length)];
}
string value = new string(chars);

RandomNumberGenerator.GetInt32 is unbiased. System.Random is not cryptographic and is the wrong class for this job.

Shell
# Linux and macOS. LC_ALL=C stops tr rejecting bytes outside the locale.
LC_ALL=C tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 64; echo

# openssl, if you want 64 hexadecimal characters instead
# (32 bytes = 64 hex characters = 256 bits, exactly)
openssl rand -hex 32

tr -dc discards bytes outside the set rather than folding them into it, so the one-liner is uniform. /dev/urandom is the kernel CSPRNG on both platforms.

Questions

How strong is a 64-character random string?

381.07 bits over the 62-character alphanumeric alphabet. That is far beyond any cryptographic requirement: AES-256 uses 256 bits and NIST mandates a minimum generator security strength of 112. The length is not the interesting property of a value this size.

Should my JWT signing secret be 64 characters?

The requirement is normally stated in bits, not characters. A secret matched to SHA-256 is 256 bits, which is 32 bytes or 64 hexadecimal characters. Sixty-four alphanumeric characters carries more entropy than that, but if the library or the config expects hexadecimal it will reject the alphabet, so generate hex.

Is 64 characters better than 32?

Not against guessing. Both are past 128 bits, which is where brute force stops being the threat, so the extra 190 bits changes nothing an attacker can act on. Choose 64 when a format, a column width or an existing secret requires it, and 32 otherwise.

Why is 64 hex characters exactly 256 bits but 64 alphanumeric characters is not a round number?

Hexadecimal has 16 symbols and log2(16) is exactly 4, so every character carries exactly 4 bits. The alphanumeric alphabet has 62 symbols and log2(62) is 5.954196, which is not a whole number and never will be. Base16, base32 and base64 are the alphabets that land on exact bit counts.

Can I generate these in bulk?

Yes. Batches are generated in chunks so the tab stays responsive, with a progress indicator and a cancel button, and the output can be copied or downloaded as plain lines, JSON, CSV or .env format. Nothing is uploaded at any batch size.

Related generators