Skip to main content
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Heather Harkins
Ben Nadel at cf.Objective() 2013 (Bloomington, MN) with: Heather Harkins

Generating Random Binary Values Using randRange() In ColdFusion

By
Published in Comments (2)

The other day, I wrote about generating "cryptographically strong" random tokes in ColdFusion. In my approach, I was using the java.security.SecureRandom class to generate the random bytes. In the comments, however, Henry Ho came up with a really interesting solution to the same problem which was to use randRange() to generate the random bytes that went into the token. Now, he wasn't using the default randRange() behavior; rather, he was using randRange() with the SHA1PRNG algorithm for more secure randomness. After seeing his solution, I wanted to write up a quick demo using the same approach to generically look at how randRange() can be used to generate random binary values in ColdFusion.

In this demonstration, I'm going to be using the SHA1PRNG algorithm, just like Henry; but, the extra secure randomness (provided by the SHA1PRNG algorithm in the Java Cryptography Extension (JCE) installed by ColdFusion) isn't strictly necessary for generating random binary data. At a high level, all we're doing here is using randRange() to generate a random integer; then, we're taking that integer, interpreting it as a byte, and then casting an array of bytes into an actual binary value.

While I am generating a random token here, the real point of the demo is the implementation of the nextBytes() method which generates and returns the random binary value:

<cfscript>

	// Generate random tokens which are based on randomly generated binary values.
	for ( i = 1 ; i <= 25 ; i++ ) {

		writeOutput( "#i#: #nextToken()# <br />" );

	}


	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //


	/**
	* I generate a random binary value with the given number of bytes.
	*
	* @byteCount I am the length of the random binary value.
	* @output false
	*/
	public binary function nextBytes( required numeric byteCount ) {

		var bytes = [];

		// Since we know how many bytes we want to produce, let's resize the array to
		// be the desired size. This can help with dynamic memory allocation.
		arrayResize( bytes, byteCount );

		for ( var i = 1 ; i <= byteCount ; i++ ) {

			// NOTE: In Java, bytes are signed. Which means that the left-most bit
			// (of the byte) indicates the sign of the byte. In order to not jump through
			// hoops to convert a random INT into a SIGNED BYTE, we'll just produce an
			// INT in the range that we know will fit nicely into a byte. We're still
			// exercising all 8-bits.
			bytes[ i ] = randRange( -128, 127, "SHA1PRNG" );

		}

		// Return random bytes cast as a binary value.
		return( javaCast( "byte[]", bytes ) );

	}


	/**
	* I generate a random token based on the given number of bytes. The token is returned
	* as a base64url-encoded value, safe for use as a URI component.
	*
	* @byteCount I am the number of bytes on which to base our random token.
	* @output false
	*/
	public string function nextToken( numeric byteCount = 32 ) {

		// Get the random binary value and encode it as base64.
		var token = binaryEncode( nextBytes( byteCount ), "base64" );

		// Convert form base64 to a base64url schema in order to make the token safely
		// usable in a wider number of contexts (such as a URI component).
		token = replace( token, "+", "-", "all" );
		token = replace( token, "/", "_", "all" );
		token = reReplace( token, "=+$", "", "one" );

		return( token );

	}

</cfscript>

As you can see, generating the random token is really just the process of generating a random binary value and then encoding that binary value using the base64url schema. And, when we run this code, we get the following output:

1: 0OqNe6a6TVTL2ELXE7bUiOtDzuNBD6yCR7zms6WL22E
2: C4dZKY8etF_jaSImrQr0FHskEQvtn54QIzo-dR-4WNo
3: Gl671zzFMe5oeVcAcDV3rnU7vsIGZpZl4w4SPW4iMjE
4: kpA8Xw8rfRFS01fyP3xi7jlaTYxjVQaPQ8-IdNvQ7G0
5: 0jOT22jHlChUZiDCjI16fEDaHMCBBb_C1kPeH3Z1zn0
6: LM1BIxUy2uBPZTc14COc-HSmwnI0GN51Rz_6ex5bk3Q
7: 2UFTblaE3L2kyTc3MobCVc6rh3xjqh3BnWFHqg0v3LQ
8: vOwU8F4BkOe_Fp8vqnKE_SZ5Uceg1EOk4INM-dSY4KQ
9: 5F8Cu3iOPWTBE9W_RgjzLsVK_eBi6LAhmmtlcFRQY6w
10: umBo4ksF8lLKBzzXhrnPekn1zyBosH4mP7tGHgQExx0
11: Rl9oMQDckbcn5vJfrymUrL4-yho6PC-KFtKtCfVITUs
12: Z2OprYd4BiuK5lXDbMhpYVqRrWU-zo8aW2cAYQA_r0I
13: va5Jq47ctubaiPdqO3T5kMUxyDooebUKgjKTwBBHfkI
14: iJqfUTG8YIEjLM7Doy_UbIvqxjsDlChVodIUVo7-2Jc
15: b_9j18SoLcSFA_4Peqyg8h9MgUKyVmkMCVnhpMz-L88
16: yX7ltlBR8cIusNwZXOoGwe2P6vNTZYNP955uapOcuoc
17: YkpwhLqfbvPBvIF-21JUwFqUAz9GpyqQTFyECQIAawo
18: QB2SkAlEbNZCE9xrllPELlRkeCTyxVunBw8jzHsR6Hc
19: 3qyY9PjK6wMQbTrvgSKagx5DqLZrVDEsWx124qpQh5s
20: KOw_Oua0j4_Id2KzXatJTLp_fACtXp6htFueGCNZXu8
21: --XC5ofNBFNp0yD9Py0nzMr8kWH4QEcX91Ay_Z2rZ-w
22: s-x5BXewOQ1TulzaD1_pbvxoHHF0Y0BzdFmj9wA1IT0
23: QwtfqQPXBzDby3q37NApbT7HWSzZDHKxrb4MTRv8yc4
24: rgBfgZDTp4qV1Dp89Qj72A-NSsJvb_Qlgfxen9skkdM
25: aliy7UsEGIMAdSVmMUkcWk2vmA7aWc-zzqy3WrEtLec

Because we're using ColdFusion to generate bytes of data, we have to be careful about the values that we are casting. Behind the scenes, Java uses signed bytes. Which means that a Java byte can't represent anything over 127. As such, to ensure that we are exercising all 8 bits of the byte, the random values that I am producing using randRange() go from -128 to 127. This range can safely be cast to a Java byte using javaCast() without worrying about involving bit-masks.

When Henry Ho showed me his solution, I had to admit that I completely forgot that randRange() event allowed an algorithm to be defined. Based on the documentation, it would seem that selecting the SHA1PRNG algorithm would produce more secure randomness. But, without actually knowing what is going on behind the scenes, it's hard to say. Plus, I'm not really a security guy, so take anything I say about security with a grain of salt. That said, security aside, it's definitely possible to use randRange() to generate random binary values in ColdFusion.

Want to use code from this post? Check out the license.

Reader Comments

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel