在 C# 中为 AWS REST 查询创建 HMAC 签名

Creating a HMAC signature for AWS REST query in C#

所以我正在尝试对 AWS 的 SNS 服务进行 REST API 调用,但我一直收到 IncompleteSignature 错误。我根据自己 http://www.jokecamp.com/blog/examples-of-creating-base64-hashes-using-hmac-sha256-in-different-languages/#csharp on how to create the signature and 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。

EDIT2: 经过长时间的尝试,我终于发现 AWS 需要 Signature= 而不是 signature=,但现在我得到了 SignatureDoesNotMatch 错误,所以接下来我需要解决这个问题。我也不知道为什么这种问题会被否决。一旦我弄清楚语法,在任何应用程序中调用 AWS API 都是微不足道的。如果您使用 AWS .NET SDK,您将向二进制文件添加 6 兆字节。这怎么能不值得呢?

解决方案:

此代码有效,无需 AWS SDK 即可发送 SNS 通知:

    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);
    }

推出自己的解决方案而不是使用 SDK 并没有错。事实上,我更喜欢它,因为除了更轻量级的代码之外,你更有可能理解意外行为的问题,因为你正在使用本机界面。

这是您所缺少的:

Add the query string parameters ... sorted using lexicographic byte ordering

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

例如,TargetArn 不应在 SignatureMethod 之前。他们 所有 都需要排序。对于任何给定的消息,只有一个可能的正确签名,因此排序顺序很重要。