C#生成Base16编码SHA256哈希函数返回结果与Postman不一致问题排查
The core problem here is that the JSON string you're hashing doesn't match the exact request body Postman sends to the AWS API. AWS requires you to hash the exact raw payload bytes transmitted over the wire—and Postman automatically serializes JSON into a compact, whitespace-free format, while your code uses the human-readable, indented JSON string you provided.
When you use indented JSON with spaces, newlines, and indentation, its byte representation is completely different from the compact version Postman sends. Since SHA256 generates a unique hash based on every single byte of input, these small differences lead to totally mismatched hash values.
Verify the Fix
If you take your original JSON and strip all whitespace (spaces, newlines, tabs, indentation) to get this compact string:
{"shipTo":{"name":"A3","addressLine1":"SWA Test Account","addressLine2":"SWA Test Account","addressLine3":"SWA Test Account","stateOrRegion":"","postalCode":"DN1 1QZ","city":"Doncaster","countryCode":"GB","email":"test+test@amazon.com","phoneNumber":"444-444-4444"},"shipFrom":{"name":"A1","addressLine1":"4 Neal Street","stateOrRegion":"","postalCode":"WC2H 9QL","city":"London","countryCode":"GB","email":"test+test@amazon.com","phoneNumber":"444-444-4444"},"packages":[{"dimensions":{"length":3.14,"width":3.14,"height":3.14,"unit":"INCH"},"weight":{"unit":"KILOGRAM","value":3.14159},"items":[{"quantity":1,"itemIdentifier":"V-02","description":"Sundries","isHazmat":false,"weight":{"unit":"KILOGRAM","value":1.14159}},{"quantity":1,"itemIdentifier":"V-01","description":"Sundries","isHazmat":false,"weight":{"unit":"KILOGRAM","value":1.14159}}],"insuredValue":{"unit":"GBP","value":29.98},"packageClientReferenceId":"abcd"}],"channelDetails":{"channelType":"EXTERNAL"}}
Running this through your existing HashRequestBody function will produce the expected hash: 71d826b7bab4af808f73033e81154bce32fed66f1270393d8b0fa05839a6fb29.
Solution: Serialize to Compact JSON Before Hashing
Instead of using the pre-formatted JSON string directly, serialize your .NET object to a compact, whitespace-free JSON string first. This ensures you're hashing the exact same payload that gets sent to AWS.
Example with System.Text.Json
using System.Text.Json; using System.Globalization; using System.Security.Cryptography; using System.Text; public static string HashRequestBody<T>(T requestObject) { // Serialize to compact, whitespace-free JSON string compactJson = JsonSerializer.Serialize(requestObject, new JsonSerializerOptions { WriteIndented = false, IgnoreNullValues = false // Match AWS's handling of empty/null values }); // Use non-obsolete SHA256 implementation using var sha256 = SHA256.Create(); byte[] hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(compactJson)); StringBuilder sb = new StringBuilder(); foreach (byte b in hashedBytes) { sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } return sb.ToString(); }
Example with Newtonsoft.Json (Json.NET)
using Newtonsoft.Json; using System.Globalization; using System.Security.Cryptography; using System.Text; public static string HashRequestBody<T>(T requestObject) { string compactJson = JsonConvert.SerializeObject(requestObject, Formatting.None); using var sha256 = SHA256.Create(); byte[] hashedBytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(compactJson)); StringBuilder sb = new StringBuilder(); foreach (byte b in hashedBytes) { sb.Append(b.ToString("x2", CultureInfo.InvariantCulture)); } return sb.ToString(); }
Key Notes
- Hash the exact payload: Any modification to the JSON (whitespace, key order) before hashing will break the signature. Always hash the same data you send.
- Avoid obsolete types:
SHA256Managedis obsolete in modern .NET versions—useSHA256.Create()instead for better compatibility. - Check API-specific requirements: Some AWS services require lexicographical ordering of JSON keys for canonical requests. Review your target API's documentation to confirm if this is needed, and adjust your serialization settings accordingly.
内容的提问来源于stack exchange,提问作者Luke4792

