public async Task Manual_CertificateSentMatchesCertificateReceived_Success( int numberOfRequests, bool reuseClient) // validate behavior with and without connection pooling, which impacts client cert usage { var options = new LoopbackServer.Options { UseSsl = true }; using (X509Certificate2 cert = Configuration.Certificates.GetClientCertificate()) { Func<HttpClient> createClient = () => { var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = delegate { return true; } }; handler.ClientCertificates.Add(cert); return new HttpClient(handler); }; Func<HttpClient, Socket, Uri, Task> makeAndValidateRequest = async (client, server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetStringAsync(url), LoopbackServer.AcceptSocketAsync(server, async (socket, stream, reader, writer) => { SslStream sslStream = Assert.IsType<SslStream>(stream); Assert.Equal(cert, sslStream.RemoteCertificate); await LoopbackServer.ReadWriteAcceptedAsync(socket, reader, writer); return null; }, options)); }; await LoopbackServer.CreateServerAsync(async (server, url) => { if (reuseClient) { using (HttpClient client = createClient()) { for (int i = 0; i < numberOfRequests; i++) { await makeAndValidateRequest(client, server, url); GC.Collect(); GC.WaitForPendingFinalizers(); } } } else { for (int i = 0; i < numberOfRequests; i++) { using (HttpClient client = createClient()) { await makeAndValidateRequest(client, server, url); } GC.Collect(); GC.WaitForPendingFinalizers(); } } }, options); } }
public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol) { using (var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates }) using (var client = new HttpClient(handler)) { if (requestOnlyThisProtocol) { handler.SslProtocols = acceptedProtocol; } var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), client.GetAsync(url)); }, options); } }
public async Task GetAsync_DisallowTls10_AllowTls11_AllowTls12() { using (var handler = new HttpClientHandler() { SslProtocols = SslProtocols.Tls11 | SslProtocols.Tls12, ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates }) using (var client = new HttpClient(handler)) { if (BackendSupportsSslConfiguration) { LoopbackServer.Options options = new LoopbackServer.Options { UseSsl = true }; options.SslProtocols = SslProtocols.Tls; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( Assert.ThrowsAsync<IOException>(() => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)), Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(url))); }, options); foreach (var prot in new[] { SslProtocols.Tls11, SslProtocols.Tls12 }) { options.SslProtocols = prot; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), client.GetAsync(url)); }, options); } } else { await Assert.ThrowsAnyAsync<NotSupportedException>(() => client.GetAsync($"http://{Guid.NewGuid().ToString()}/")); } } }
public async Task GetAsync_AllowedSSLVersionDiffersFromServer_ThrowsException( SslProtocols allowedProtocol, SslProtocols acceptedProtocol, Type exceptedServerException) { using (var handler = new HttpClientHandler() { SslProtocols = allowedProtocol, ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates }) using (var client = new HttpClient(handler)) { var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( Assert.ThrowsAsync(exceptedServerException, () => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)), Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(url))); }, options); } }
public async Task GetAsync_NoSpecifiedProtocol_DefaultsToTls12() { using (var handler = new HttpClientHandler() { ServerCertificateCustomValidationCallback = LoopbackServer.AllowAllCertificates }) using (var client = new HttpClient(handler)) { var options = new LoopbackServer.Options { UseSsl = true }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( client.GetAsync(url), LoopbackServer.AcceptSocketAsync(server, async (s, stream, reader, writer) => { Assert.Equal(SslProtocols.Tls12, Assert.IsType<SslStream>(stream).SslProtocol); await LoopbackServer.ReadWriteAcceptedAsync(s, reader, writer); return null; }, options)); }, options); } }
public async Task GetAsync_IPBasedUri_Success(IPAddress address) { using (var client = new HttpClient()) { var options = new LoopbackServer.Options { Address = address }; await LoopbackServer.CreateServerAsync(async (server, url) => { await TestHelper.WhenAllCompletedOrAnyFailed( LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options), client.GetAsync(url)); }, options); } }
internal static bool IsDigestAuthTokenValid(string clientResponse, string requestMethod, LoopbackServer.Options options) { string clientHash = clientResponse.Substring(clientResponse.IndexOf(nameof(AuthenticationProtocols.Digest), StringComparison.OrdinalIgnoreCase) + nameof(AuthenticationProtocols.Digest).Length).Trim(); string[] values = clientHash.Split(','); string username = null, uri = null, realm = null, nonce = null, response = null, algorithm = null, cnonce = null, opaque = null, qop = null, nc = null; bool userhash = false; for (int i = 0; i < values.Length; i++) { string trimmedValue = values[i].Trim(); if (trimmedValue.Contains(nameof(username))) { // Username is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; username = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } // Username is mandatory. if (string.IsNullOrEmpty(username)) { return(false); } } else if (trimmedValue.Contains(nameof(userhash)) && trimmedValue.Contains("true")) { userhash = true; } else if (trimmedValue.Contains(nameof(uri))) { int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; uri = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } // Request uri is mandatory. if (string.IsNullOrEmpty(uri)) { return(false); } } else if (trimmedValue.Contains(nameof(realm))) { // Realm is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; realm = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } // Realm is mandatory. if (string.IsNullOrEmpty(realm)) { return(false); } } else if (trimmedValue.Contains(nameof(cnonce))) { // CNonce is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; cnonce = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } } else if (trimmedValue.Contains(nameof(nonce))) { // Nonce is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; nonce = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } // Nonce is mandatory. if (string.IsNullOrEmpty(nonce)) { return(false); } } else if (trimmedValue.Contains(nameof(response))) { // response is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; response = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } // Response is mandatory. if (string.IsNullOrEmpty(response)) { return(false); } } else if (trimmedValue.Contains(nameof(algorithm))) { int startIndex = trimmedValue.IndexOf('='); if (startIndex != -1) { startIndex += 1; algorithm = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim(); } } else if (trimmedValue.Contains(nameof(opaque))) { // Opaque is a quoted string. int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; opaque = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } } else if (trimmedValue.Contains(nameof(qop))) { int startIndex = trimmedValue.IndexOf('"'); if (startIndex != -1) { startIndex += 1; qop = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex - 1); } else if ((startIndex = trimmedValue.IndexOf('=')) != -1) { startIndex += 1; qop = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim(); } } else if (trimmedValue.Contains(nameof(nc))) { int startIndex = trimmedValue.IndexOf('='); if (startIndex != -1) { startIndex += 1; nc = trimmedValue.Substring(startIndex, trimmedValue.Length - startIndex).Trim(); } } } // Verify username. if (userhash && ComputeHash(options.Username + ":" + realm, algorithm) != username) { return(false); } if (!userhash && options.Username != username) { return(false); } if (string.IsNullOrEmpty(algorithm)) { algorithm = "sha-256"; } // Calculate response and compare with the client response hash. string a1 = options.Username + ":" + realm + ":" + options.Password; if (algorithm.Contains("sess")) { a1 = ComputeHash(a1, algorithm) + ":" + nonce; if (cnonce != null) { a1 += ":" + cnonce; } } string a2 = requestMethod + ":" + uri; if (!string.IsNullOrEmpty(qop) && qop.Equals("auth-int")) { // Request content is empty. a2 = a2 + ":" + ComputeHash(string.Empty, algorithm); } string serverResponseHash = ComputeHash(a1, algorithm) + ":" + nonce + ":"; if (nc != null) { serverResponseHash += nc + ":"; } if (cnonce != null) { serverResponseHash += cnonce + ":"; } if (qop != null) { serverResponseHash += qop + ":"; } serverResponseHash += ComputeHash(a2, algorithm); serverResponseHash = ComputeHash(serverResponseHash, algorithm); return(response == serverResponseHash); }