使用 C# 进行 Amazon ec2 API 版本 2 签名编码
我在对 ec2 API 版本 2 签名的哈希值进行编码时遇到问题。
请注意,我的版本 1 签名哈希工作正常,但这已被折旧,我需要移至版本 2。所以首先这是有效的代码...
参数只是一个字典,我要做的只是对参数进行排序按键并附加每个不带分隔符的值对,然后根据我的键对该字符串进行哈希处理。 (再次注意,这工作正常)
private string GetVersion1Sig()
{
string sig = string.Join(string.Empty, parameters.OrderBy(vp => vp.Key).Select(p => string.Format("{0}{1}", p.Key, p.Value)).ToArray());
UTF8Encoding encoding = new UTF8Encoding();
HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey));
byte[] hash = signature.ComputeHash(encoding.GetBytes(sig));
string result = Convert.ToBase64String(hash);
return result;
}
现在,版本 2 有一些更改,这里是 API 开发人员指南中的 doco...
- 创建您稍后在此过程中需要的规范化查询字符串
: 按参数名称以自然字节顺序对 UTF-8 查询字符串组件进行排序。 参数可以来自 GET URI 或来自 POST 正文(当 Content-Type 是 application/x-www-form-urlencoded)。
b. URL 根据以下规则对参数名称和值进行编码:
• 不要对RFC 3986 定义的任何非保留字符进行URL 编码。 这些非保留字符包括 AZ、az、0-9、连字符 (-)、下划线 (_)、句点 (.)、 和波形符 ( ~ )。
• 使用 %XY 对所有其他字符进行百分比编码,其中 X 和 Y 是十六进制字符 0-9, 大写 AF。
• 对扩展 UTF-8 字符进行百分比编码,格式为 %XY%ZA....
• 将空格字符百分比编码为 %20(而不是 +,如常见的编码方案) 做)。
注意
目前,所有 AWS 服务参数名称都使用非保留字符,因此您不必 需要对它们进行编码。 但是,您可能希望包含处理参数的代码 使用保留字符的名称,以供将来使用。
C。 使用等号 ( = ) 将编码参数名称与其编码值分开 (ASCII 字符 61),即使参数值为空。
d. 使用与号 ( & )(ASCII 代码 38)分隔名称-值对。
- 根据以下伪语法创建要签名的字符串(“\n”代表一个 ASCII 换行符)。 StringToSign = HTTPVerb + "\n" + ValueOfHostHeaderInLowercase + "\n" + HTTPRequestURI + "\n" +
规范化查询字符串 HTTPRequestURI 组件是 URI 的 HTTP 绝对路径组件,但不是 包括查询字符串。 如果 HTTPRequestURI 为空,请使用正斜杠 (/)。 - 使用您刚刚创建的字符串(您的秘密访问密钥)计算符合 RFC 2104 的 HMAC 作为密钥,SHA256 或 SHA1 作为哈希算法。 有关详细信息,请访问 http://www.rfc.net/rfc2104.html。
- 将结果值转换为 base64。
- 使用结果值作为签名请求参数的值。
所以我所拥有的是......
private string GetSignature()
{
StringBuilder sb = new StringBuilder();
sb.Append("GET\n");
sb.Append("ec2.amazonaws.com\n");
sb.Append("/\n");
sb.Append(string.Join("&", parameters.OrderBy(vp => vp.Key, new CanonicalizedDictCompare()).Select(p => string.Format("{0}={1}", HttpUtility.UrlEncode(p.Key), HttpUtility.UrlEncode(p.Value))).ToArray()));
UTF8Encoding encoding = new UTF8Encoding();
HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey));
byte[] hash = signature.ComputeHash(encoding.GetBytes(sb.ToString()));
string result = Convert.ToBase64String(hash);
return result;
}
为了完整起见,这里是 IComparer 实现......
internal class CanonicalizedDictCompare : IComparer<string>
{
#region IComparer<string> Members
public int Compare(string x, string y)
{
return string.CompareOrdinal(x, y);
}
#endregion
}
据我所知,我已经完成了该哈希所需的所有操作,但我不断从服务器收到错误,告诉我我的签名不正确。
I am having a problem with encoding the hash for the Version 2 Signature of the ec2 API.
Note my Version 1 Signature hashing works fine, but this is depreciated and I will need to move to version 2. So firstly here is the code that works...
parameters is just a dictionary, what I have to do is simply sort the parameters by key and append each value-pair with no delimeters, then hash that string against my key. (again, note this works fine)
private string GetVersion1Sig()
{
string sig = string.Join(string.Empty, parameters.OrderBy(vp => vp.Key).Select(p => string.Format("{0}{1}", p.Key, p.Value)).ToArray());
UTF8Encoding encoding = new UTF8Encoding();
HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey));
byte[] hash = signature.ComputeHash(encoding.GetBytes(sig));
string result = Convert.ToBase64String(hash);
return result;
}
Now, with Version 2 there are some changes, here is the doco from the API developers guide...
- Create the canonicalized query string that you need later in this procedure:
a. Sort the UTF-8 query string components by parameter name with natural byte ordering.
The parameters can come from the GET URI or from the POST body (when Content-Type
is application/x-www-form-urlencoded).
b. URL encode the parameter name and values according to the following rules:
• Do not URL encode any of the unreserved characters that RFC 3986 defines.
These unreserved characters are A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ),
and tilde ( ~ ).
• Percent encode all other characters with %XY, where X and Y are hex characters 0-9 and
uppercase A-F.
• Percent encode extended UTF-8 characters in the form %XY%ZA....
• Percent encode the space character as %20 (and not +, as common encoding schemes
do).
Note
Currently all AWS service parameter names use unreserved characters, so you don't
need to encode them. However, you might want to include code to handle parameter
names that use reserved characters, for possible future use.
c. Separate the encoded parameter names from their encoded values with the equals sign ( = )
(ASCII character 61), even if the parameter value is empty.
d. Separate the name-value pairs with an ampersand ( & ) (ASCII code 38).
- Create the string to sign according to the following pseudo-grammar (the "\n" represents an
ASCII newline).
StringToSign = HTTPVerb + "\n" +
ValueOfHostHeaderInLowercase + "\n" +
HTTPRequestURI + "\n" +
CanonicalizedQueryString
The HTTPRequestURI component is the HTTP absolute path component of the URI up to, but not
including, the query string. If the HTTPRequestURI is empty, use a forward slash ( / ). - Calculate an RFC 2104-compliant HMAC with the string you just created, your Secret Access Key
as the key, and SHA256 or SHA1 as the hash algorithm.
For more information, go to http://www.rfc.net/rfc2104.html. - Convert the resulting value to base64.
- Use the resulting value as the value of the Signature request parameter.
So what I have is....
private string GetSignature()
{
StringBuilder sb = new StringBuilder();
sb.Append("GET\n");
sb.Append("ec2.amazonaws.com\n");
sb.Append("/\n");
sb.Append(string.Join("&", parameters.OrderBy(vp => vp.Key, new CanonicalizedDictCompare()).Select(p => string.Format("{0}={1}", HttpUtility.UrlEncode(p.Key), HttpUtility.UrlEncode(p.Value))).ToArray()));
UTF8Encoding encoding = new UTF8Encoding();
HMACSHA256 signature = new HMACSHA256(encoding.GetBytes(_secretAccessKey));
byte[] hash = signature.ComputeHash(encoding.GetBytes(sb.ToString()));
string result = Convert.ToBase64String(hash);
return result;
}
for completeness here is the IComparer implementation....
internal class CanonicalizedDictCompare : IComparer<string>
{
#region IComparer<string> Members
public int Compare(string x, string y)
{
return string.CompareOrdinal(x, y);
}
#endregion
}
As far as I can tell I have done everything I need to do for this hash, but I keep getting an error from the server telling me that my signature is incorrect.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好吧,我明白了....HttpUtility 类中的 UrlEncoding 不符合 Amazon 编码方案...grrr(特别是 .NET 实用程序中 % 后面的十六进制值是小写,而不是大写)
b。 根据以下规则对参数名称和值进行 URL 编码:
RFC 3986 的非保留字符
定义。 这些毫无保留的字符
是 AZ、az、0-9、连字符 (-)、
下划线 (_)、句点 (.) 和
波形符 (~)。
对所有其他字符进行百分比编码
与 %XY,其中 X 和 Y 是十六进制
字符 0-9 和大写 AF。
对
百分比编码扩展UTF-8
%XY%ZA.... 形式的字符
字符为 %20(而不是 +,如
常见的编码方案都可以)。
因此,在编写了一个对该方案进行编码的快速方法之后,它工作得很好。
Ok, I figured it out....The UrlEncoding in the HttpUtility class does not conform to the Amazon encoding scheme....grrr (specifically the hex value after the % in the .NET utility is lowercase, not uppercase)
b. URL encode the parameter name and values according to the following rules:
unreserved characters that RFC 3986
defines. These unreserved characters
are A-Z, a-z, 0-9, hyphen ( - ),
underscore ( _ ), period ( . ), and
tilde ( ~ ).
Percent encode all other characters
with %XY, where X and Y are hex
characters 0-9 and uppercase A-F.
Percent encode extended UTF-8
characters in the form %XY%ZA....
character as %20 (and not +, as
common encoding schemes do).
So after writing a quick method that encodes to this scheme, it works fine.