2012-06-13 110 views
12

我是我做錯了什麼在這裏混淆...經典ASP Amazon S3的休息授權

<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 
+0

你有沒有設法找到你的問題克里斯? – TheCarver

+0

還沒有:( –

+0

)您好Chris,您是否註冊了Amazon S3服務以及AWS賬戶?請參閱此主題https://forums.aws.amazon.com/thread.jspa?threadID=45582 –

回答

2

亞馬遜簽名必須以與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); 
    } 

您需要的相關usings:

using System.Security.Cryptography; 

簽名完全必須在計算SHA和B之前按字母順序包含所有查詢字符串名稱 - 值對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 

希望可以幫到

9

我想解釋S3 Rest Api如何工作,據我所知。
首先,您需要了解Amazon應接受的字符串。

格式:

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; 

不幸的是,你會播放字節到字節,因爲沒有任何SDK爲傳統的ASP發佈。所以,應該通過讀取整個頁面http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html

對於字符串籤,你可以在格式見上文理解,有三種天然頭通過API保留。 Content-TypeContent-MD5 and Date。這些頭文件必須存在於字符串中才能簽名,即使您的請求沒有將它們作爲空白而沒有頭名稱,也只是它的值。如果請求中已存在x-amz-date標頭,則有一個例外,Date標頭中必須爲空,才能簽名。然後,如果請求具有規範的amazon標題,則應將它們添加爲鍵值對,如x-amz-headername:value。但是,還有一個例外需要考慮多個頭文件。多個標題應該合併爲一個標頭,並用逗號分隔。

正確

的x AMZ-headername:值1,值

錯誤

的x AMZ-headername:VALUE1 \ n
X -amz-headername:value2

最重要的是,標題必須是由它的組中的字符串升序排列的符號。首先,按升序保留標題,然後按升序排列標準標題。

我建議使用DomDocument功能來生成Base64編碼的字符串。 此外,除了Windows腳本組件(.wsc文件)之外,還可以使用.Net的interops(如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 
%> 


+0

我收到錯誤「msxml6.dll錯誤」80072f7c' HTTP重定向請求失敗「任何想法可能導致它? –

+0

@ChrisDowdeswell除了存儲區名稱,訪問密鑰和密鑰之外,您對代碼進行了哪些更改? –

+0

除了你放置的變量之外什麼都沒有...... :( –

0

非常感謝你對這個問題,一直這樣很大的幫助,開始我WSH/VBScript中爲我的S3備份服務;-)

我沒有太多時間,所以我不會詳細講解我從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 

親愛的石頭邊技術的VBScript的隊友(*),讓我知道,如果它是爲你工作作爲好;-)

(*)這是一個參考從斯普德利的評論,見上面;-)