|
1 |
| -using System.Security.Cryptography; |
| 1 | +using System.Security.Authentication; |
| 2 | +using System.Security.Cryptography; |
2 | 3 | using System.Security.Cryptography.X509Certificates;
|
3 | 4 | using Bit.Core.Platform.X509ChainCustomization;
|
4 | 5 | using Bit.Core.Settings;
|
| 6 | +using Microsoft.AspNetCore.Builder; |
| 7 | +using Microsoft.AspNetCore.Hosting; |
| 8 | +using Microsoft.AspNetCore.Server.Kestrel.Https; |
5 | 9 | using Microsoft.Extensions.Configuration;
|
6 | 10 | using Microsoft.Extensions.DependencyInjection;
|
7 | 11 | using Microsoft.Extensions.Hosting;
|
@@ -182,6 +186,104 @@ public void CustomLocation_NoCertificates_Logs()
|
182 | 186 | );
|
183 | 187 | }
|
184 | 188 |
|
| 189 | + [Fact] |
| 190 | + public async Task CallHttpWithSelfSignedCert_SelfSignedCertificateConfigured_Works() |
| 191 | + { |
| 192 | + var selfSignedCertificate = CreateSelfSignedCert("localhost"); |
| 193 | + await using var app = await CreateServerAsync(55555, options => |
| 194 | + { |
| 195 | + options.ServerCertificate = selfSignedCertificate; |
| 196 | + }); |
| 197 | + |
| 198 | + var services = CreateServices((gs, environment, config) => {}, services => |
| 199 | + { |
| 200 | + services.Configure<X509ChainOptions>(options => |
| 201 | + { |
| 202 | + options.AdditionalCustomTrustCertificates = [selfSignedCertificate]; |
| 203 | + }); |
| 204 | + }); |
| 205 | + |
| 206 | + var httpClient = services.GetRequiredService<IHttpClientFactory>().CreateClient(); |
| 207 | + |
| 208 | + var response = await httpClient.GetStringAsync("https://localhost:55555"); |
| 209 | + Assert.Equal("Hi", response); |
| 210 | + } |
| 211 | + |
| 212 | + [Fact] |
| 213 | + public async Task CallHttpWithSelfSignedCert_SelfSignedCertificateNotConfigured_Throws() |
| 214 | + { |
| 215 | + var selfSignedCertificate = CreateSelfSignedCert("localhost"); |
| 216 | + await using var app = await CreateServerAsync(55556, options => |
| 217 | + { |
| 218 | + options.ServerCertificate = selfSignedCertificate; |
| 219 | + }); |
| 220 | + |
| 221 | + var services = CreateServices((gs, environment, config) => {}, services => |
| 222 | + { |
| 223 | + services.Configure<X509ChainOptions>(options => |
| 224 | + { |
| 225 | + options.AdditionalCustomTrustCertificates = [CreateSelfSignedCert("example.com")]; |
| 226 | + }); |
| 227 | + }); |
| 228 | + |
| 229 | + var httpClient = services.GetRequiredService<IHttpClientFactory>().CreateClient(); |
| 230 | + |
| 231 | + var requestException = await Assert.ThrowsAsync<HttpRequestException>(async () => await httpClient.GetStringAsync("https://localhost:55556")); |
| 232 | + Assert.NotNull(requestException.InnerException); |
| 233 | + var authenticationException = Assert.IsAssignableFrom<AuthenticationException>(requestException.InnerException); |
| 234 | + Assert.Equal("The remote certificate was rejected by the provided RemoteCertificateValidationCallback.", authenticationException.Message); |
| 235 | + } |
| 236 | + |
| 237 | + [Fact] |
| 238 | + public async Task CallHttpWithSelfSignedCert_SelfSignedCertificateConfigured_WithExtraCert_Works() |
| 239 | + { |
| 240 | + var selfSignedCertificate = CreateSelfSignedCert("localhost"); |
| 241 | + await using var app = await CreateServerAsync(55557, options => |
| 242 | + { |
| 243 | + options.ServerCertificate = selfSignedCertificate; |
| 244 | + }); |
| 245 | + |
| 246 | + var services = CreateServices((gs, environment, config) => {}, services => |
| 247 | + { |
| 248 | + services.Configure<X509ChainOptions>(options => |
| 249 | + { |
| 250 | + options.AdditionalCustomTrustCertificates = [selfSignedCertificate, CreateSelfSignedCert("example.com")]; |
| 251 | + }); |
| 252 | + }); |
| 253 | + |
| 254 | + var httpClient = services.GetRequiredService<IHttpClientFactory>().CreateClient(); |
| 255 | + |
| 256 | + var response = await httpClient.GetStringAsync("https://localhost:55557"); |
| 257 | + Assert.Equal("Hi", response); |
| 258 | + } |
| 259 | + |
| 260 | + private static async Task<IAsyncDisposable> CreateServerAsync(int port, Action<HttpsConnectionAdapterOptions> configure) |
| 261 | + { |
| 262 | + // Start HTTP Server with self signed cert |
| 263 | + var builder = WebApplication.CreateSlimBuilder(); |
| 264 | + builder.Logging.AddFakeLogging(); |
| 265 | + builder.Services.AddRoutingCore(); |
| 266 | + builder.WebHost.UseKestrelCore() |
| 267 | + .ConfigureKestrel(options => |
| 268 | + { |
| 269 | + options.ListenLocalhost(port, listenOptions => |
| 270 | + { |
| 271 | + listenOptions.UseHttps(httpsOptions => |
| 272 | + { |
| 273 | + configure(httpsOptions); |
| 274 | + }); |
| 275 | + }); |
| 276 | + }); |
| 277 | + |
| 278 | + var app = builder.Build(); |
| 279 | + |
| 280 | + app.MapGet("/", () => "Hi"); |
| 281 | + |
| 282 | + await app.StartAsync(); |
| 283 | + |
| 284 | + return app; |
| 285 | + } |
| 286 | + |
185 | 287 | private static X509ChainOptions CreateOptions(Action<GlobalSettings, IHostEnvironment, Dictionary<string, string>> configure, Action<IServiceCollection>? after = null)
|
186 | 288 | {
|
187 | 289 | var services = CreateServices(configure, after);
|
|
0 commit comments