I am confused on what I am doing wrong here...
I'd like to explain how S3 Rest Api works as far as I know.
First, you need to learn what should be the string to sign Amazon accepts.
Format :
StringToSign = HTTP-Verb + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Date + "\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
Generating signed string :
Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
Passing authorization header:
Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
Unfortunately you'll play byte to byte since there is no any SDK released for classic asp. So, should understand by reading the entire page http://docs.amazonwebservices.com/AmazonS3/latest/dev/RESTAuthentication.html
For string to sign as you can see above in format, there are three native headers are reserved by the API. Content-Type, Content-MD5 and Date. These headers must be exists in the string to sign even your request hasn't them as empty without header name, just its value. There is an exception, Date
header must be empty in string to sign if x-amz-date
header is already exists in the request. Then, If request has canonical amazon headers, you should add them as key-value pairs like x-amz-headername:value
. But, there is another exception need to be considered for multiple headers. Multiple headers should combine to one header with values comma separated.
Correct
x-amz-headername:value1,value2
Wrong
x-amz-headername:value1\n
x-amz-headername:value2
Most importantly, headers must be ascending order by its group in the string to sign. First, reserved headers with ascending order, then canonical headers with ascending order.
I'd recommend using DomDocument functionality to generate Base64 encoded strings.
Additionally instead of a Windows Scripting Component (.wsc files), you could use .Net's interops such as System.Security.Cryptography
to generating keyed hashes more effectively with power of System.Text
. All of these interoperabilities are available in today's IIS web servers.
So, as an example I wrote the below script just sends a file to bucket you specified. Consider and test it.
Assumed local file name is myimage.jpg
and will be uploaded with same name to root of the bucket.
<%
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 "Uploaded File"
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
%>