public async Task MessageHandlerSignsRequests() { var algorithm = new HttpSignatureAlgorithm(TestKeyConstants.TestKey, new TestClock()); var handler = new SignatureAuthorizationHandler(TestKeyConstants.ValidKeyId, algorithm, new RequireSignatureHandler()); using (var client = new HttpClient(handler)) { var response = await client.GetAsync("http://localhost:5000/api/test/1?value=2011-12-20T12:13:21Z"); response.StatusCode.Should().Be(HttpStatusCode.OK); } }
/// <summary> /// Determines whether the signature is valid for the specified message. /// </summary> /// <param name="signature">The signature to validate.</param> /// <param name="method">The HTTP method of the message.</param> /// <param name="uri">The requested URI of the message.</param> /// <param name="body">The message body.</param> /// <param name="cancellationToken">A token to monitor for cancellation requests.</param> /// <returns>A value indicating the result of the validation.</returns> public virtual async Task <SignatureValidationResult> ValidateAsync( HttpSignature signature, string method, string uri, Stream body, CancellationToken cancellationToken) { var timeDiff = Clock.UtcNow - signature.Timestamp; if (timeDiff.Duration() > Options.ClockSkewMargin) { Logger?.LogInformation("The time difference {TimeDiff} between the signature timestamp {Timestamp} and the current time exceeds {Margin}.", timeDiff, signature.Timestamp, Options.ClockSkewMargin); return(SignatureValidationResult.Expired); } var entry = new NonceCacheEntry(signature.Nonce); if (Cache.TryGetValue(entry, out _)) { Logger?.LogInformation("The nonce '{Nonce}' is not unique and has been used before in the past {Expiration}.", signature.Nonce, Options.NonceExpiration); return(SignatureValidationResult.Duplicate); } var key = await KeyLookup.GetKeyOrDefaultAsync(signature.KeyId).ConfigureAwait(false); if (key == null) { throw KeyNotFoundException.WithId(signature.KeyId); } var algorithm = new HttpSignatureAlgorithm(key, Clock, Logger); var newHash = await algorithm.CalculateHashAsync(method, uri, body, signature.Nonce, signature.Timestamp, cancellationToken).ConfigureAwait(false); if (!newHash.HashEquals(signature.Hash)) { Logger?.LogInformation("The signature for {Method} {Uri} with nonce '{Nonce}' and timestamp {Timestamp} does not match.", method, uri, signature.Nonce, signature.Timestamp); return(SignatureValidationResult.Invalid); } Cache.Set(entry, true, Options.NonceExpiration); return(SignatureValidationResult.OK); }
public async Task SignedRequestContainsValidAuthorizationHeader() { var algorithm = new HttpSignatureAlgorithm(TestKeyConstants.TestKey, new TestClock()); var request = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri("http://localhost:5000/api/test/1?value=2011-12-20T12:13:21Z") }; await request.SignAsync(algorithm, TestKeyConstants.ValidKeyId); request.Headers.Authorization.Should().NotBeNull(); request.Headers.Authorization.Parameter.Should().NotBeNull(); var param = HttpSignature.Parse(request.Headers.Authorization.Parameter); param.KeyId.Should().Be(TestKeyConstants.ValidKeyId); param.Nonce.Should().NotBeNull(); param.Timestamp.Should().Be(TestClock.TestValue); param.Hash.Should().NotBeEmpty(); }