But then it dawned on me, the Java example could be mimicked to the T in ColdFusion, since it runs on the Java platform!
<cffunction name="signGoogleURL"> <cfargument name="url" /> <!--- I stored the key in the request scope, you can remove the default attribute ---> <cfargument name="key" default="#request.GoogleAPIs.mapsV3.cryptoKey#" /> <cfset var local = {} /> <cfif Find(".googleapis.com", arguments.url) EQ 0> <cfthrow message="Invalid Google URL." /> </cfif> <!--- pull off the googleapis piece of the URL ---> <cfset local.urlToEncrypt = Right(arguments.url, Len(arguments.url)-(Find(".googleapis.com", arguments.url)+14) ) /> <!--- this is the part we encrypt and pass as signature ---> <cfset local.msg_digest = cfhmac(local.urlToEncrypt, webSafeToBase64(arguments.key)) /> <cfreturn arguments.url & "&signature=" & Base64ToWebSafe(local.msg_digest) /> </cffunction> <!--- this function mimmicks the code in signRequest from the Java example ---> <cffunction name="CFHMAC" output="false"> <cfargument name="signMsg" type="string" required="true" /> <cfargument name="signKey" type="string" required="true" /> <cfset var local = {} /> <!--- get the key in binary ---> <cfset local.key = BinaryDecode(arguments.signKey, "base64") /> <!--- initialize the crypto object with the key and algorithm ---> <cfset local.keySpec = createObject("java", "javax.crypto.spec.SecretKeySpec").init(local.key, "HmacSHA1") /> <cfset local.mac = createObject("java", "javax.crypto.Mac").getInstance("HmacSHA1") /> <cfset local.mac.init(local.keySpec) /> <!--- pass the message's bytes into the crypto object. we return it in Base64 ---> <cfreturn ToBase64( local.mac.doFinal(arguments.signMsg.getBytes()) ) /> </cffunction> <!--- simple switch from google's webSafe format to the actual Base64, in the Java example this is done in UrlSigner() ---> <cffunction name="webSafeToBase64"> <cfargument name="val" /> <cfset arguments.val = replace( replace(arguments.val, "-", "+", "ALL") , "_", "/", "ALL") /> <cfreturn arguments.val /> </cffunction> <!--- switch back to google's format, before adding to URL. In the Java example this is done at the bottom of signRequest() ---> <cffunction name="Base64ToWebSafe"> <cfargument name="val" /> <cfset arguments.val = replace( replace(arguments.val, "+", "-", "ALL") , "/", "_", "ALL") /> <cfreturn arguments.val /> </cffunction>I chose to include these in a component, and call it as follows:
<cfset variables.requestURL = Maps.signGoogleUrl([url], [API CryptoKey]) />The url variable is the full googleapis address including the protocol, and URL parameters like the required client variable. You can then pass that into a cfhttp call and your requests will be validated by your client and cryptoKey values! Hope this saves somebody some time, let me know if you find it useful!