Ivona请求签名问题 – 签名不匹配(AWS签名版本4)

我正在尝试基于此documnent实施Ivona请求签名

一切正常,所有结果都与示例值匹配,但签名结果除外。 所以签名的结果是cf1141e33a8fbba23913f8f36f29faa524a57db37690a1b819f43bbeaabf3b76,但在文件中它等于2cdfef28d5c5f6682280600a6141a8940c608cfefacb47f172329cbadb5864cc

是Ivona文件中的错误还是错误?

下面是我使用的C#代码:

class Program { static void Main() { try { Console.WriteLine(SendIvonaRequest()); } catch (Exception ex) { Console.WriteLine(ex.Message); } } private static string SendIvonaRequest() { var date = new DateTime(2013, 09, 13, 09, 20, 54, DateTimeKind.Utc); const string algorithm = "AWS4-HMAC-SHA256"; const string regionName = "eu-west-1"; const string serviceName = "tts"; const string method = "POST"; const string canonicalUri = "/CreateSpeech"; const string canonicalQueryString = ""; const string contentType = "application/json"; const string accessKey = "MyAccessKey"; const string secretKey = "MySecretKey"; const string host = serviceName + "." + regionName + ".ivonacloud.com"; const string requestPayload = "{\"Input\":{\"Data\":\"Hello world\"}}"; var hashedRequestPayload = HexEncode(Hash(ToBytes(requestPayload))); Debug.Assert(hashedRequestPayload.Equals("f43e25253839f2c3feae433c5e477d79f7dfafdc0e4af19a952adb44a60265ba")); var dateStamp = date.ToString("yyyyMMdd"); var requestDate = date.ToString("yyyyMMddTHHmmss") + "Z"; var credentialScope = string.Format("{0}/{1}/{2}/aws4_request", dateStamp, regionName, serviceName); var headers = new SortedDictionary { {"content-type", "application/json"}, {"host", "tts.eu-west-1.ivonacloud.com"}, {"x-amz-content-sha256", hashedRequestPayload}, {"x-amz-date", requestDate} }; string canonicalHeaders = string.Join("\n", headers.Select(x => x.Key.ToLowerInvariant() + ":" + x.Value.Trim())) + "\n"; const string signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date"; // Task 1: Create a Canonical Request For Signature Version 4 var canonicalRequest = method + '\n' + canonicalUri + '\n' + canonicalQueryString + '\n' + canonicalHeaders + '\n' + signedHeaders + '\n' + hashedRequestPayload; var hashedCanonicalRequest = HexEncode(Hash(ToBytes(canonicalRequest))); Debug.Assert(hashedCanonicalRequest.Equals("73ff17c0bf9da707afb02bbceb77d359ab945a460b5ac9fff7a0a61cfaab95e6")); // Task 2: Create a String to Sign for Signature Version 4 // StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HashedCanonicalRequest var stringToSign = string.Format("{0}\n{1}\n{2}\n{3}", algorithm, requestDate, credentialScope, hashedCanonicalRequest); Debug.Assert(stringToSign.Equals("AWS4-HMAC-SHA256" + "\n" + "20130913T092054Z" + "\n" + "20130913/eu-west-1/tts/aws4_request" + "\n" + "73ff17c0bf9da707afb02bbceb77d359ab945a460b5ac9fff7a0a61cfaab95e6")); // Task 3: Calculate the AWS Signature Version 4 // HMAC(HMAC(HMAC(HMAC("AWS4" + kSecret,"20130913"),"eu-west-1"),"tts"),"aws4_request") byte[] signingKey = GetSignatureKey(secretKey, dateStamp, regionName, serviceName); // signature = HexEncode(HMAC(derived-signing-key, string-to-sign)) var signature = HexEncode(HmacSha256(stringToSign, signingKey)); Debug.Assert(signature.Equals("2cdfef28d5c5f6682280600a6141a8940c608cfefacb47f172329cbadb5864cc")); // Task 4: Prepare a signed request // Authorization: algorithm Credential=access key ID/credential scope, SignedHeadaers=SignedHeaders, Signature=signature var authorization = string.Format("{0} Credential={1}/{2}/{3}/{4}/aws4_request, SignedHeaders={5}, Signature={6}", algorithm, accessKey, dateStamp, regionName, serviceName, signedHeaders, signature); // Send the request var webRequest = WebRequest.Create("https://" + host + canonicalUri); webRequest.Method = method; webRequest.Timeout = 2000; webRequest.ContentType = contentType; webRequest.Headers.Add("X-Amz-date", requestDate); webRequest.Headers.Add("Authorization", authorization); webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload); webRequest.ContentLength = requestPayload.Length; using (Stream newStream = webRequest.GetRequestStream()) { newStream.Write(ToBytes(requestPayload), 0, requestPayload.Length); } var response = (HttpWebResponse) webRequest.GetResponse(); using (Stream responseStream = response.GetResponseStream()) { if (responseStream != null) { using (var streamReader = new StreamReader(responseStream)) { return streamReader.ReadToEnd(); } } } return string.Empty; } private static byte[] GetSignatureKey(String key, String dateStamp, String regionName, String serviceName) { byte[] kDate = HmacSha256(dateStamp, ToBytes("AWS4" + key)); byte[] kRegion = HmacSha256(regionName, kDate); byte[] kService = HmacSha256(serviceName, kRegion); return HmacSha256("aws4_request", kService); } private static byte[] ToBytes(string str) { return Encoding.UTF8.GetBytes(str.ToCharArray()); } private static string HexEncode(byte[] bytes) { return BitConverter.ToString(bytes).Replace("-", string.Empty).ToLowerInvariant(); } private static byte[] Hash(byte[] bytes) { var sha256 = SHA256.Create(); return sha256.ComputeHash(bytes); } private static byte[] HmacSha256(String data, byte[] key) { return new HMACSHA256(key).ComputeHash(ToBytes(data)); } } 

UPD1:

我还尝试了http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html中的示例,并注意到我的代码生成了与这些示例中相同的签名。 所以我假设Ivona文件中存在问题……

UPD2:

一切正常! 我已根据描述实现了CreateSpeech方法,并将使用的完整示例上传到GitHub https://github.com/MalyutinS/DotNetIvonaAPI

解决了! 实际上文档示例中存在问题。 所以代码工作正常。

它现在有点旧了,但它可能与使用此代码的其他人相关;

只要你只坚持ASCII字符,代码就可以正常工作。 对于英语以外的语言,必须先将字符串转换为UTF-8字节数组。 该数组可能比字符串中的字符数长,因此通过WebRequest发送的字节数组的长度必须反映数组本身的长度,而不是字符数。

真的是一个经典,但无论如何相关指出..

此错误导致签名不正确,因此出现身份validation错误。 原因可能并不明显。

那么现有的代码;

  webRequest.Method = method; webRequest.Timeout = 2000; webRequest.ContentType = contentType; webRequest.Headers.Add("X-Amz-date", requestDate); webRequest.Headers.Add("Authorization", authorization); webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload); webRequest.ContentLength = requestPayload.Length; using (Stream newStream = webRequest.GetRequestStream()) { newStream.Write(ToBytes(requestPayload), 0, requestPayload.Length); } 

变为:

  var bytes = ToBytes(requestPayload); webRequest.Method = method; webRequest.Timeout = 2000; webRequest.ContentType = contentType; webRequest.Headers.Add("X-Amz-date", requestDate); webRequest.Headers.Add("Authorization", authorization); webRequest.Headers.Add("x-amz-content-sha256", hashedRequestPayload); webRequest.ContentLength = bytes.Length; using (Stream newStream = webRequest.GetRequestStream()) { newStream.Write(bytes, 0, bytes.Length); newStream.Flush(); } 

也; 有关执行AWS-4签名的更合适和可重用的方法,请参阅Amazons自己的: http : //aws.amazon.com/code