【发布时间】:2017-04-10 16:52:24
【问题描述】:
我正在开发一个用户应该能够将视频文件上传到 AWS 的网站。为了避免不必要的流量,我希望用户直接上传到 AWS(而不是通过 API 服务器)。为了不在 JavaScript 中公开我的密钥,我试图在 API 中生成签名。但是,当我尝试上传时,它会告诉我签名不匹配。
对于签名生成,我一直在使用http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-UsingHTTPPOST.html
我在后端运行 C#。
我使用生成签名
string policy = $@"{{""expiration"":""{expiration}"",""conditions"":[{{""bucket"":""dennisjakobsentestbucket""}},[""starts-with"",""$key"",""""],{{""acl"":""private""}},[""starts-with"",""$Content-Type"",""""],{{""x-amz-algorithm"":""AWS4-HMAC-SHA256""}}]}}";
生成以下内容
{"expiration":"2016-11-27T13:59:32Z","conditions":[{"bucket":"dennisjakobsentestbucket"},["starts-with","$key",""],{"acl":"private"},["starts-with","$Content-Type",""],{"x-amz-algorithm":"AWS4-HMAC-SHA256"}]}
基于http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-HTTPPOSTConstructPolicy.html(我对策略进行base64编码)。我尽量保持简单,作为一个起点。
为了生成签名,我使用了 AWS 网站上的代码。
static byte[] HmacSHA256(String data, byte[] key)
{
String algorithm = "HmacSHA256";
KeyedHashAlgorithm kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(Encoding.UTF8.GetBytes(data));
}
static byte[] GetSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);
return kSigning;
}
我是这样使用的:
byte[] signingKey = GetSignatureKey(appSettings["aws:SecretKey"], dateString, appSettings["aws:Region"], "s3");
byte[] signature = HmacSHA256(encodedPolicy, signingKey);
dateString 的格式为 yyyymmdd
我使用 JavaScript 发布信息
let xmlHttpRequest = new XMLHttpRequest();
let formData = new FormData();
formData.append("key", "<path-to-upload-location>");
formData.append("acl", signature.acl); // private
formData.append("Content-Type", "$Content-Type");
formData.append("AWSAccessKeyId", signature.accessKey);
formData.append("policy", signature.policy); //base64 of policy
formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request
formData.append("x-amz-date", signature.date);
formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");
formData.append("Signature", signature.signature);
formData.append("file", file);
xmlHttpRequest.open("post", "http://<bucketname>.s3-eu-west-1.amazonaws.com/");
xmlHttpRequest.send(formData);
我一直在按照 AWS 的规定使用 UTF8。在他们的示例中,签名采用十六进制格式,我也尝试过。 无论我尝试什么,我都会收到错误 403
The request signature we calculated does not match the signature you provided. Check your key and signing method.
我在 AWS 上的策略有“s3:Get*”、“s3:Put*”
是我遗漏了什么,还是它的工作方式与我的预期完全不同?
编辑:下面的答案是其中一个步骤。另一个是AWS区分大小写十六进制字符串。 0xFF != AWS 眼中的 0xff。他们想要全小写的签名。
【问题讨论】:
标签: javascript c# amazon-web-services amazon-s3