4

我正在尝试使用 ColdFusion 使用他们当前称为签名版本 4 的身份验证方法访问 Amazon Web Services (AWS)。我查阅了他们的文档,其中包含几种编程语言的代码示例,以及其他语言的伪代码。他们提供了一些测试输入值以传递给我的脚本的签名函数,以及一些预期的结果。

以下是测试输入:

key = 'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY'
dateStamp = '20120215'
regionName = 'us-east-1'
serviceName = 'iam'

以下是预期结果:

kSecret  = '41575334774a616c725855746e46454d492f4b374d44454e472b62507852666943594558414d504c454b4559'
kDate    = '969fbb94feb542b71ede6f87fe4d5fa29c789342b0f407474670f0c2489e0a0d'
kRegion  = '69daa0209cd9c5ff5c8ced464a696fd4252e981430b10e3d3fd8e2f197d7a70c'
kService = 'f72cfd46f26bc4643f06a11eabb6c0ba18780c19a8da0c31ace671265e3c87fa'
kSigning = 'f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d'

“kSigning”的正确值应该是这样的:

f4780e2d9f65fa895f9c67b32ce1baf0b0d8a43505a000a1a9e090d414db404d

但是,对于“kSigning”,我的代码会生成:

31A84DCE0538A8B15ED68CCFBD803F17947E41BF625EFFD1AD6A67FC821F9BE2

我正在使用 Railo 4.2。有人可以帮我解决这个问题,以便预期值与转储值匹配吗?这是我的 ColdFusion 标记:

<cfsilent>

<!--- HMACSHA256 --->
<cffunction name="sign" returntype="binary" access="private" output="false" hint="Sign with NSA SHA-256 Algorithm">
   <cfargument name="signMessage" type="string" required="true" />
   <cfargument name="signKey" type="string" required="true" />

   <cfset var jMsg = JavaCast("string",arguments.signMessage).getBytes("utf-8") />
   <cfset var jKey = JavaCast("string",arguments.signKey).getBytes("utf-8") />
   <cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec") />
   <cfset var mac = createObject("java","javax.crypto.Mac") />

   <cfset key = key.init(jKey,"HmacSHA256") />
   <cfset mac = mac.getInstance(key.getAlgorithm()) />
   <cfset mac.init(key) />

   <cfreturn mac.doFinal(jMsg) />
</cffunction>

<!--- Get Signature Key --->
<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 var kSecret = "AWS4" & arguments.key />
    <cfset var kDate = sign( arguments.dateStamp, kSecret ) />
    <cfset var kRegion = sign( arguments.regionName, kDate ) />
    <cfset var kService = sign( arguments.serviceName, kRegion ) />
    <cfset var kSigning = sign( arguments.serviceName, kService ) />

    <cfreturn kSigning />
</cffunction>

</cfsilent><!doctype html>

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>AWS Test</title>
</head>
<body>

<cfset kSecret = getSignatureKey( 
    'wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY', 
    '20120215', 
    'us-east-1', 
    'iam' 
) />
<cfdump var="#BinaryEncode(kSecret, 'hex')#" label="kSecret" />

</body>
</html>
4

3 回答 3

3

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

我对代码运行没有错误感到惊讶,因为该sign()函数需要两个字符串,但代码实际上是为第二个参数传入一个字节数组。(在 CF11 下它会引发错误)。也许正在进行某种隐式转换?无论如何,在稍微重构函数之后,该示例运行良好,但有一个例外。该示例的最后一行使用文字字符串“aws4_request”而不是“arguments.serviceName”。请参见下面的示例。

话虽如此,Railo 不是有一个可以使用而不是自己滚动的 HMAC 函数吗?我猜是这样,因为HMAC()包含在 CF10+ 中。更新:正如vrtjason 在评论中指出的那样,Railo在4.0.0.011HMAC()中添加了该功能。但是,为了向后兼容,下面的 java 版本应该适用于大多数任何版本。

例子:

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

结果:

F4780E2D9F65FA895F9C67B32CE1BAF0B0D8A43505A000A1A9E090D414DB404D 

职能:

<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>
于 2015-09-11T02:49:32.977 回答
1

我看到了一个问题,并希望您也能看到它,当您注意到这两行有多相似时:

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

红旗警报,两次hmac服务名称有意义吗?

最后一步的输入是字符串文字。

<cfset var kSigning = sign( "aws4_request", kService ) />
于 2015-09-11T02:45:21.893 回答
1

我相信您可以使用内置的 HMAC() 函数在 ColdFusion 10+ 中执行此操作,而无需创建 Java 对象:

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;
}
于 2015-11-10T22:08:35.743 回答