private async Task SignRequest(HttpRequestMessage request) { if (StringExtensions.IsIgnoredPath(IgnoredPaths, request.RequestUri.AbsolutePath, request.Method.Method)) { return; } var content = request.Content != null ? (await request.Content.ReadAsByteArrayAsync()) : new byte[0]; var validationKey = Credential.Key as X509SecurityKey; var pathAndQuery = Uri.UnescapeDataString(request.RequestUri.AbsolutePath) + request.RequestUri.Query; var headersToSign = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var name in HeaderNames) { if (HttpRequestTarget.HeaderName.Equals(name, StringComparison.OrdinalIgnoreCase)) { headersToSign.Add(HttpRequestTarget.HeaderName, new HttpRequestTarget(request.Method.Method, pathAndQuery).ToString()); continue; } else if (HttpDigest.HTTPHeaderName.Equals(name, StringComparison.OrdinalIgnoreCase)) { headersToSign.Add(HttpDigest.HTTPHeaderName, new HttpDigest(Credential.Algorithm, content).ToString()); request.Headers.Add(HttpDigest.HTTPHeaderName, headersToSign[name]); continue; } else if (HeaderFieldNames.Created.Equals(name, StringComparison.OrdinalIgnoreCase)) { headersToSign.Add(HeaderFieldNames.Created, request.Headers.TryGetValues(RequestCreatedHeaderName, out var createdDate) ? createdDate.First() : DateTimeOffset.UtcNow.ToString("r")); if (!request.Headers.Contains(RequestCreatedHeaderName)) { request.Headers.Add(RequestCreatedHeaderName, headersToSign[HeaderFieldNames.Created]); } continue; } else { if (request.Headers.Contains(name)) { var value = request.Headers.GetValues(name).FirstOrDefault(); if (headersToSign.ContainsKey(name)) { headersToSign[name] = value; } else { headersToSign.Add(name, value); } Debug.WriteLine($"HttpSignature: Include '{name}: {value}'"); } else { throw new Exception($"HttpSignature: Cannot include header'{name}' it is missing from the request payload"); } } } var signature = new HttpSignature(Credential, headersToSign, DateTime.UtcNow, null); request.Headers.Add(HttpSignature.HTTPHeaderName, signature.ToString()); Debug.WriteLine($"HttpSignature: {HttpSignature.HTTPHeaderName} Header: {signature}"); request.Headers.Add(RequestSignatureCertificateHeaderName, Convert.ToBase64String(validationKey.Certificate.Export(X509ContentType.Cert))); }
/// <summary> /// constructs the token for sending a request with http signature. /// </summary> /// <param name="signingCredentials"></param> /// <param name="requestBody"></param> /// <param name="includedHeaders"></param> /// <param name="createdDate"></param> /// <param name="expirationDate"></param> public HttpSignatureSecurityToken(SigningCredentials signingCredentials, byte[] requestBody, IDictionary <string, string> includedHeaders, DateTime?createdDate = null, DateTime?expirationDate = null) { Digest = new HttpDigest(signingCredentials.Algorithm, requestBody); includedHeaders.Add(HttpDigest.HTTPHeaderName, Digest.ToString()); Signature = new HttpSignature(signingCredentials, includedHeaders, createdDate, expirationDate); RequestId = includedHeaders["X-Request-Id"]; }
/// <summary> /// Constructs the token for validating an incoming request. /// </summary> public HttpSignatureSecurityToken(string rawDigest, string rawSignature) { RawDigest = rawDigest; RawSignature = rawSignature; Digest = HttpDigest.Parse(rawDigest); Signature = HttpSignature.Parse(rawSignature); }
private async Task ValidateResponse(HttpRequestMessage request, HttpResponseMessage response) { if (IgnoreResponseValidation) { return; } if (StringExtensions.IsIgnoredPath(IgnoredPaths, request.RequestUri.AbsolutePath, request.Method.Method)) { return; } if (!response.Headers.TryGetValues(HttpSignature.HTTPHeaderName, out var signatureValues) || signatureValues.Count() == 0) { return; } if (!response.Headers.TryGetValues(ResponseSignatureCertificateHeaderName, out var certValues) || certValues.Count() == 0) { var error = $"Missing certificate in HTTP header '{ResponseSignatureCertificateHeaderName}'. Cannot validate signature."; throw new Exception(error); } var rawSignature = signatureValues.First(); var rawCertificate = certValues.First(); Debug.WriteLine($"{nameof(HttpSignatureDelegatingHandler)}: Raw Signature: {rawSignature}"); Debug.WriteLine($"{nameof(HttpSignatureDelegatingHandler)}: Raw Certificate: {rawCertificate}"); X509Certificate2 certificate; try { certificate = new X509Certificate2(Convert.FromBase64String(rawCertificate)); } catch (Exception inner) { var error = $"Signature Certificate not in a valid format. Expected a base64 encoded x509."; throw new Exception(error, inner); } var validationKey = new X509SecurityKey(certificate); Debug.WriteLine($"{nameof(HttpSignatureDelegatingHandler)}: Validation Key: {validationKey.KeyId}"); var httpSignature = HttpSignature.Parse(rawSignature); if (response.Headers.TryGetValues(HttpDigest.HTTPHeaderName, out var digestValues) && digestValues.Count() > 0) { var rawDigest = digestValues.First(); Debug.WriteLine($"{nameof(HttpSignatureDelegatingHandler)}: Raw Digest: {rawDigest}"); var httpDigest = HttpDigest.Parse(rawDigest); Debug.WriteLine($"{nameof(HttpSignatureDelegatingHandler)}: Validated Token Digest: {httpDigest}"); var responseBody = await response.Content.ReadAsByteArrayAsync(); // Validate the request. var disgestIsValid = httpDigest.Validate(responseBody); if (!disgestIsValid) { var error = $"Response digest validation failed."; throw new Exception(error); } } response.Headers.TryGetValues(ResponseCreatedHeaderName, out var createdFieldValue); var signatureIsValid = httpSignature.Validate(validationKey, request.RequestUri, request.Method.Method, createdFieldValue.First(), response.Headers); if (!signatureIsValid) { var error = $"Response signature validation failed."; throw new Exception(error); } }