经典ASP亚马逊s3休息授权

Chr*_*ell 12 rest vbscript wsh amazon-s3 asp-classic

我对这里的错误感到困惑......

<script language="javascript" runat="server">
  function GMTNow(){return new Date().toGMTString()}
</script>
<%

Const AWS_BUCKETNAME = "uk-bucketname"
Const AWS_ACCESSKEY = "GOES HERE"
Const AWS_SECRETKEY = "SECRET"
LocalFile = Server.Mappath("/test.jpg")

Dim sRemoteFilePath
    sRemoteFilePath = "/files/test.jpg" 'Remote Path, note that AWS paths (in fact they aren't real paths) are strictly case sensitive

Dim strNow
    strNow = GMTNow() ' GMT Date String

Dim StringToSign
    StringToSign = Replace("PUT\n\nimage/jpeg\n\nx-amz-date:" & strNow & "\n/"& AWS_BUCKETNAME & sRemoteFilePath, "\n", vbLf)

Dim Signature
    Signature = BytesToBase64(HMACSHA1(AWS_SECRETKEY, StringToSign))

Dim Authorization
    Authorization = "AWS " & AWS_ACCESSKEY & ":" & Signature

Dim AWSBucketUrl
    AWSBucketUrl = "http://s3.amazonaws.com/" & AWS_BUCKETNAME

With Server.CreateObject("Microsoft.XMLHTTP")
    .open "PUT", AWSBucketUrl & sRemoteFilePath, False
    .setRequestHeader "Authorization", Authorization
    .setRequestHeader "Content-Type", "image/jpeg"
    .setRequestHeader "Host", AWS_BUCKETNAME & ".s3.amazonaws.com"  
    .setRequestHeader "x-amz-date", strNow
    .send GetBytes(LocalFile) 'Get bytes of local file and send
    If .status = 200 Then ' successful
        Response.Write "<a href="& AWSBucketUrl & sRemoteFilePath &" target=_blank>Uploaded File</a>"
    Else ' an error ocurred, consider xml string of error details
        Response.ContentType = "text/xml"
        Response.Write .responseText
    End If
End With

Function GetBytes(sPath)
    dim fs,f
set fs=Server.CreateObject("Scripting.FileSystemObject")
set f=fs.GetFile(sPath)
GetBytes = f.Size
set f=nothing
set fs=nothing
End Function

Function BytesToBase64(varBytes)
    With Server.CreateObject("MSXML2.DomDocument").CreateElement("b64")
        .dataType = "bin.base64"
        .nodeTypedValue = varBytes
        BytesToBase64 = .Text
    End With
End Function

Function HMACSHA1(varKey, varValue)
    With Server.CreateObject("System.Security.Cryptography.HMACSHA1")
        .Key = UTF8Bytes(varKey)
        HMACSHA1 = .ComputeHash_2(UTF8Bytes(varValue))
    End With
End Function

Function UTF8Bytes(varStr)
    With Server.CreateObject("System.Text.UTF8Encoding")
        UTF8Bytes = .GetBytes_4(varStr)
    End With
End Function
%>
Run Code Online (Sandbox Code Playgroud)

现在得到错误.

msxml3.dll error '800c0008'

The download of the specified resource has failed.

/s3.asp, line 39
Run Code Online (Sandbox Code Playgroud)

Kul*_*gin 9

据我所知,我想解释一下S3 Rest Api是如何工作的.
首先,您需要了解亚马逊接受的字符串应该是什么.

格式:

StringToSign = HTTP-Verb + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Date + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;
Run Code Online (Sandbox Code Playgroud)

生成签名字符串:

Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
Run Code Online (Sandbox Code Playgroud)

传递授权标题:

Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
Run Code Online (Sandbox Code Playgroud)

不幸的是,由于没有为经典asp发布任何SDK,因此您将逐字节播放.所以,应该通过阅读整个页面来理解 http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html

对于要在格式中看到的字符串进行签名,API会保留三个本机标头.内容类型,内容-MD5日期.这些标题必须存在于字符串中以进行签名,即使您的请求没有标题名称也不是空的,只是它的值.有一个例外,Date如果x-amz-date请求中已存在标头,则标头必须为空才能签名.然后,如果请求具有规范的亚马逊标头,则应将它们添加为键值对x-amz-headername:value.但是,还需要考虑多个标头的另一个例外.多个标题应合并为一个标题,其值为逗号分隔.

正确

X-AMZ-headername:值1,值

错误

x-amz-headername:value1 \n
x-amz-headername:value2

最重要的是,标题必须按字符串中的组升序才能签名.首先,保留标题按升序排列,然后是标准标题按升序排列.

我建议使用DomDocument功能来生成Base64编码的字符串.此外,您可以使用.Net的互操作代替Windows脚本组件(.wsc文件),System.Security.Cryptography以便更有效地生成密钥哈希System.Text.所有这些互操作性都可以在今天的IIS Web服务器中使用.
因此,作为一个例子,我写下面的脚本只是将文件发送到您指定的存储桶.考虑并测试它.
假定的本地文件名是myimage.jpg,并将以相同的名称上传到存储桶的根目录.

<script language="javascript" runat="server">
function GMTNow(){return new Date().toGMTString()}
</script>
Run Code Online (Sandbox Code Playgroud)
<%
Const AWS_BUCKETNAME = "uk-bucketname"
Const AWS_ACCESSKEY = "GOES HERE"
Const AWS_SECRETKEY = "SECRET"

LocalFile = Server.Mappath("/test.jpg")

Dim sRemoteFilePath
    sRemoteFilePath = "/files/test.jpg" 'Remote Path, note that AWS paths (in fact they aren't real paths) are strictly case sensitive

Dim strNow
    strNow = GMTNow() ' GMT Date String

Dim StringToSign
    StringToSign = Replace("PUT\n\nimage/jpeg\n\nx-amz-date:" & strNow & "\n/"& AWS_BUCKETNAME & sRemoteFilePath, "\n", vbLf)

Dim Signature
    Signature = BytesToBase64(HMACSHA1(AWS_SECRETKEY, StringToSign))

Dim Authorization
    Authorization = "AWS " & AWS_ACCESSKEY & ":" & Signature

Dim AWSBucketUrl
    AWSBucketUrl = "https://" & AWS_BUCKETNAME & ".s3.amazonaws.com"

With Server.CreateObject("MSXML2.ServerXMLHTTP.6.0")
    .open "PUT", AWSBucketUrl & sRemoteFilePath, False
    .setRequestHeader "Authorization", Authorization
    .setRequestHeader "Content-Type", "image/jpeg"
    .setRequestHeader "Host", AWS_BUCKETNAME & ".s3.amazonaws.com"  
    .setRequestHeader "x-amz-date", strNow
    .send GetBytes(LocalFile) 'Get bytes of local file and send
    If .status = 200 Then ' successful
        Response.Write "<a href="& AWSBucketUrl & sRemoteFilePath &" target=_blank>Uploaded File</a>"
    Else ' an error ocurred, consider xml string of error details
        Response.ContentType = "text/xml"
        Response.Write .responseText
    End If
End With

Function GetBytes(sPath)
    With Server.CreateObject("Adodb.Stream")
        .Type = 1 ' adTypeBinary
        .Open
        .LoadFromFile sPath
        .Position = 0
        GetBytes = .Read
        .Close
    End With
End Function

Function BytesToBase64(varBytes)
    With Server.CreateObject("MSXML2.DomDocument").CreateElement("b64")
        .dataType = "bin.base64"
        .nodeTypedValue = varBytes
        BytesToBase64 = .Text
    End With
End Function

Function HMACSHA1(varKey, varValue)
    With Server.CreateObject("System.Security.Cryptography.HMACSHA1")
        .Key = UTF8Bytes(varKey)
        HMACSHA1 = .ComputeHash_2(UTF8Bytes(varValue))
    End With
End Function

Function UTF8Bytes(varStr)
    With Server.CreateObject("System.Text.UTF8Encoding")
        UTF8Bytes = .GetBytes_4(varStr)
    End With
End Function
%>
Run Code Online (Sandbox Code Playgroud)



Gra*_*ham 2

Amazon 签名的 url 编码方式必须与 VBSCript 编码方式略有不同。以下函数将正确编码结果:

\n\n

JScript 版本:

\n\n
function amazonEncode(s)\n{\n    return Server.UrlEncode(s).replace(/\\+/g,"%20").replace(/\\%2E/g,".").replace(/\\%2D/g,"-").replace(/\\%7E/g,"~").replace(/\\%5F/g,"_");\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

VBScript 版本:

\n\n
function amazonEncode(s)\n    dim retval\n    retval = Server.UrlEncode(s)\n    retval = replace(retval,"+","%20")\n    retval = replace(retval,"%2E",".")\n    retval = replace(retval,"%2D","-")\n    retval = replace(retval,"%7E","~")\n    retval = replace(retval,"%5F","_")\n    amazonEncode = retval\nend function\n
Run Code Online (Sandbox Code Playgroud)\n\n

至于base64,我使用了.NET 已经为其构建的功能。我必须创建一个 DLL 来包装它,以便我可以从 JScript(或 VBScript)使用它。

\n\n

创建该 dll 的方法如下:

\n\n
Download the free C# 2010 Express and install it.\nYou also need to use two other tools that you won\xe2\x80\x99t have a path to, so you will need to add the path to your PATH environment variable, so at a cmd prompt search for regasm.exe, guidgen.exe and sn.exe (you might find several versions \xe2\x80\x93 select the one with the latest date).\n\xe2\x80\xa2   cd\\\n\xe2\x80\xa2   dir/s regasm.exe\n\xe2\x80\xa2   dir/s sn.exe\n\xe2\x80\xa2   dir/s guidgen.exe\n\n\nSo as an example, a COM object that has just one method which just returns \xe2\x80\x9cHello\xe2\x80\x9d:\nOur eventual aim is to use it like this:\n<%@Language=JScript%>\n<%\nvar x = Server.CreateObject("blah.whatever");\nResponse.Write(x.someMethod());\n%>\n\nor \n\n<%@Language=VBScript%>\n<%\ndim x\nset x = Server.CreateObject("blah.whatever")\nResponse.Write x.someMethod()\n%>\n\n\xe2\x80\xa2   Start C# and create a new project\n\xe2\x80\xa2   Select \xe2\x80\x9cEmpty Project\xe2\x80\x9d\n\xe2\x80\xa2   Give it a name \xe2\x80\x93 this becomes the namespace by default (the blah in the sample above)\n\xe2\x80\xa2   Next save the project (so you know where to go for the next bit).  This will create a folder structure like so:\no   blah    this contains your solution files that the editor needs (blah.sln etc)\n\xef\x82\xa7   blah    this contains your source code and project files\n\xe2\x80\xa2   bin\no   Debug           the compiled output ends up here\n\xe2\x80\xa2   Next, using the cmd console, navigate to the root blah folder and create a key pair file:\n   sn \xe2\x80\x93k key.snk\n\xe2\x80\xa2   Next you need a unique guid (enter guidgen at the cmd prompt)\no   Select registry format\no   Click \xe2\x80\x9cNew Guid\xe2\x80\x9d\no   Click \xe2\x80\x9cCopy\xe2\x80\x9d\n\xe2\x80\xa2   Back to C# editor \xe2\x80\x93 from the menu, select Project \xe2\x80\x93 Add Class\n\xe2\x80\xa2   Give it a name \xe2\x80\x93 this is the whatever in the sample above\n\xe2\x80\xa2   After the opening brace just after the namespace line type:\n   [GuidAttribute(\xe2\x80\x9cpaste your guid here\xe2\x80\x9d)]\n   remove the curly brackets from your pasted guid\n\xe2\x80\xa2   You will need to add another \xe2\x80\x9cusing\xe2\x80\x9d at the top\n  using System.Runtime.InteropServices;\n\xe2\x80\xa2   Finally you need to create someMethod\n\nThe final C# code looks like this (the bits in red may be different in your version):\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Runtime.InteropServices;\n\nnamespace blah\n{\n    [GuidAttribute("AEF4F27F-9E97-4189-9AD5-64386A1699A7")]\n    public class whatever\n    {\n        public string someMethod()\n        {\n            return "Hello";\n        }\n    }\n}\n\n\xe2\x80\xa2   Next, from the menu, select Project \xe2\x80\x93 Properties\no   On the left, select Application and, for the Output type dropdown, select \xe2\x80\x9cClass Library\xe2\x80\x9d\no   On the left, select Signing and tick the \xe2\x80\x9cSign the assembly\xe2\x80\x9d box, then browse to the key.snk file you made earlier\no   Save the properties (CTRL-S)\n\xe2\x80\xa2   Next build the dll (Press F6) \xe2\x80\x93 This will create a dll in the Debug folder\n\xe2\x80\xa2   Open a cmd window as administrator (right click cmd.exe and select \xe2\x80\x9cRun as Administrator\xe2\x80\x9d)\n\xe2\x80\xa2   Navigate to the Debug folder and enter the following to register the assembly:\n  regasm blah.dll /tlb:blah.tlb /codebase blah\n\nThat\xe2\x80\x99s it \xe2\x80\x93 the above is a genuine COM component and will work in other applications, the example below allows for event handling and only really works in ASP due to the default property mechanism of ASP:\n
Run Code Online (Sandbox Code Playgroud)\n\n

Base64 的代码是:

\n\n
    // returns a base 64 encoded string that has been encrypted with SHA256\n    // parameters:\n    //  s   string to encrypt\n    //  k   key to use during encryption\n    public string getBase64SHA256(string s, string k)\n    {\n        HMACSHA256 sha = new HMACSHA256();\n        System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();\n        sha.Key = encoding.GetBytes(k);\n        byte[] hashBytes = sha.ComputeHash(encoding.GetBytes(s));\n        return System.Convert.ToBase64String(hashBytes);\n    }\n\n    // returns a base 64 encoded string that has been encrypted with SHA1\n    // parameters:\n    //  s   string to encrypt\n    //  k   key to use during encryption\n    public string getBase64SHA1(string s, string k)\n    {\n        HMACSHA1 sha = new HMACSHA1();\n        System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();\n        sha.Key = encoding.GetBytes(k);\n        byte[] hashBytes = sha.ComputeHash(encoding.GetBytes(s));\n        return System.Convert.ToBase64String(hashBytes);\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

您需要相关的用法:

\n\n
using System.Security.Cryptography;\n
Run Code Online (Sandbox Code Playgroud)\n\n

在计算 SHA 和 base64 之前,完整签名必须包含按字母顺序排列的所有查询字符串名称-值对。这是我的签名创建器功能版本:

\n\n
function buildAmazonSignature(host,req,qstring)\n{\n    var str="", i, arr = String(qstring).split("&");\n\n    for (i=0; i<arr.length; i++)\n        arr[i] = arr[i].split("=");\n    arr.sort(amazonSortFunc);\n\n    for (i=0; i<arr.length; i++)\n    {\n        if (str != "")\n            str += "&";\n\n        str += arr[i][0] + "=" + arr[i][1];\n    }\n\n    str = "GET\\n"+host+"\\n"+req+"\\n"+str;\n\n    var utils = Server.CreateObject("FMAG.Utils");\n    var b64 = utils.getBase64SHA256(str, "xxxxxxxxxx");\n    utils = null;\n\n    return amazonEncode(b64);\n}\n\nfunction amazonSortFunc(a,b)\n{\n    return (a[0]<b[0])?-1:((a[0]>b[0])?1:0);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

VBScript 没有很好的数组排序功能,因此您必须自己解决这个问题 - 抱歉

\n\n

我还有以下格式的时间戳:

\n\n

YYYY-MM-DDTHH:MM:SSZ

\n\n

查询字符串中的内容还包括以下内容:

\n\n
AWSAccessKeyId\nSignatureMethod\nSignatureVersion\nVersion\nExpires\nAction\n
Run Code Online (Sandbox Code Playgroud)\n\n

希望有帮助

\n