How to derive a sign-in key for AWS Signature Version 4 (in ColdFusion)?

扶醉桌前 提交于 2019-12-01 21:36:44
Leigh

<cfset var kRegion = sign( arguments.regionName, kDate ) />

I am little surprised the code runs without error, as the sign() function expects two strings, but the code is actually passing in a byte array for the second parameter. (Under CF11 it throws an error). Perhaps there is some sort of implicit conversion going on? Anyway, after refactoring the functions slightly, the example worked fine with one exception. The very last line of the example uses the literal string "aws4_request" rather than "arguments.serviceName". See example below.

Having said that, does not Railo have an HMAC function you can use rather than rolling your own? I am guessing so, as HMAC() is included in CF10+. Update: As vrtjason noted in the comments, Railo added the HMAC() function in 4.0.0.011. However, for backward compatibility the java version below should work with most any version.

Example:

result = getSignatureKey(key, dateStamp, regionName, serviceName);
writeDump( binaryEncode(result, "hex") );

Results:

F4780E2D9F65FA895F9C67B32CE1BAF0B0D8A43505A000A1A9E090D414DB404D 

Functions:

<cffunction name="getSignatureKey" returntype="binary" access="private" output="false" hint="Derive the sign-in key">
    <cfargument name="key" type="string" required="true" />
    <cfargument name="dateStamp" type="string" required="true" />
    <cfargument name="regionName" type="string" required="true" />
    <cfargument name="serviceName" type="string" required="true" />

    <cfset Local.kSecret = charsetDecode("AWS4" & arguments.key, "UTF-8") />
    <cfset Local.kDate = sign( arguments.dateStamp, Local.kSecret ) />
    <cfset Local.kRegion = sign( arguments.regionName, Local.kDate ) />
    <cfset Local.kService = sign( arguments.serviceName, Local.kRegion ) />
    <cfset Local.kSigning = sign( "aws4_request", Local.kService ) />

    <cfreturn Local.kSigning />
</cffunction>




<cffunction name="sign" returntype="binary" access="private" output="false" hint="Sign with NSA SHA-256 Algorithm">
   <cfargument name="message" type="string" required="true" />
   <cfargument name="key" type="binary" required="true" />
   <cfargument name="algorithm" type="string" default="HmacSHA256" />
   <cfargument name="encoding" type="string" default="UTF-8" />

   <cfset Local.keySpec = createObject("java","javax.crypto.spec.SecretKeySpec") />
   <cfset Local.keySpec = Local.keySpec.init( arguments.key, arguments.algorithm ) />
   <cfset Local.mac = createObject("java","javax.crypto.Mac").getInstance( arguments.algorithm ) />
   <cfset Local.mac.init( Local.keySpec ) />

   <cfreturn Local.mac.doFinal( charsetDecode(arguments.message, arguments.encoding ) ) />
</cffunction>

I see a problem and expect you will see it, too, when you note how similar these two lines are:

<cfset var kService = sign( arguments.serviceName, kRegion ) />
<cfset var kSigning = sign( arguments.serviceName, kService ) />

Red flag alert, does it make sense to hmac the service name twice?

The input to the last step is a string literal.

<cfset var kSigning = sign( "aws4_request", kService ) />

I believe you can do this in ColdFusion 10+ using the built in HMAC() function without needing to Create a Java Object:

function getSignatureKey(key, dateStamp, regionName, serviceName) {
    var kDate= lCase(HMAC(ARGUMENTS.dateStamp,"AWS4" & ARGUMENTS.key,"hmacsha256"));
    var kRegion= lCase(HMAC(ARGUMENTS.regionName,binaryDecode(kDate,'hex'),"hmacsha256"));
    var kService=lCase(HMAC(ARGUMENTS.serviceName,binaryDecode(kRegion,'hex'),"hmacsha256"));
    var kSigning= lCase(HMAC("aws4_request",binaryDecode(kService,'hex'),"hmacsha256"));

    return kSigning;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!