12

我对我在这里做错了什么感到困惑......

<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
%>

现在得到错误。

msxml3.dll error '800c0008'

The download of the specified resource has failed.

/s3.asp, line 39
4

3 回答 3

9

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

格式 :

StringToSign = HTTP-Verb + "\n" +
    Content-MD5 + "\n" +
    Content-Type + "\n" +
    Date + "\n" +
    CanonicalizedAmzHeaders +
    CanonicalizedResource;

生成签名字符串:

Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );

传递授权标头:

Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;

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

对于要签名的字符串,如您在上面看到的格式,API 保留了三个本机标头。Content-TypeContent-MD5Date。这些标头必须存在于字符串中才能签名,即使您的请求在没有标头名称的情况下它们不是空的,只是它的值。有一个例外,如果请求中已经存在标头,Date则标头必须在字符串中为空才能签名。x-amz-date然后,如果请求具有规范的亚马逊标头,您应该将它们添加为键值对,例如x-amz-headername:value. 但是,对于多个标头,还需要考虑另一个例外情况。多个标头应合并为一个标头,其中值以逗号分隔。

正确的

x-amz-headername:value1,value2

错误的

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>
<%
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
%>


于 2012-09-20T20:49:32.603 回答
2

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

JScript 版本:

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

VBScript 版本:

function amazonEncode(s)
    dim retval
    retval = Server.UrlEncode(s)
    retval = replace(retval,"+","%20")
    retval = replace(retval,"%2E",".")
    retval = replace(retval,"%2D","-")
    retval = replace(retval,"%7E","~")
    retval = replace(retval,"%5F","_")
    amazonEncode = retval
end function

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

以下是创建该 dll 的方法:

Download the free C# 2010 Express and install it.
You also need to use two other tools that you won’t 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 – select the one with the latest date).
•   cd\
•   dir/s regasm.exe
•   dir/s sn.exe
•   dir/s guidgen.exe


So as an example, a COM object that has just one method which just returns “Hello”:
Our eventual aim is to use it like this:
<%@Language=JScript%>
<%
var x = Server.CreateObject("blah.whatever");
Response.Write(x.someMethod());
%>

or 

<%@Language=VBScript%>
<%
dim x
set x = Server.CreateObject("blah.whatever")
Response.Write x.someMethod()
%>

•   Start C# and create a new project
•   Select “Empty Project”
•   Give it a name – this becomes the namespace by default (the blah in the sample above)
•   Next save the project (so you know where to go for the next bit).  This will create a folder structure like so:
o   blah    this contains your solution files that the editor needs (blah.sln etc)
   blah    this contains your source code and project files
•   bin
o   Debug           the compiled output ends up here
•   Next, using the cmd console, navigate to the root blah folder and create a key pair file:
   sn –k key.snk
•   Next you need a unique guid (enter guidgen at the cmd prompt)
o   Select registry format
o   Click “New Guid”
o   Click “Copy”
•   Back to C# editor – from the menu, select Project – Add Class
•   Give it a name – this is the whatever in the sample above
•   After the opening brace just after the namespace line type:
   [GuidAttribute(“paste your guid here”)]
   remove the curly brackets from your pasted guid
•   You will need to add another “using” at the top
  using System.Runtime.InteropServices;
•   Finally you need to create someMethod

The final C# code looks like this (the bits in red may be different in your version):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace blah
{
    [GuidAttribute("AEF4F27F-9E97-4189-9AD5-64386A1699A7")]
    public class whatever
    {
        public string someMethod()
        {
            return "Hello";
        }
    }
}

•   Next, from the menu, select Project – Properties
o   On the left, select Application and, for the Output type dropdown, select “Class Library”
o   On the left, select Signing and tick the “Sign the assembly” box, then browse to the key.snk file you made earlier
o   Save the properties (CTRL-S)
•   Next build the dll (Press F6) – This will create a dll in the Debug folder
•   Open a cmd window as administrator (right click cmd.exe and select “Run as Administrator”)
•   Navigate to the Debug folder and enter the following to register the assembly:
  regasm blah.dll /tlb:blah.tlb /codebase blah

That’s it – 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:

base64 的代码是:

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

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

您将需要相关用途:

using System.Security.Cryptography;

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

function buildAmazonSignature(host,req,qstring)
{
    var str="", i, arr = String(qstring).split("&");

    for (i=0; i<arr.length; i++)
        arr[i] = arr[i].split("=");
    arr.sort(amazonSortFunc);

    for (i=0; i<arr.length; i++)
    {
        if (str != "")
            str += "&";

        str += arr[i][0] + "=" + arr[i][1];
    }

    str = "GET\n"+host+"\n"+req+"\n"+str;

    var utils = Server.CreateObject("FMAG.Utils");
    var b64 = utils.getBase64SHA256(str, "xxxxxxxxxx");
    utils = null;

    return amazonEncode(b64);
}

function amazonSortFunc(a,b)
{
    return (a[0]<b[0])?-1:((a[0]>b[0])?1:0);
}

VBScript 没有很好的数组排序工具,所以你必须自己解决这个问题 - 抱歉

我也有这种格式的时间戳:

YYYY-MM-DDTHH:MM:SSZ

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

AWSAccessKeyId
SignatureMethod
SignatureVersion
Version
Expires
Action

希望有帮助

于 2012-09-19T10:57:41.497 回答
0

非常感谢您提出这个问题,为我的 S3 备份服务启动 WSH/VBScript 帮助很大;-)

我没有太多时间,所以我不会详细介绍我从 Chris 的代码中更改的内容,但请在下面找到我的完美运行的小原型脚本;-)

这只是一个 WSH/VBScript,所以你不需要 IIS 来运行它,你只需要将内容粘贴到一个扩展名为“.vbs”的文件中,然后你就可以直接执行它;-)

Option Explicit
'-- Amazon Web Services > My Account > Access Credentials > Access Keys --'
Dim strAccessKeyID: strAccessKeyID = "..."
Dim strSecretAccessKey: strSecretAccessKey = "..."
'-- Parameters: --'
Dim strLocalFile: strLocalFile = "..."
Dim strRemoteFile: strRemoteFile = "..."
Dim strBucket: strBucket = "..."
'-- Authentication: --'
Dim strNowInGMT: strNowInGMT = NowInGMT()
Dim strStringToSign: strStringToSign = _
  "PUT" & vbLf & _
  "" & vbLf & _
  "text/xml" & vbLf & _
  strNowInGMT & vbLf & _
  "/" & strBucket + "/" & strRemoteFile
Dim strSignature: strSignature = ConvertBytesToBase64(HMACSHA1(strSecretAccessKey, strStringToSign))
Dim strAuthorization: strAuthorization = "AWS " & strAccessKeyID & ":" & strSignature
'-- Upload: --'
Dim xhttp: Set xhttp = CreateObject("MSXML2.ServerXMLHTTP")
xhttp.open "PUT", "http://" & strBucket & ".s3.amazonaws.com/" & strRemoteFile, False
xhttp.setRequestHeader "Content-Type", "text/xml"
xhttp.setRequestHeader "Date", strNowInGMT 'Yes, this line is mandatory ;-) --'
xhttp.setRequestHeader "Authorization", strAuthorization
xhttp.send GetBytesFromFile(strLocalFile)
If xhttp.status = "200" Then
  WScript.Echo "The file has been successfully uploaded ;-)"
Else
  WScript.Echo "There was an error :-(" & vbCrLf & vbCrLf & _
  xhttp.responseText
End If
Set xhttp = Nothing
'-- NowInGMT ------------------------------------------------------------------'
Function NowInGMT()
  'This is probably not the best implementation, but it works ;-) --'
  Dim sh: Set sh = WScript.CreateObject("WScript.Shell")
  Dim iOffset: iOffset = sh.RegRead("HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias")
  Dim dtNowGMT: dtNowGMT = DateAdd("n", iOffset, Now())
  Dim strDay: strDay = "NA"
  Select Case Weekday(dtNowGMT)
    Case 1 strDay = "Sun"
    Case 2 strDay = "Mon"
    Case 3 strDay = "Tue"
    Case 4 strDay = "Wed"
    Case 5 strDay = "Thu"
    Case 6 strDay = "Fri"
    Case 7 strDay = "Sat"
    Case Else strDay = "Error"
  End Select
  Dim strMonth: strMonth = "NA"
  Select Case Month(dtNowGMT)
    Case 1 strMonth = "Jan"
    Case 2 strMonth = "Feb"
    Case 3 strMonth = "Mar"
    Case 4 strMonth = "Apr"
    Case 5 strMonth = "May"
    Case 6 strMonth = "Jun"
    Case 7 strMonth = "Jul"
    Case 8 strMonth = "Aug"
    Case 9 strMonth = "Sep"
    Case 10 strMonth = "Oct"
    Case 11 strMonth = "Nov"
    Case 12 strMonth = "Dec"
    Case Else strMonth = "Error"
  End Select
  Dim strHour: strHour = CStr(Hour(dtNowGMT))
  If Len(strHour) = 1 Then strHour = "0" & strHour End If
  Dim strMinute: strMinute = CStr(Minute(dtNowGMT))
  If Len(strMinute) = 1 Then strMinute = "0" & strMinute End If
  Dim strSecond: strSecond = CStr(Second(dtNowGMT))
  If Len(strSecond) = 1 Then strSecond = "0" & strSecond End If
  Dim strNowInGMT: strNowInGMT = _
    strDay & _
    ", " & _
    Day(dtNowGMT) & _
    " " & _
    strMonth & _
    " " & _
    Year(dtNowGMT) & _
    " " & _
    strHour & _
    ":" & _
    strMinute & _
    ":" & _
    strSecond & _
    " +0000"
  NowInGMT = strNowInGMT
End Function
'-- GetBytesFromString --------------------------------------------------------'
Function GetBytesFromString(strValue)
  Dim stm: Set stm = CreateObject("ADODB.Stream")
  stm.Open
  stm.Type = 2
  stm.Charset = "ascii"
  stm.WriteText strValue
  stm.Position = 0
  stm.Type = 1
  GetBytesFromString = stm.Read
  Set stm = Nothing
End Function
'-- HMACSHA1 ------------------------------------------------------------------'
Function HMACSHA1(strKey, strValue)
  Dim sha1: Set sha1 = CreateObject("System.Security.Cryptography.HMACSHA1")
  sha1.key = GetBytesFromString(strKey)
  HMACSHA1 = sha1.ComputeHash_2(GetBytesFromString(strValue))
  Set sha1 = Nothing
End Function
'-- ConvertBytesToBase64 ------------------------------------------------------'
Function ConvertBytesToBase64(byteValue)
  Dim dom: Set dom = CreateObject("MSXML2.DomDocument")
  Dim elm: Set elm = dom.CreateElement("b64")
  elm.dataType = "bin.base64"
  elm.nodeTypedValue = byteValue
  ConvertBytesToBase64 = elm.Text
  Set elm = Nothing
  Set dom = Nothing
End Function
'-- GetBytesFromFile ----------------------------------------------------------'
Function GetBytesFromFile(strFileName)
  Dim stm: Set stm = CreateObject("ADODB.Stream")
  stm.Type = 1 'adTypeBinary --'
  stm.Open
  stm.LoadFromFile strFileName
  stm.Position = 0
  GetBytesFromFile = stm.Read
  stm.Close
  Set stm = Nothing
End Function

亲爱的stone-edge-technology-VBScript-mates (*),让我知道它是否也适合您;-)

(*) 这是对 Spudley 评论的引用,见上文 ;-)

于 2013-01-12T21:13:05.580 回答