FantasticFiasco / aws-signature-version-4

The buttoned-up and boring, but deeply analyzed, implementation of SigV4 in .NET
Apache License 2.0
76 stars 18 forks source link

Multi-part user agent is joined incorrectly leading to AWS signature failures #1155

Closed mungojam closed 1 month ago

mungojam commented 2 months ago

Describe the bug

When creating a multi-part user-agent header, .net will space separate the different entries, as per the user agent spec. This is also what AWS expects in its canonical header that should be signed.

However, this library always comma delimits multi-part headers by applying this default:

https://github.com/FantasticFiasco/aws-signature-version-4/blob/ba6d9e2a790ab5a6499d394634e6eca032a68c8d/src/Private/CanonicalRequest.cs#L24-L31

To Reproduce

                var client= new HttpClient();

                var toolHeader1 = new ProductHeaderValue(
                    "ToolA",
                    "1.2.3",
                );
                var toolUserAgent1 = new ProductInfoHeaderValue(toolHeader1);
                client.DefaultRequestHeaders.UserAgent.Add(toolUserAgent1);

                var toolHeader2 = new ProductHeaderValue(
                    "ToolB",
                    "1.2.3",
                );
                var toolUserAgent2 = new ProductInfoHeaderValue(toolHeader2);
                client.DefaultRequestHeaders.UserAgent.Add(toolUserAgent2);

                await client.UsualCallViaThisLibrary();

This leads to

The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. The Canonical String for this request should have been 'POST /something/something host:redacted.execute-api.eu-west-1.amazonaws.com user-agent:ToolA/1.2.3 ToolB/1.2.3 x-amz-date:20240912T171219Z x-amz-security-...

Expected behavior

It's expected to get through the AWS signature checks

Desktop (please complete the following information):

** Other comments

It can probably be worked around, by manually adding the full user agent as a single entry.

I don't know for sure if any other multi-part headers are also supposed to be space delimited like this. User Agent may be an exception

mungojam commented 2 months ago

A hacky workaround, that only works because I've not got any other multi-part headers:

        var currentHeaderSeparator = CanonicalRequest.HeaderValueSeparator;
        CanonicalRequest.HeaderValueSeparator = " ";

        var response = await MakeAPICall(client, uri, content, credentials);

        CanonicalRequest.HeaderValueSeparator = currentHeaderSeparator;
mungojam commented 2 months ago

I'm working on a fix now, should have a PR ready soon

FantasticFiasco commented 1 month ago

Hi @mungojam and thanks for reporting the issue! Let me look into the issue and your proposed solution, I'll be back.