使用C#为AWS REST查询创建HMAC签名

4
我正在尝试对AWS的SNS服务进行REST API调用,但是一直收到"IncompleteSignature"错误。我参考了http://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/#csharp关于如何创建签名的内容和http://docs.aws.amazon.com/AmazonSimpleDB/latest/DeveloperGuide/HMACAuth.html中的信息来确定签名内容。
以下是我编写的测试代码:
    static void Main(string[] args)
    {
        string region = "us-west-2";
        string msg = "This is a test!";
        string secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
        string key = "XXXXXXXXXXXXXXX";
        string arn = "arn:aws:sns:us-west-2:xxxxxxxxxx:snstest1";

        string query = "Action=Publish&Message=" + HttpUtility.UrlEncode(msg) + "&MessageStructure=json&TargetArn=" + HttpUtility.UrlEncode(arn) + "&SignatureMethod=HmacSHA256&AWSAccessKeyId=" + key + "&SignatureVersion=2&Timestamp=" + HttpUtility.UrlEncode(DateTime.UtcNow.ToString("o"));
        string tosign = "GET\nsns." + region + ".amazonaws.com\n/\n" + query;

        System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();

        byte[] keyByte = encoding.GetBytes(secret);
        byte[] messageBytes = encoding.GetBytes(tosign);

        var hmacsha256 = new HMACSHA256(keyByte);

        byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
        query += "&signature=" + HttpUtility.UrlEncode(Convert.ToBase64String(hashmessage));

        Console.WriteLine("REST Call: https://sns." + region + ".amazonaws.com/?" + query);
    }

你有什么想法可能出了问题吗?

编辑:我尝试使用http://wiki.alphasoftware.com/~alphafiv/DotNet+Example%3A+Digital+Hash中的代码更改签名部分,它使用CharArray而不是byte[],不确定哪个是正确的,它会产生不同的签名,但仍无法与AWS一起使用。

编辑2:经过长时间的尝试,我终于发现AWS希望使用Signature = 而不是signature = ,但现在我收到一个SignatureDoesNotMatch错误,所以我需要接下来解决这个问题。此外,我不知道为什么这种类型的问题会被downvote。一旦我弄清楚语法,AWS API调用将在任何应用程序中都很容易完成。如果您使用AWS .NET SDK,则会向二进制文件添加6兆字节。这不是一个值得追求的事情吗?

解决方案:

此代码有效,并且将发送SNS通知而无需使用AWS SDK:

    static void Main(string[] args)
    {
        string region = "us-west-2";
        string msg = "Test test: sfdfds\nfsd: sdsda\n";
        string secret = "XXXXXXXXXXXXXXXXXXX";
        string key = "ZZZZZZZZZZZ";
        string arn = "arn:aws:sns:us-west-2:YYYYYYYYYYY:snstest1";

        string query = "AWSAccessKeyId=" + Uri.EscapeDataString(key) + "&Action=Publish&Message=" + Uri.EscapeDataString(msg) + "&SignatureMethod=HmacSHA256&SignatureVersion=2&TargetArn=" + Uri.EscapeDataString(arn) + "&Timestamp=" + Uri.EscapeDataString(System.DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"));
        string tosign = "GET\nsns." + region + ".amazonaws.com\n/\n" + query;

        Console.WriteLine(tosign + "\n");

        UTF8Encoding encoding = new UTF8Encoding();
        HMACSHA256 hmac = new HMACSHA256(encoding.GetBytes(secret));
        string signature = Convert.ToBase64String(hmac.ComputeHash(encoding.GetBytes(tosign)));

        query += "&Signature=" + Uri.EscapeDataString(signature);

        Console.WriteLine("REST Call: https://sns." + region + ".amazonaws.com/?" + query);
    }

1
为什么不能直接使用SDK,它可以为您处理这些部分? - Antoine
我希望尽可能地保持代码的小巧精悍。这应该可以手动完成,AWS解释了如何做到这一点,只是我无法弄清签名部分。 - Dendory
这似乎是为了“保持简洁”而付出了很大的代价;使用AWS的.NET SDK查询它不到10行代码:http://docs.aws.amazon.com/AWSSdkDocsNET/latest/DeveloperGuide/sns-apis-intro.html - George Stocker
是的,如果这个方法不起作用,那就只能使用我的备选方案了。看起来很遗憾,本应该可以实现的,但现在却不能。 - Dendory
我仍然希望让这个工作起来。使用REST调用会生成一个5kb的二进制文件。而使用AWS SDK则需要一个6mb的DLL文件。 - Dendory
显示剩余4条评论
1个回答

2

使用自己的解决方案而不是使用SDK没有问题。事实上,我更喜欢这样做,因为除了更轻量级的代码外,由于您正在使用本地接口,您更有可能理解意外行为的问题。

这就是您所错过的:

添加查询字符串参数...按字典顺序排序

http://docs.aws.amazon.com/general/latest/gr/signature-version-2.html

例如,不能将TargetArn放在SignatureMethod之前。它们全部都需要被排序。对于任何给定的消息,只有一个可能的正确签名,因此排序顺序至关重要。


谢谢!你不知道我花了多少小时才能让它正常工作。这意味着在部署一个 AWS 调用时,二进制文件大小从 10k 变成了 10k + 6mb 的 DLL 文件和 16mb 的 XML 文件。 - Dendory

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接