使用c#的Amazon ec2 API版本2签名编码

Tim*_*vis 6 c# hash amazon amazon-ec2

我在为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;
}
Run Code Online (Sandbox Code Playgroud)

现在,对于版本2,有一些更改,这里是来自API开发人员指南的doco ...

  1. 创建以后在此过程中需要的规范化查询字符串:

一个.使用自然字节顺序按参数名称对UTF-8查询字符串组件进行排序.参数可以来自GET URI或POST主体(当Content-Type为application/x-www-form-urlencoded时).

湾 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)分隔名称 - 值对.

  1. 根据以下伪语法创建要签名的字符串("\n"表示ASCII换行符).StringToSign = HTTPVerb +"\n"+ ValueOfHostHeaderInLowercase +"\n"+ HTTPRequestURI +"\n"+
    CanonicalizedQueryString HTTPRequestURI组件是URI的HTTP绝对路径组件,但不包括查询字符串.如果HTTPRequestURI为空,请使用正斜杠(/).
  2. 使用刚刚创建的字符串,密钥访问密钥作为密钥,使用SHA256或SHA1作为哈希算法,计算符合RFC 2104的HMAC.有关更多信息,请访问http://www.rfc.net/rfc2104.html.
  3. 将结果值转换为base64.
  4. 使用结果值作为Signature请求参数的值.

所以我拥有的是......

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;
}
Run Code Online (Sandbox Code Playgroud)

为了完整性,这里是IComparer实现....

  internal class CanonicalizedDictCompare : IComparer<string>
  {
    #region IComparer<string> Members

    public int Compare(string x, string y)
    {
      return string.CompareOrdinal(x, y);
    }

    #endregion
  }
Run Code Online (Sandbox Code Playgroud)

据我所知,我已经完成了我需要为此哈希做的所有事情,但我不断从服务器收到错误,告诉我我的签名不正确.救命...

Tim*_*vis 7

好吧,我想通了...... HttpUtility类中的UrlEncoding不符合亚马逊编码方案.... grrr(特别是.NET实用程序中的%之后的十六进制值是小写,而不是大写)

湾 URL根据以下规则对参数名称和值进行编码:

  • 不要对RFC 3986 定义的任何未保留字符进行URL编码.这些未保留的字符是AZ,az,0-9,连字符( - ),下划线(_),句点(.)和波浪号(〜).
  • 百分比使用%XY编码所有其他字符,其中X和Y是十六进制字符0-9和大写AF.

  • 百分比编码扩展的UTF-8字符,格式为%XY%ZA ....

  • 百分比将空格字符编码为%20(而不是+,如常见的编码方案那样).

因此,在编写了一个编码到这个方案的快速方法之后,它运行正常.