4

I'm trying to implement HMAC security for an API. Everything works fine until I try to post a file.

The HMAC solution can be found here - https://github.com/gavinharriss/WebAPI.HMAC - it's a fork from the original to allow GET requests as well as POST requests.

The code to attach a file:

var requestContent = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(file);
requestContent.Add(fileContent, "file", filename);

if I immediately call HttpContent.ReadAsByteArrayAsync() there is no issue, the byte array is available.

However, the HMAC HttpClient (HMACHttpClient) implements a DelegatingHandler (HMACDelegatingHandler) in order to attach the HMAC header to requests.

In the HMACDelegatingHandler the request is passed along as a HttpRequestMessage from which the HttpRequestMessage.Content property is used in a helper to build the HMAC signature.

When building the signature, the following code is called from a helper class:

private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
    using (var md5 = MD5.Create())
    {
        byte[] hash = null;
        if (httpContent != null)
        {
            var content = await httpContent.ReadAsByteArrayAsync(); // <-- Fails here
            if (content.Length != 0)
            {
                hash = md5.ComputeHash(content);
            }
        }
        return hash;
    }
}

When stepping through the code the var content = await httpContent.ReadAsByteArrayAsync() line is hit, then nothing, no error. The requests just seems to go poof but everything is still running and the HttpClient request never gets sent.

Any ideas what's going on?

Gavin
  • 5,629
  • 7
  • 44
  • 86

1 Answers1

4

Having tested this with various sizes of file, I found the issue arose when files got around the 50,000 byte mark.

This post provided a solution: HttpContent.ReadAsStringAsync causes request to hang (or other strange behaviours).

If you replace erroring line in HMACHelper (line 66):

var content = await httpContent.ReadAsByteArrayAsync();

with this:

var ms = new MemoryStream();
await httpContent.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);

var content = ms.ToArray();

It should stop hanging.

Community
  • 1
  • 1
SamStrong
  • 66
  • 2