Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.

Commit a500d06

Browse files
committed
Add test for mutual TLS authentication
1 parent 4641d47 commit a500d06

File tree

5 files changed

+90
-42
lines changed

5 files changed

+90
-42
lines changed

src/Titanium.Web.Proxy/CertificateHandler.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,6 @@ internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sess
5656
{
5757
X509Certificate? clientCertificate = null;
5858

59-
//TODO: Can we use client certificate from client socket's Sslstream.RemoteCertificate?
60-
//Because only the client can provide the correct certificate.
61-
//Proxy has no idea about client certificate when its running on a remote machine.
62-
//That would mean we need to delay AuthenticateAsServer call with client until we reach this method
63-
//and decide right here if we should set SslServerAuthenticationOptions.ClientCertificateRequired = true for clientStream.AuthenticateAsServer call.
64-
//Sounds like a very complicated change, but technically possible.
65-
6659
//fallback to the first client certificate from proxy machine certificate store
6760
if (acceptableIssuers != null && acceptableIssuers.Length > 0 && localCertificates != null &&
6861
localCertificates.Count > 0)
@@ -92,7 +85,7 @@ internal bool ValidateServerCertificate(object sender, SessionEventArgsBase sess
9285
ClientCertificate = clientCertificate
9386
};
9487

95-
// why is the sender null?
88+
9689
ClientCertificateSelectionCallback.InvokeAsync(this, args, ExceptionFunc).Wait();
9790
return args.ClientCertificate;
9891
}

tests/Titanium.Web.Proxy.IntegrationTests/HttpsTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.IO;
23
using System.Net;
34
using System.Net.Http;
5+
using System.Security.Cryptography.X509Certificates;
46
using System.Threading.Tasks;
57
using Microsoft.AspNetCore.Http;
68
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -62,5 +64,36 @@ public async Task Can_Handle_Https_Fake_Tunnel_Request()
6264
Assert.AreEqual("I am server. I received your greetings.", body);
6365
}
6466

67+
[TestMethod]
68+
public async Task Can_Handle_Https_Mutual_Tls_Request()
69+
{
70+
var testSuite = new TestSuite(true);
71+
72+
var server = testSuite.GetServer();
73+
server.HandleRequest((context) =>
74+
{
75+
return context.Response.WriteAsync("I am server. I received your greetings.");
76+
});
77+
78+
var proxy = testSuite.GetProxy();
79+
var clientCert = proxy.CertificateManager.CreateCertificate("client.com", false);
80+
81+
proxy.ClientCertificateSelectionCallback += async (sender, e) =>
82+
{
83+
e.ClientCertificate = clientCert;
84+
await Task.CompletedTask;
85+
};
86+
87+
var client = testSuite.GetClient(proxy);
88+
89+
var response = await client.PostAsync(new Uri(server.ListeningHttpsUrl),
90+
new StringContent("hello server. I am a client."));
91+
92+
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
93+
var body = await response.Content.ReadAsStringAsync();
94+
95+
Assert.AreEqual("I am server. I received your greetings.", body);
96+
}
97+
6598
}
6699
}

tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestServer.cs

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@
44
using System.Security.Cryptography.X509Certificates;
55
using System.Threading.Tasks;
66
using Microsoft.AspNetCore;
7+
using Microsoft.AspNetCore.Authentication.Certificate;
78
using Microsoft.AspNetCore.Builder;
89
using Microsoft.AspNetCore.Connections;
910
using Microsoft.AspNetCore.Hosting;
11+
using Microsoft.AspNetCore.Hosting.Server;
1012
using Microsoft.AspNetCore.Hosting.Server.Features;
1113
using Microsoft.AspNetCore.Http;
14+
using Microsoft.AspNetCore.Http.Features;
15+
using Microsoft.AspNetCore.Server.Kestrel.Https;
1216
using Microsoft.Extensions.DependencyInjection;
17+
using Microsoft.Extensions.Hosting;
18+
using Microsoft.Extensions.Logging;
1319

1420
namespace Titanium.Web.Proxy.IntegrationTests.Setup
1521
{
@@ -24,42 +30,57 @@ public class TestServer : IDisposable
2430
public int HttpsListeningPort { get; private set; }
2531
public int TcpListeningPort { get; private set; }
2632

27-
private IWebHost host;
28-
public TestServer(X509Certificate2 serverCertificate)
33+
private readonly IHost host;
34+
public TestServer(X509Certificate2 serverCertificate, bool requireMutualTls)
2935
{
30-
var startUp = new Startup(() => requestHandler);
31-
32-
host = WebHost.CreateDefaultBuilder()
33-
.ConfigureServices(s =>
36+
host = Host.CreateDefaultBuilder()
37+
.ConfigureLogging(logging =>
38+
{
39+
logging.ClearProviders();
40+
logging.AddDebug();
41+
logging.SetMinimumLevel(LogLevel.Trace);
42+
})
43+
.ConfigureWebHostDefaults(webBuilder =>
3444
{
35-
s.AddSingleton<IStartup>(startUp);
45+
webBuilder.UseStartup(x => new Startup(() => requestHandler));
46+
webBuilder.ConfigureKestrel(options =>
47+
{
48+
options.Listen(IPAddress.Loopback, 0);
49+
if (requireMutualTls)
50+
{
51+
options.ConfigureHttpsDefaults(options =>
52+
{
53+
options.ClientCertificateValidation = (certificate, chain, errors) =>
54+
{
55+
return true;
56+
};
57+
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
58+
});
59+
}
60+
options.Listen(IPAddress.Loopback, 0, listenOptions =>
61+
{
62+
listenOptions.UseHttps(serverCertificate);
63+
});
64+
options.Listen(IPAddress.Loopback, 0, listenOptions =>
65+
{
66+
listenOptions.Run(context =>
67+
{
68+
if (tcpRequestHandler == null)
69+
{
70+
throw new Exception("Test server not configured to handle tcp request.");
71+
}
72+
73+
return tcpRequestHandler(context);
74+
});
75+
});
76+
});
3677
})
37-
.UseKestrel(options =>
38-
{
39-
options.Listen(IPAddress.Loopback, 0);
40-
options.Listen(IPAddress.Loopback, 0, listenOptions =>
41-
{
42-
listenOptions.UseHttps(serverCertificate);
43-
});
44-
options.Listen(IPAddress.Loopback, 0, listenOptions =>
45-
{
46-
listenOptions.Run(context =>
47-
{
48-
if (tcpRequestHandler == null)
49-
{
50-
throw new Exception("Test server not configured to handle tcp request.");
51-
}
52-
53-
return tcpRequestHandler(context);
54-
});
55-
});
56-
})
5778
.Build();
5879

5980
host.Start();
6081

61-
var addresses = host.ServerFeatures
62-
.Get<IServerAddressesFeature>()
82+
var addresses = host.Services.GetRequiredService<IServer>()
83+
.Features.Get<IServerAddressesFeature>()
6384
.Addresses.ToArray();
6485

6586
HttpListeningPort = new Uri(addresses[0]).Port;
@@ -86,7 +107,7 @@ public void Dispose()
86107
host.Dispose();
87108
}
88109

89-
private class Startup : IStartup
110+
private class Startup
90111
{
91112
Func<Func<HttpContext, Task>> requestHandler;
92113
public Startup(Func<Func<HttpContext, Task>> requestHandler)
@@ -108,9 +129,9 @@ public void Configure(IApplicationBuilder app)
108129

109130
}
110131

111-
public IServiceProvider ConfigureServices(IServiceCollection services)
132+
public void ConfigureServices(IServiceCollection services)
112133
{
113-
return services.BuildServiceProvider();
134+
114135
}
115136
}
116137
}

tests/Titanium.Web.Proxy.IntegrationTests/Setup/TestSuite.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ public class TestSuite
88
{
99
private TestServer server;
1010

11-
public TestSuite()
11+
public TestSuite(bool requireMutualTls = false)
1212
{
1313
var dummyProxy = new ProxyServer();
1414
var serverCertificate = dummyProxy.CertificateManager.CreateServerCertificate("localhost").Result;
15-
server = new TestServer(serverCertificate);
15+
server = new TestServer(serverCertificate, requireMutualTls);
1616
}
1717

1818
public TestServer GetServer()

tests/Titanium.Web.Proxy.IntegrationTests/Titanium.Web.Proxy.IntegrationTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
<ItemGroup>
1616
<FrameworkReference Include="Microsoft.AspNetCore.App" />
17+
<PackageReference Include="Microsoft.AspNetCore.Authentication.Certificate" Version="5.0.7" />
1718
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0" />
1819
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.0" />
1920
<PackageReference Include="MSTest.TestAdapter" Version="2.1.2" />

0 commit comments

Comments
 (0)