2012-06-13 15 views
12

estoy confundido sobre lo que estoy haciendo mal aquí ...ASP clásico Amazon S3 autorización resto

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

Ahora conseguir el error.

msxml3.dll error '800c0008' 

The download of the specified resource has failed. 

/s3.asp, line 39 
+0

¿Se las arregló para encontrar su problema, Chris? – TheCarver

+0

No, todavía no :( –

+0

Hola, Chris, ¿has iniciado sesión para el servicio de Amazon S3, así como una cuenta de AWS? Mira este hilo https://forums.aws.amazon.com/thread.jspa?threadID=45582 –

Respuesta

2

La firma de Amazon debe estar codificada en URL de una manera ligeramente diferente a lo que VBSCript codifica. La siguiente función codificará el resultado correcto:

JScript Versión:

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

VBScript Versión:

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 

En cuanto a la base 64, solía .NET ya está incorporado funcionalidad para ello. Tuve que crear una DLL para envolverlo, para poder usarlo desde JScript (o VBScript).

Aquí es cómo crear esa 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: 

El código para la materia de base 64 sería:

// 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); 
    } 

podría necesitar el usings relevantes:

using System.Security.Cryptography; 

La firma en su totalidad debe tener todos los pares de nombre-valor de cadena de consulta en orden alfabético antes de calcular el SHA y base64. Aquí está mi versión de la función de firma creador:

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 no tener una muy buena instalación gama especie, por lo que tendrá que trabajar que uno mismo - lo siento

También tengo el fecha y hora en este formato:

AAAA-MM-DDTHH: MM: SSZ

también las cosas en la cadena de consulta incluyó lo siguiente:

AWSAccessKeyId 
SignatureMethod 
SignatureVersion 
Version 
Expires 
Action 

Espero que ayude

9

Me gustaría explicar cómo S3 Rest Api funciona hasta donde yo sé.
En primer lugar, debe saber cuál debe ser la cadena que Amazon debe firmar.

Formato:

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

Generación cadena firmado:

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

Pasando cabecera de autorización:

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

Lamentablemente, jugará byte a byte ya que no hay ningún SDK lanzado para asp clásico. Así, debe entender mediante la lectura de toda la páginahttp://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html

Para cadena para firmar como se puede ver arriba en el formato, hay tres cabeceras nativos están reservados por la API. Content-Type, Content-MD5 y Fecha. Estos encabezados deben existir en la cadena para firmar, incluso si su solicitud no los tiene como vacíos sin el nombre del encabezado, solo su valor. Existe una excepción, el encabezado Date debe estar vacío en la cadena para firmar si el encabezado x-amz-date ya existe en la solicitud. Luego, si request tiene canonical amazon headers, debe agregarlos como pares clave-valor como x-amz-headername:value. Pero, hay otra excepción que debe considerarse para múltiples encabezados. Los encabezados múltiples deben combinarse con un encabezado con valores separados por comas.

correcta

x-amz-headername: valor1, valor2

erróneos

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

Lo más importante es, los encabezados deben estar en orden ascendente por su grupo en la cadena a firmar. Primero, los encabezados reservados con orden ascendente, luego los encabezados canónicos con orden ascendente.

Recomendaría usar la funcionalidad DomDocument para generar cadenas codificadas en Base64. Además, en lugar de un Componente de secuencias de comandos de Windows (archivos .wsc), puede usar las interoperabilidades de .Net como System.Security.Cryptography para generar hashes con clave de forma más efectiva con el poder de System.Text. Todas estas interoperabilidades están disponibles en los servidores web actuales de IIS.
Así que, como un ejemplo, escribí la siguiente secuencia de comandos simplemente envía un archivo al depósito que usted especificó. Considera y prueba.
El nombre de archivo local supuesto es myimage.jpg y se cargará con el mismo nombre en la raíz del depósito.

<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

Recibo el mensaje de error "msxml6.dll error '80072f7c' La solicitud de redireccionamiento HTTP ha fallado" ¿Alguna idea de qué podría estar causando esto? –

+0

@ChrisDowdeswell ¿Qué cambios ha realizado en el código a excepción del nombre del depósito, la clave de acceso y la clave secreta? –

+0

Nada excepto las variables que pones ... :( –

0

Muchas gracias por esta pregunta, ha sido una gran ayuda para comenzar mi WSH/VBScript para mi servicio de copia de seguridad S3 ;-)

No tengo mucho tiempo, así que no revisaré los detalles de las cosas que cambié del código de Chris, pero A continuación mi pequeño script prototipo que funciona perfectamente ;-)

Esto es sólo una WSH/VBScript, por lo que no necesita IIS para ejecutarlo, sólo tiene que pegar el contenido en un archivo con el " .vbs" extensión, y se puede ejecutar directamente después que ;-)

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 

Estimado piedra de borde tecnología de VBScript-compañeros (*), que me haga saber si está funcionando para usted como bien ;-)

(*) Esto es una referencia al comentario de Spudley, ver arriba ;-)