Generate 6 Letter Security Codes in Java or CFML

by Pete Freitag

A few weeks ago I shared how to generate a 6 digit security code in ColdFusion or Java. A reader asked me how would I do that for codes that might contain letters, such as an airline confirmation code, eg XYZ-ABC.

Let's start by creating a function to generate a random alpha numeric value (a letter or number). This function will generate upper case or lower case letters, and numbers 0-9, A-Z, or a-z:

string function generateRandomAlphaNumericCharacter() {
	//use a strong secure random number generator
	var secureRandom = createObject("java", "java.security.SecureRandom").getInstanceStrong();

    //get int between and including 0-61
	var randomInteger = secureRandom.nextInt(62);
	
    if (randomInteger <= 9) {
        //number 0-9
        return randomInteger;
    } else if (randomInteger <= 35) {
        //uppercase ascii 65-90
        return chr( randomInteger - 9 + 64 );
    } else {
        //lowercase ascii 97-122
        return chr( randomInteger - 35 + 96 );
    }
    
}

Using Java's SecureRandom class I am generating a random integer between 0 and 61 inclusive. The nextInt(62) call is exclusive of its upper bound, so it returns values 0 through 61. Are you wondering why 62? It is 10 + 26 + 26, which represents 10 digits (0-9), 26 uppercase letters (A-Z), and 26 lowercase letters (a-z).

Once we have the randomInteger value, if it is 9 or below, we just return that value as our random character, taking care of the numeric values. Next for values 10 through and including 35, we convert those numbers to an ascii value by subtracting 9 and adding 64. Ascii uppercase A has the code 65, and Z 90. Finally we generate lower case characters the same way, just different offset (lowercase a has an ascii code of 97).

A quick note on getInstanceStrong(): on Linux it resolves to NativePRNGBlocking, which reads from /dev/random and can block when the entropy pool is low (see Will Sargent's The Right Way to Use SecureRandom for the details). For generating short codes like this, createObject("java", "java.security.SecureRandom").init() is usually sufficient and non-blocking - but getInstanceStrong() is the safer default if you can tolerate the occasional wait.

Now we can call this generateRandomAlphaNumericCharacter function multiple times to generate a random code of the desired length. For example:

string function generateRandomAlphaNumericCode(numeric codeLength=6) {
    var i=0;
    var code = "";
    for (i=0;i<arguments.codeLength;i++) {
        code &= generateRandomAlphaNumericCharacter();
    }
    return code;
}

I think it is a bit cleaner to implement this in two functions, but it might be slightly more efficient to avoid creating the SecureRandom over and over again. So if you prefer here is an all in one function:

string function generateRandomAlphaNumericCode(numeric codeLength=6) {
    var code = "";
    var secureRandom = createObject("java", "java.security.SecureRandom").getInstanceStrong();
    var randomInteger = 0;
    if ( arguments.codeLength < 1 ) {
        throw(message="Invalid code length");
	}
    while ( len(code) < arguments.codeLength ) {
        randomInteger = secureRandom.nextInt(62);
        if (randomInteger <= 9) {
            //number 0-9
            code &= randomInteger;
        } else if (randomInteger <= 35) {
            //uppercase ascii 65-90
            code &= chr( randomInteger - 9 + 64 );
        } else {
            //lowercase ascii 97-122
            code &= chr( randomInteger - 35 + 96 );
        }
    }
    return code;
}

Now finally to create a code like XYZ-ABC, we could do something like this:

uCase( generateRandomAlphaNumericCode(3) & "-" & generateRandomAlphaNumericCode(3))

And if you truly only want to generate uppercase letters (no digits, no lowercase), a simpler approach is to skip the digit and lowercase branches entirely and just do chr( secureRandom.nextInt(26) + 65 ) per character — nextInt(26) returns a value 0 through 25, which maps to A-Z.

How much entropy is in a 6 character code?

With a 62 character alphabet, a 6 character code has 626, or approximately 56.8 billion possible values. That is plenty of entropy to make a brute force guess impractical on its own, but if the code is being used for authentication (a password reset, a login link, a one time confirmation) you should still rate limit attempts and expire codes after a short window. The code is only as strong as the surrounding controls.

Java Version

Since the CFML version is really just calling Java under the hood, here is the same logic written directly in Java:

import java.security.SecureRandom;

public class AlphaNumericCode {

    private static final SecureRandom secureRandom = new SecureRandom();

    public static String generateRandomAlphaNumericCode(int codeLength) {
        if (codeLength < 1) {
            throw new IllegalArgumentException("Invalid code length");
        }
        StringBuilder code = new StringBuilder(codeLength);
        while (code.length() < codeLength) {
            int randomInteger = secureRandom.nextInt(62);
            if (randomInteger <= 9) {
                // number 0-9
                code.append(randomInteger);
            } else if (randomInteger <= 35) {
                // uppercase ascii 65-90
                code.append((char) (randomInteger - 9 + 64));
            } else {
                // lowercase ascii 97-122
                code.append((char) (randomInteger - 35 + 96));
            }
        }
        return code.toString();
    }

    public static void main(String[] args) throws Exception {
        System.out.println(generateRandomAlphaNumericCode(6));
    }
}

In the Java example, I am reusing a static instance of SecureRandom to avoid re-creating it on every call.

The Fixinator Code Security Scanner for ColdFusion & CFML is an easy to use security tool that every CF developer can use. It can also easily integrate into CI for automatic scanning on every commit.