是什么导致带有 HMAC 身份验证的 WebApi 只能工作一次?

What could cause WebApi with HMAC Authentication to only work once?

我有一个 C# MVC 项目,我最近向其中添加了 WebApi。我已经使用 HMAC 身份验证保护 API,并且正在使用 CustomDelegatingHandler 将授权添加到请求的 header。

我已经在另一个项目上使用相同的代码成功地完成了这项工作,而且它有效。但是在这个新项目中,身份验证只工作一次,然后由于 "Unauthorized",对 API 的所有其他调用都会失败。这只发生在我们的 development/test 服务器上,而不是在我的本地机器上 运行 2017 年 Visual Studio 的项目。

什么可能导致这种奇怪的行为?

这是我正在使用的 CustomDelegatingHandler:

public class CustomDelegatingHandler : DelegatingHandler
{
    private string APPId = "";
    private string APIKey = "";

    public CustomDelegatingHandler(string appid, string apikey)
    {
        APPId = appid;
        APIKey = apikey;
    }

    protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {

        HttpResponseMessage response = null;
        string requestContentBase64String = string.Empty;

        string requestUri = System.Web.HttpUtility.UrlEncode(request.RequestUri.AbsoluteUri.ToLower());

        string requestHttpMethod = request.Method.Method;

        //Calculate UNIX time
        DateTime epochStart = new DateTime(1970, 01, 01, 0, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan timeSpan = DateTime.UtcNow - epochStart;
        string requestTimeStamp = Convert.ToUInt64(timeSpan.TotalSeconds).ToString();

        //create random nonce for each request
        string nonce = Guid.NewGuid().ToString("N");

        //Checking if the request contains body, usually will be null wiht HTTP GET and DELETE
        if (request.Content != null)
        {
            byte[] content = await request.Content.ReadAsByteArrayAsync();
            MD5 md5 = MD5.Create();
            //Hashing the request body, any change in request body will result in different hash, we'll incure message integrity
            byte[] requestContentHash = md5.ComputeHash(content);
            requestContentBase64String = Convert.ToBase64String(requestContentHash);
        }

        //Creating the raw signature string
        string signatureRawData = String.Format("{0}{1}{2}{3}{4}{5}", APPId, requestHttpMethod, requestUri, requestTimeStamp, nonce, requestContentBase64String);

        var secretKeyByteArray = Convert.FromBase64String(APIKey);

        byte[] signature = Encoding.UTF8.GetBytes(signatureRawData);

        using (HMACSHA256 hmac = new HMACSHA256(secretKeyByteArray))
        {
            byte[] signatureBytes = hmac.ComputeHash(signature);
            string requestSignatureBase64String = Convert.ToBase64String(signatureBytes);
            //Setting the values in the Authorization header using custom scheme (amx)
            request.Headers.Authorization = new AuthenticationHeaderValue("amx", string.Format("{0}:{1}:{2}:{3}", APPId, requestSignatureBase64String, nonce, requestTimeStamp));
        }

        response = await base.SendAsync(request, cancellationToken);

        return response;
    }
}

我们有一个类似的问题,结果证明与客户端与服务器的时间戳有关。

通过检查请求是否在允许的时间内解决了问题(server-side):

(a > b ? a-b : b-a) > maxtime 对比 a - b > maxtime

如果b > a,是一个unsigned long (ulong),该值变得很大,从而触发无效请求条件。

1522896501 - 1522896502 > 300(18446744073709551615 是计算出来的)