我正在尝试根据this 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<string, string>
{
{"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:
一切正常!我已根据the description实现了CreateSpeech方法,并将使用的完整示例上传到GitHub https://github.com/MalyutinS/DotNetIvonaAPI
答案 0 :(得分:0)
解决!实际上文档示例中存在问题。所以代码工作正常。
答案 1 :(得分:0)
现在有点老了,但使用此代码可能与其他人有关;
只要你只坚持使用ASCII字符,代码就可以正常工作。对于英语以外的语言,必须先将字符串转换为UTF-8字节数组。该数组可能比字符串中的字符数长,因此通过WebRequest发送的字节数组的长度必须反映数组本身的长度,而不是字符数。
真的很经典,但无论如何都要指出..
此错误导致签名不正确,因此出现身份验证错误。原因可能不是每个人都明白的。
现有代码;
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签名的更合适和可重用的方法,请参阅亚马逊拥有:http://aws.amazon.com/code