Creating a AWS v4 Signature for AWIS

我的梦境 提交于 2020-03-16 06:44:16

问题


For years we've been using the AWS v2 signature which is now replaced by v4.

I'm slowly working through all the steps but have come unstuck on step 3 where it's asking for HMAC-SHA256 encryptions that output in binary format. https://docs.aws.amazon.com/AlexaWebInfoService/latest/CalculatingSignatures.html https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html

I need to do this in Classic ASP, I have scripts that encode SHA256 but nothing I can find that will output in binary.

The nearest I've got is the Chilkat component that seems to output binary from SHA256 hashes but I can't work out how to achieve this: http://www.chilkatsoft.com/refdoc/xChilkatCrypt2Ref.html#method72

Can anyone point me in the right direction either with script (asp, vbscript, javascript) or with the Chilkat (or other) component? It's the binary output that is really causing problems.

Many thanks in advance


回答1:


The AWS V4 Signature generation process had me scratching my head for days (it was the HMAC binary output that had me stumped too), but it is possible using pure classic ASP. Here's a class I wrote for generating signed S3 URLs (I'm not familiar with AWIS, but after glancing at the documentation you linked I can see the signature generation process is the same):

Class AmazonWebServices

    Private AWS_utc, AWS_timestamp, AWS_timestamp_short, AWS_url, AWS_headers, AWS_string_to_sign, AWS_signature

    Private AWS_S3_key, AWS_S3_region, AWS_S3_version, AWS_S3_bucket, AWS_S3_host, AWS_S3_secret

    Private Sub Class_Initialize()

        ' AWS expects a UTC timestamp, if your server isn't set to UTC you will need to apply
        ' an offset to NOW() using DateAdd()

        AWS_utc = NOW() ' Or to apply an offset: DateAdd("h",-1,NOW())

        AWS_timestamp = year(AWS_utc) & zero_pad(month(AWS_utc)) & zero_pad(day(AWS_utc)) &_
        "T" & zero_pad(hour(AWS_utc)) & zero_pad(minute(AWS_utc)) & zero_pad(second(AWS_utc)) & "Z"

        AWS_timestamp_short = left(AWS_timestamp,8)

        AWS_S3_key = "XXXXXXXXXXXXXXXXXXXX"

        AWS_S3_region = "eu-west-2"

        AWS_S3_version = "2006-03-01"

        AWS_S3_bucket = "BUCKETNAME"

        AWS_S3_host = AWS_S3_bucket & ".s3." & AWS_S3_region & ".amazonaws.com"

        AWS_S3_secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

    End Sub

    public function s3_signed_url(ByVal theFile, ByVal expire)

        ' expire is the number of seconds to keep the url alive

        ' Prefix the file name with a slash

        if NOT inStr(theFile,"/") = 1 then theFile = "/" & theFile

        ' Construct the S3 URL

        AWS_url = "https://" & AWS_S3_host & theFile &_
        "?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD" &_
        "&X-Amz-Algorithm=AWS4-HMAC-SHA256" &_
        "&X-Amz-Credential=" & AWS_S3_key &_ 
            "%2F" & AWS_timestamp_short &_ 
            "%2F" & AWS_S3_region &_ 
            "%2Fs3" &_
            "%2Faws4_request" &_
        "&X-Amz-Date=" & AWS_timestamp &_
        "&X-Amz-SignedHeaders=host" &_
        "&X-Amz-Expires=" & expire &_
        "&X-Amz-Signature="

        ' Construct the GET headers

        ' headers need to be separated with just a line feed
        ' VBlf = line feed
        ' VBcr = carriage return
        ' VBcrlf = carriage return & line feed
        ' Anything but VBlf (or chr(10)) will return a signature mismatch

        AWS_headers = "GET" & VBlf &_ 
        theFile & VBlf &_ 
        "X-Amz-Algorithm=AWS4-HMAC-SHA256" &_
        "&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD" &_
        "&X-Amz-Credential=" & AWS_S3_key &_ 
            "%2F" & AWS_timestamp_short &_ 
            "%2F" & AWS_S3_region &_ 
            "%2Fs3" &_
            "%2Faws4_request" &_
        "&X-Amz-Date=" & AWS_timestamp &_ 
        "&X-Amz-Expires=" & expire &_ 
        "&X-Amz-SignedHeaders=host" & VBlf &_ 
        "host:" & AWS_S3_host & VBlf & VBlf &_ 
        "host" & VBlf &_ 
        "UNSIGNED-PAYLOAD"

        ' GET headers are hashed with SHA256

        AWS_headers = hash(AWS_headers,"SHA256","hex")

        ' Construct the string to sign

        AWS_string_to_sign = "AWS4-HMAC-SHA256" & VBlf &_ 
        AWS_timestamp & VBlf &_
        AWS_timestamp_short & "/" & AWS_S3_region & "/s3/aws4_request" & VBlf &_ 
        AWS_headers

        ' date key, generated by hashing the short timestamp using the secret key as the HMAC key

        AWS_signature = hash_hmac("AWS4" & AWS_S3_secret,AWS_timestamp_short,"SHA256","raw")    

        ' region key, generated by hashing the region using the date key as the HMAC key

        AWS_signature = hash_hmac(AWS_signature,AWS_S3_region,"SHA256","raw")

        ' service key, generated by hashing the service name using the region key as the HMAC key

        AWS_signature = hash_hmac(AWS_signature,"s3","SHA256","raw")        

        ' signing key, generated by hashing the request type using the service key as the HMAC key

        AWS_signature = hash_hmac(AWS_signature,"aws4_request","SHA256","raw")

        ' signature, generated by hashing the "string to sign" using the signing key as the HMAC key

        AWS_signature = hash_hmac(AWS_signature,AWS_string_to_sign,"SHA256","hex")

        ' Return the complete URL with signature

        s3_signed_url = AWS_url & AWS_signature

    end function

    private function hash(ByVal input, ByVal alg, ByVal encoding)
        ' Convert the input to bytes if not already
        if NOT vartype(input) = 8209 then input = string_to_UTF8_bytes(input)
        Dim hAlg : Set hAlg = Server.CreateObject("System.Security.Cryptography." & get_hash_class(alg))
            hash = binary_encode(hAlg.ComputeHash_2((input)),encoding)     
        set hAlg = nothing
    end function

    private function hash_hmac(ByVal secret, ByVal message, ByVal alg, ByVal encoding)
        ' Convert the input to bytes if not already
        if NOT vartype(secret) = 8209 then secret = string_to_UTF8_bytes(secret)
        if NOT vartype(message) = 8209 then message = string_to_UTF8_bytes(message)
        Dim hAlg : Set hAlg = Server.CreateObject("System.Security.Cryptography." & get_hmac_class(alg))
            hAlg.Initialize() 
            hAlg.key = secret
            hash_hmac = binary_encode(hAlg.ComputeHash_2((message)),encoding)             
       set hAlg = nothing
    end function

    private function binary_encode(ByVal binary, ByVal encoding)
        encoding = lCase(encoding)
        if encoding = "raw" then
            binary_encode = binary
            exit function
        end if
        Dim enc : Set enc = Server.CreateObject("MSXML2.DomDocument").CreateElement("encode")
            if encoding = "base64" OR encoding = "b64" then
                ' base64 string
                enc.dataType = "bin.base64"
                enc.nodeTypedValue = binary
                binary_encode = enc.Text 
            else
                ' hexadecimal string
                enc.dataType = "bin.hex"
                enc.nodeTypedValue = binary
                binary_encode = enc.Text 
            end if
        Set enc = nothing
    end function

    private function get_hash_class(ByVal alg)
        ' get the cryptography class name for the specified hashing algorithm,
        ' return the class name for SHA1 if not found
        select case uCase(alg)
            case "MD5"
                get_hash_class = "MD5CryptoServiceProvider"
            case "SHA1"
                get_hash_class = "SHA1CryptoServiceProvider"
            case "SHA2","SHA256"
                get_hash_class = "SHA256Managed"
            case "SHA3","SHA384"
                get_hash_class = "SHA384Managed"
            case "SHA5","SHA512"
                get_hash_class = "SHA512Managed"
            case else
                get_hash_class = "SHA1CryptoServiceProvider"
        end select
    end function

    private function get_hmac_class(ByVal alg)
        ' get the cryptography class name for the specified HMAC algorithm,
        ' return the class name for SHA1 if not found
        select case uCase(alg)
            case "MD5"
                get_hmac_class = "HMACMD5"
            case "SHA1"
                get_hmac_class = "HMACSHA1"
            case "SHA3","SHA384"
                get_hmac_class = "HMACSHA384"
            case "SHA2","SHA256"
                get_hmac_class = "HMACSHA256"
            case "SHA5","SHA512"
                get_hmac_class = "HMACSHA512"
            case else
                get_hmac_class = "HMACSHA1"
        end select
    end function

    private function string_to_UTF8_bytes(ByVal aString) 
        ' convert a UTF8 string to bytes
        Dim UTF8 : Set UTF8 = Server.CreateObject("System.Text.UTF8Encoding") 
            string_to_UTF8_bytes = UTF8.GetBytes_4(aString) 
        set UTF8 = nothing
    end function

    private function zero_pad(ByVal theNum)
        if len(theNum) = 1 then
            zero_pad = cStr("0" & theNum)
        else
            zero_pad = theNum
        end if
    end function

end class

To generate a 24 hour S3 signed URL:

set AWS = new AmazonWebServices

    response.write AWS.s3_signed_url("file.name",86400)

set AWS = nothing

Example output:

https://BUCKETNAME.s3.eu-west-2.amazonaws.com/file.name?X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=XXXXXXXXXXXXXXXXXXXX%2F20190404%2Feu-west-2%2Fs3%2Faws4_request&X-Amz-Date=20190404T130643Z&X-Amz-SignedHeaders=host&X-Amz-Expires=86400&X-Amz-Signature=4e1ef2a12c304de0896f5faa2a472be5f66724d3c9778a73a1d623d30769c162

Time to execute: 0.00391s

You also mentioned Chilkat in your question. Chilkat can actually generate v4 signatures for you:

https://www.chilkatsoft.com/refdoc/xChilkatAuthAwsRef.html

https://www.example-code.com/asp/aws_pre_signed_url_v4.asp

It does require a license though. I bought one a few years back and I'd highly recommend it for anyone who still uses Classic ASP.




回答2:


Pretty sure you can somehow extract the code you need from CryptoJs (https://code.google.com/archive/p/crypto-js/) in order to use the CryptoJS.HmacSHA256() function. Store the needed function(s) in a JS file that you should then be able to include in your ASP code.

EDITED: Check the accepted answer to this question: How to get digest representation of CryptoJS.HmacSHA256 in JS



来源:https://stackoverflow.com/questions/55512946/creating-a-aws-v4-signature-for-awis

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