/// <summary> /// Checks if a request is valid based on what the client passed /// </summary> /// <param name="content">The content that the client passed</param> /// <param name="resource">The resource that the client requested (URI ENCODED)</param> /// <param name="resourceRequestMethod">The method that the client used to request the resource (ALL UPPER)</param> /// <param name="appId">The appId the client is claiming to be</param> /// <param name="incomingBase64Signature">The base64 signature the client passed</param> /// <param name="nonce">The nonce the client provided to identify the request</param> /// <param name="requestTimeStamp">The timestamp the client provided to identify the request</param> /// <returns>(Is the request valid, description of the result)</returns> protected virtual Tuple <bool, HmacIsValidRequestResult> IsValidRequest(Stream content, string resource, string resourceRequestMethod, string appId, string incomingBase64Signature, string nonce, string requestTimeStamp) { if (string.IsNullOrWhiteSpace(resource)) { LoggingService.LogMessage("HMAC Auth Resource Not Valid", "No valid request requested resource", LogLevel.Debug); return(new Tuple <bool, HmacIsValidRequestResult>(false, HmacIsValidRequestResult.NoValidResouce)); } string requestContentBase64String = ""; ApiKeyService.TryGetValue(appId, out string sharedKey); if (string.IsNullOrWhiteSpace(sharedKey)) { LoggingService.LogMessage("Api Auth Request Not Valid", "Unable to find appId", LogLevel.Debug); return(new Tuple <bool, HmacIsValidRequestResult>(false, HmacIsValidRequestResult.UnableToFindAppId)); } if (IsReplayRequest(nonce, requestTimeStamp)) { LoggingService.LogMessage("Api Auth Request Not Valid", "Looked like a replay request.\r\nNonce - " + nonce + "\r\nRequest Timestamp - " + requestTimeStamp + "\r\nOur Timestamp" + (DateTime.UtcNow - Constants.DateTime.UnixEpoch).TotalSeconds, LogLevel.Debug); return(new Tuple <bool, HmacIsValidRequestResult>(false, HmacIsValidRequestResult.ReplayRequest)); } // we need a content stream (we need to make sure we pass this back) Tuple <Stream, byte[], string> hashAndContent = ComputeHash(content); if (hashAndContent.Item2 != null) { requestContentBase64String = Convert.ToBase64String(hashAndContent.Item2); } string encodedResource = System.Web.HttpUtility.UrlEncode(resource.ToLower()); string data = $"{appId}{resourceRequestMethod}{encodedResource}{requestTimeStamp}{nonce}{requestContentBase64String}"; byte[] secretKeyBytes = Convert.FromBase64String(sharedKey); byte[] signature = data.GetBytes(); using (HMACSHA256 hmac = new HMACSHA256(secretKeyBytes)) { byte[] signatureBytes = hmac.ComputeHash(signature); string signatureString = Convert.ToBase64String(signatureBytes); bool matched = incomingBase64Signature.Equals(signatureString, StringComparison.Ordinal); if (!matched) { LoggingService.LogMessage("Api Auth Request Not Valid", "Signatures didn't match.\r\nRequest Signature - " + incomingBase64Signature + ".\r\nInternal Signature - " + signatureString + ".\r\nInternal signature data - " + data + "\r\nRaw Content - " + hashAndContent.Item2, LogLevel.Debug); } return(new Tuple <bool, HmacIsValidRequestResult>(matched, matched ? HmacIsValidRequestResult.NoError : HmacIsValidRequestResult.SignaturesMismatch)); } }