Hello Unity Community, I am trying to have a web-build of a project put a simple .csv file onto our Amazon S3 bucket. The idea is that the web-player would gather some information during play, and then store that information in .csv format on the bucket that we have created on Amazon S3.
I am using the WWW class, as well as the WWWForm class to build an HTML form, and send that to Amazon’s servers. The documentation for building the request form to amazon is here, the documentation for building the policy is here and the documentation on authentication and signing is here.
Below is the code that constructs the form:
using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Security.Cryptography;
public class AWSWebFormBuilder
{
// TODO: still need to store the accessKeyID of the actual root account (mturk needs this and can't use amazon IAM).
const string rootID = "REMOVED";
const string rootAccessKeyID = "";
const string rootSecretAccessKey = "";
const string appAccessKeyID = "REMOVED";
const string appSecretAccessKey = "REMOVED";
const double requestValidForSeconds = 15.0f;
string CreateSignature(string date, string region, string service, string policy)
{
// Source: http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
byte[] policyBytes = Encoding.Default.GetBytes(policy);
byte[] dateBytes = Encoding.Default.GetBytes(date);
byte[] regionBytes = Encoding.Default.GetBytes(region);
byte[] serviceBytes = Encoding.Default.GetBytes(service);
byte[] secretAccessKeyBytes = Encoding.Default.GetBytes("AWS4" + appSecretAccessKey);
byte[] requestBytes = Encoding.Default.GetBytes("aws4_request");
HMACSHA256 dateHash = new HMACSHA256(secretAccessKeyBytes);
dateHash.ComputeHash(dateBytes);
HMACSHA256 dateRegionHash = new HMACSHA256(dateHash.Hash);
dateRegionHash.ComputeHash(regionBytes);
HMACSHA256 dateRegionServiceHash = new HMACSHA256(dateRegionHash.Hash);
dateRegionServiceHash.ComputeHash(serviceBytes);
HMACSHA256 signingKey = new HMACSHA256(dateRegionServiceHash.Hash);
signingKey.ComputeHash(requestBytes);
HMACSHA256 finalSignature = new HMACSHA256(signingKey.Hash);
finalSignature.ComputeHash(policyBytes);
string signatureString = Convert.ToBase64String(finalSignature.Hash);
//Debug.Log(signature);
//string signature = BitConverter.ToString(finalSignature.Hash);
//signature = signature.Replace("-", "");
return signatureString;
}
public void BuildUploadRequest(ref WWWForm requestForm, string fileName, string fileContent)
{
DateTime currentTime = DateTime.UtcNow;
DateTime requestExpiryTime = currentTime.AddSeconds(requestValidForSeconds);
string iso8601ExpiryTime = requestExpiryTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
string iso8601CurrentTime = currentTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
iso8601CurrentTime = iso8601CurrentTime.Replace(" ", "").Replace("-", "").Replace(":", "");
string policy = "{ \"expiration\": \"" + iso8601ExpiryTime + "\"," +
" \"conditions\": [" +
" {\"bucket\": \"ourbucket\" }," +
" [\"starts-with\", \"$key\", \"Project 1 Output/\"]," +
" {\"x-amz-credential\": \"" + appAccessKeyID + "/" + currentTime.ToString("yyyymmdd") + "/us-east-1/s3/aws4_request\"}," +
" {\"x-amz-algorithm\": \"AWS4-HMAC-SHA256\"}," +
" {\"x-amz-date\": \"" + iso8601CurrentTime + "\" }" +
"]}";
policy = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
string signature = CreateSignature(currentTime.ToString("yyyymmdd"), "us-east-1", "s3", policy);
requestForm.AddField("AWSAccessKeyId", appAccessKeyID);
requestForm.AddField("policy", policy);
requestForm.AddField("key", fileName);
requestForm.AddField("x-amz-credential", appAccessKeyID + "/" + currentTime.ToString("yyyymmdd") + "/us-east-1/s3/aws4_request");
requestForm.AddField("x-amz-algorithm", "AWS4-HMAC-SHA256");
requestForm.AddField("x-amz-date", iso8601CurrentTime);
requestForm.AddField("Signature", signature);
requestForm.AddBinaryData("file", Encoding.UTF8.GetBytes(fileContent));
}
}
Now, based on the documentation, that code should work. Unfortunately it does not. Whenever I try to make a POST request to Amazon’s servers I get the following error:
403 Forbidden - SignatureDoesNotMatch -
The request signature we calculated does not match the signature you provided. Check your key and signing method.
As the error suggests, I must not be generating my signature correctly. I’ve been trying to figure this out for quite some time now, and I’m at my wit’s end as I don’t really know what I’m doing wrong.
I’ve also tried resetting the access & secret access keys on the IAM console, but that didn’t help.
Can anybody help me out?