Generating Signatures in ColdFusion with RSA-SHA1 for Secure AuthSub in Google Analytics

See also: Google Analytics API Offline Access with Service Account and ColdFusion

To send secure AuthSub requests to Google Analytics, you have to sign the request with a private key that is verified against the certificate you upload to Google. Google has a library where this can be done in Java that is specific to Google Analytics and, provided you can add .jar files to your ColdFusion server or use javaloader, you can run them using ColdFusion. If you need to roll your own or simply want to control the process, this example may help.

Before you implement any code that signs requests, the keys and certificate must be generated and then registered with Google you are attempting to access. Google has instructions on how to generate the keys and certificate using OpenSSL and how to register the certificate with Google. You will need the private key in the PKCS#8 format.

Google also supplies instructions for signing the AuthSub requests which is what we will step through here.

To get a single-use token for secure AuthSub, you send the user to authenticate through Google with a link similar to this:

<a href="">Log in using Secure AuthSub through Google</a>

Notice that secure is set to 1 indicating we want to send secure requests. Session is also set to 1 indicating we want a multi-use authorization token for multiple calls to the Google Analytics Data Export API. If you are only making one call to the API, you can set session to 0 (zero).

Google will then the user to your page designated by the next parameter with the token attached as a URL parameter called “token.” After getting this single-use token back from Google, you then need to send it back to Google to exchange for your multi-use token. This is where the signing begins. Every request from this point forward needs to be signed.

Secure AuthSub differs from AuthSub in that it requires several parameters to be added to the Authorization header of the request. They are:

  1. token: single-use token (or multi-use token once you get it) from Google
  2. sigalg: rsa-sha1
  3. data: the http-method, request URL, timestamp, and nonce all separated by a space.
  4. sig: the signature made by the private key generated against the data parameter above.

Generating this only requires a few lines of ColdFusion code:

        <cfset Math = createObject('java','java.lang.Math') />
        <cfset randNum = createObject('java', '') />
        <cfset numeric_nonce = Math.abs(JavaCast("long",randNum.nextLong())) />
        <cfset nonce = numeric_nonce.toString() />
        <cfset timestmp = DateDiff("s",DateConvert("utc2Local", "January 1 1970 00:00"), Now()) />
        <cfset appSignature = rsa_sha1(rsaPrivateKey, 'GET ' & timestmp & ' ' & nonce) />
        <cfset authHeaderValue = 'AuthSub token="' & URL.token & '" data="GET ' & timestmp & ' ' & nonce & '" sig="' & appSignature & '"  sigalg="rsa-sha1"' />

The rsa_sha1 function creates the signature. It was written by Sharad Gupta and used here with permission.

<cffunction name="rsa_sha1" returntype="string" access="public" descrition="RSA-SHA1 computation based on supplied private key and supplied base signature string.">
    <!---Written by Sharad Gupta (used with permission)--->
           <cfargument name="signKey" type="string" required="true" hint="base64 formatted PKCS8 private key">
           <cfargument name="signMessage" type="string" required="true" hint="msg to sign">
           <cfargument name="sFormat" type="string" required="false" default="UTF-8">

           <cfset var jKey = JavaCast("string", arguments.signKey)>
           <cfset var jMsg = JavaCast("string",arguments.signMessage).getBytes(arguments.sFormat)>

           <cfset var key = createObject("java", "")>
           <cfset var keySpec = createObject("java","")>
           <cfset var keyFactory = createObject("java","")>
           <cfset var b64dec = createObject("java", "sun.misc.BASE64Decoder")>

           <cfset var sig = createObject("java", "")>

           <cfset var byteClass = createObject("java", "java.lang.Class")>
           <cfset var byteArray = createObject("java","java.lang.reflect.Array")>

           <cfset byteClass = byteClass.forName(JavaCast("string","java.lang.Byte"))>
           <cfset keyBytes = byteArray.newInstance(byteClass, JavaCast("int","1024"))>
           <cfset keyBytes = b64dec.decodeBuffer(jKey)>

           <cfset sig = sig.getInstance("SHA1withRSA", "SunJSSE")>
           <cfset sig.initSign(keyFactory.getInstance("RSA").generatePrivate(keySpec.init(keyBytes)))>
           <cfset sig.update(jMsg)>
           <cfset signBytes = sig.sign()>
           <cfreturn ToBase64(signBytes)>

The request for and parsing out of the multi-use token looks like this:

<cfhttp url="" method="GET">
 <cfhttpparam name="Authorization" type="header" value="#authHeaderValue#">
<cfset output = cfhttp.filecontent />
<cfset authSubSessionToken = Mid(output, FindNoCase("Token=",output) + (Len("Token=")), Len(output)) />     

Any more requests of the Data Export API have to be made in the same manner except now you will use the multi-use token (authSubSessionToken) as the token in the Authorization header.


Posted in ColdFusion, Google Analytics. Tags: , . Permalink. Both comments and trackbacks are closed.

One Comment

  1. sabino
    December 30, 2010 at 9:32 am | Permalink

    Hi Sharad, i am new in cryptography, the article is great, but i have problem with the code line:
    <cfset sig.initSign(keyFactory.getInstance("RSA").generatePrivate

    a null pointer error, i already debuged the code and read the java libraries and looks fine, I will appreciate any help,

One Trackback

  1. [...] Read the original post: Generating Signatures in ColdFusion with RSA-SHA1 for Secure … [...]