diff --git a/src/WireMock.Net.Minimal/HttpsCertificate/CertificateLoader.cs b/src/WireMock.Net.Minimal/HttpsCertificate/CertificateLoader.cs
index 3e0a97d6..0dcc4b18 100644
--- a/src/WireMock.Net.Minimal/HttpsCertificate/CertificateLoader.cs
+++ b/src/WireMock.Net.Minimal/HttpsCertificate/CertificateLoader.cs
@@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
+using WireMock.Owin;
namespace WireMock.HttpsCertificate;
@@ -13,19 +14,13 @@ internal static class CertificateLoader
///
/// Used by the WireMock.Net server
///
- public static X509Certificate2 LoadCertificate(
- string? storeName,
- string? storeLocation,
- string? thumbprintOrSubjectName,
- string? filePath,
- string? passwordOrKey,
- string host)
+ public static X509Certificate2 LoadCertificate(IWireMockMiddlewareOptions options, string host)
{
- if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
+ if (!string.IsNullOrEmpty(options.X509StoreName) && !string.IsNullOrEmpty(options.X509StoreLocation))
{
- var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
+ var thumbprintOrSubjectNameOrHost = options.X509ThumbprintOrSubjectName ?? host;
- var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
+ var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), options.X509StoreName!), (StoreLocation)Enum.Parse(typeof(StoreLocation), options.X509StoreLocation!));
try
{
certStore.Open(OpenFlags.ReadOnly);
@@ -39,7 +34,7 @@ internal static class CertificateLoader
if (matchingCertificates.Count == 0)
{
// No certificates matched the search criteria.
- throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
+ throw new FileNotFoundException($"No Certificate found with in store '{options.X509StoreName}', location '{options.X509StoreLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
}
}
@@ -56,27 +51,28 @@ internal static class CertificateLoader
}
}
- if (!string.IsNullOrEmpty(filePath))
+ if (!string.IsNullOrEmpty(options.X509CertificateFilePath))
{
- if (filePath!.EndsWith(ExtensionPem, StringComparison.OrdinalIgnoreCase))
+ if (options.X509CertificateFilePath.EndsWith(ExtensionPem, StringComparison.OrdinalIgnoreCase))
{
// PEM logic based on: https://www.scottbrady91.com/c-sharp/pem-loading-in-dotnet-core-and-dotnet
#if NET5_0_OR_GREATER
- if (!string.IsNullOrEmpty(passwordOrKey))
+ if (!string.IsNullOrEmpty(options.X509CertificatePassword))
{
- var certPem = File.ReadAllText(filePath);
- var cert = X509Certificate2.CreateFromPem(certPem, passwordOrKey);
+ var certPem = File.ReadAllText(options.X509CertificateFilePath);
+ var cert = X509Certificate2.CreateFromPem(certPem, options.X509CertificatePassword);
const string defaultPasswordPem = "WireMock.Net";
+
return new X509Certificate2(cert.Export(X509ContentType.Pfx, defaultPasswordPem), defaultPasswordPem);
}
- return X509Certificate2.CreateFromPemFile(filePath);
+ return X509Certificate2.CreateFromPemFile(options.X509CertificateFilePath);
#elif NETCOREAPP3_1
- var cert = new X509Certificate2(filePath);
- if (!string.IsNullOrEmpty(passwordOrKey))
+ var cert = new X509Certificate2(options.X509CertificateFilePath);
+ if (!string.IsNullOrEmpty(options.X509CertificatePassword))
{
var key = System.Security.Cryptography.ECDsa.Create()!;
- key.ImportECPrivateKey(System.Text.Encoding.UTF8.GetBytes(passwordOrKey), out _);
+ key.ImportECPrivateKey(System.Text.Encoding.UTF8.GetBytes(options.X509CertificatePassword), out _);
return cert.CopyWithPrivateKey(key);
}
return cert;
@@ -85,10 +81,17 @@ internal static class CertificateLoader
#endif
}
- return !string.IsNullOrEmpty(passwordOrKey) ? new X509Certificate2(filePath, passwordOrKey) : new X509Certificate2(filePath);
+ return !string.IsNullOrEmpty(options.X509CertificatePassword) ?
+ new X509Certificate2(options.X509CertificateFilePath, options.X509CertificatePassword) :
+ new X509Certificate2(options.X509CertificateFilePath);
}
- throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
+ if (options.X509Certificate != null)
+ {
+ return options.X509Certificate;
+ }
+
+ throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath OR X509Certificate are mandatory. Note that X509CertificatePassword is optional.");
}
///
diff --git a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard.cs b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard.cs
index 510bd9ea..981ef04e 100644
--- a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard.cs
+++ b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard.cs
@@ -11,113 +11,105 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using CertificateLoader = WireMock.HttpsCertificate.CertificateLoader;
-namespace WireMock.Owin
+namespace WireMock.Owin;
+
+internal partial class AspNetCoreSelfHost
{
- internal partial class AspNetCoreSelfHost
+ private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{
- private static void SetKestrelOptionsLimits(KestrelServerOptions options)
- {
- options.Limits.MaxRequestBodySize = null; // https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel
- options.Limits.MaxRequestBufferSize = null;
- options.Limits.MaxRequestHeaderCount = 100;
- options.Limits.MaxResponseBufferSize = null;
- }
+ options.Limits.MaxRequestBodySize = null; // https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel
+ options.Limits.MaxRequestBufferSize = null;
+ options.Limits.MaxRequestHeaderCount = 100;
+ options.Limits.MaxResponseBufferSize = null;
+ }
- private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable urlDetails)
+ private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable urlDetails)
+ {
+ foreach (var urlDetail in urlDetails)
{
- foreach (var urlDetail in urlDetails)
+ if (urlDetail.IsHttps)
{
- if (urlDetail.IsHttps)
+ Listen(kestrelOptions, urlDetail, listenOptions =>
{
- Listen(kestrelOptions, urlDetail, listenOptions =>
+ listenOptions.UseHttps(options =>
{
- listenOptions.UseHttps(options =>
+ if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
- if (wireMockMiddlewareOptions.CustomCertificateDefined)
- {
- options.ServerCertificate = CertificateLoader.LoadCertificate(
- wireMockMiddlewareOptions.X509StoreName,
- wireMockMiddlewareOptions.X509StoreLocation,
- wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
- wireMockMiddlewareOptions.X509CertificateFilePath,
- wireMockMiddlewareOptions.X509CertificatePassword,
- urlDetail.Host
- );
- }
+ options.ServerCertificate = CertificateLoader.LoadCertificate(wireMockMiddlewareOptions, urlDetail.Host);
+ }
- options.ClientCertificateMode = (ClientCertificateMode)wireMockMiddlewareOptions.ClientCertificateMode;
- if (wireMockMiddlewareOptions.AcceptAnyClientCertificate)
- {
- options.ClientCertificateValidation = (_, _, _) => true;
- }
- });
-
- if (urlDetail.IsHttp2)
+ options.ClientCertificateMode = (ClientCertificateMode)wireMockMiddlewareOptions.ClientCertificateMode;
+ if (wireMockMiddlewareOptions.AcceptAnyClientCertificate)
{
- listenOptions.Protocols = HttpProtocols.Http2;
+ options.ClientCertificateValidation = (_, _, _) => true;
}
});
- continue;
- }
- if (urlDetail.IsHttp2)
- {
- Listen(kestrelOptions, urlDetail, listenOptions =>
+ if (urlDetail.IsHttp2)
{
listenOptions.Protocols = HttpProtocols.Http2;
- });
- continue;
- }
-
- Listen(kestrelOptions, urlDetail, _ => { });
+ }
+ });
+ continue;
}
- }
- private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action configure)
- {
- // Listens on any IP with the given port.
- if (urlDetail is { Port: > 0, Host: "0.0.0.0" })
+ if (urlDetail.IsHttp2)
{
- kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
- return;
- }
-
- // Listens on ::1 and 127.0.0.1 with the given port.
- if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "::1" })
- {
- kestrelOptions.ListenLocalhost(urlDetail.Port, configure);
- return;
+ Listen(kestrelOptions, urlDetail, listenOptions =>
+ {
+ listenOptions.Protocols = HttpProtocols.Http2;
+ });
+ continue;
}
- // Try to parse the host as a valid IP address and bind to the given IP address and port.
- if (IPAddress.TryParse(urlDetail.Host, out var ipAddress))
- {
- kestrelOptions.Listen(ipAddress, urlDetail.Port, configure);
- return;
- }
-
- // Otherwise, listen on all IPs.
- kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
+ Listen(kestrelOptions, urlDetail, _ => { });
}
}
- internal static class IWebHostBuilderExtensions
+ private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action configure)
{
- internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder)
+ // Listens on any IP with the given port.
+ if (urlDetail is { Port: > 0, Host: "0.0.0.0" })
{
- return builder.ConfigureAppConfiguration(config =>
- {
- config.AddEnvironmentVariables();
- });
+ kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
+ return;
}
- internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
+ // Listens on ::1 and 127.0.0.1 with the given port.
+ if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "::1" })
{
- return builder.ConfigureServices((context, services) =>
- {
- services.Configure(context.Configuration.GetSection("Kestrel"));
- });
+ kestrelOptions.ListenLocalhost(urlDetail.Port, configure);
+ return;
}
+
+ // Try to parse the host as a valid IP address and bind to the given IP address and port.
+ if (IPAddress.TryParse(urlDetail.Host, out var ipAddress))
+ {
+ kestrelOptions.Listen(ipAddress, urlDetail.Port, configure);
+ return;
+ }
+
+ // Otherwise, listen on all IPs.
+ kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
}
}
-#endif
+
+internal static class IWebHostBuilderExtensions
+{
+ internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder)
+ {
+ return builder.ConfigureAppConfiguration(config =>
+ {
+ config.AddEnvironmentVariables();
+ });
+ }
+
+ internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
+ {
+ return builder.ConfigureServices((context, services) =>
+ {
+ services.Configure(context.Configuration.GetSection("Kestrel"));
+ });
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard13.cs b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard13.cs
index 32821624..9e04420c 100644
--- a/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard13.cs
+++ b/src/WireMock.Net.Minimal/Owin/AspNetCoreSelfHost.NETStandard13.cs
@@ -28,19 +28,9 @@ internal partial class AspNetCoreSelfHost
{
options.UseHttps(new HttpsConnectionFilterOptions
{
- ServerCertificate = wireMockMiddlewareOptions.CustomCertificateDefined
- ? CertificateLoader.LoadCertificate(
- wireMockMiddlewareOptions.X509StoreName,
- wireMockMiddlewareOptions.X509StoreLocation,
- wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
- wireMockMiddlewareOptions.X509CertificateFilePath,
- wireMockMiddlewareOptions.X509CertificatePassword,
- urlDetail.Host)
- : PublicCertificateHelper.GetX509Certificate2(),
+ ServerCertificate = wireMockMiddlewareOptions.CustomCertificateDefined ? CertificateLoader.LoadCertificate(wireMockMiddlewareOptions, urlDetail.Host) : PublicCertificateHelper.GetX509Certificate2(),
ClientCertificateMode = (ClientCertificateMode) wireMockMiddlewareOptions.ClientCertificateMode,
- ClientCertificateValidation = wireMockMiddlewareOptions.AcceptAnyClientCertificate
- ? (_, _, _) => true
- : null,
+ ClientCertificateValidation = wireMockMiddlewareOptions.AcceptAnyClientCertificate ? (_, _, _) => true : null
});
}
}
diff --git a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs
index cd4c4d8d..d6f4c7ee 100644
--- a/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs
+++ b/src/WireMock.Net.Minimal/Owin/IWireMockMiddlewareOptions.cs
@@ -7,6 +7,10 @@ using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util;
+using System.Security.Cryptography.X509Certificates;
+
+using JetBrains.Annotations;
+
#if !USE_ASPNETCORE
using Owin;
#else
@@ -70,6 +74,11 @@ internal interface IWireMockMiddlewareOptions
string? X509CertificateFilePath { get; set; }
+ ///
+ /// A X.509 certificate instance.
+ ///
+ public X509Certificate2? X509Certificate { get; set; }
+
string? X509CertificatePassword { get; set; }
bool CustomCertificateDefined { get; }
diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs
index eff4c4e5..6f675459 100644
--- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs
+++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptions.cs
@@ -7,6 +7,8 @@ using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util;
+using System.Security.Cryptography.X509Certificates;
+
#if !USE_ASPNETCORE
using Owin;
#else
@@ -81,13 +83,17 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
///
public string? X509CertificateFilePath { get; set; }
+ ///
+ public X509Certificate2? X509Certificate { get; set; }
+
///
public string? X509CertificatePassword { get; set; }
///
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
- !string.IsNullOrEmpty(X509CertificateFilePath);
+ !string.IsNullOrEmpty(X509CertificateFilePath) ||
+ X509Certificate != null;
///
public bool? SaveUnmatchedRequests { get; set; }
diff --git a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs
index 7ba92b5f..a5dbd144 100644
--- a/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs
+++ b/src/WireMock.Net.Minimal/Owin/WireMockMiddlewareOptionsHelper.cs
@@ -39,6 +39,7 @@ internal static class WireMockMiddlewareOptionsHelper
options.X509StoreName = settings.CertificateSettings!.X509StoreName;
options.X509StoreLocation = settings.CertificateSettings.X509StoreLocation;
options.X509ThumbprintOrSubjectName = settings.CertificateSettings.X509StoreThumbprintOrSubjectName;
+ options.X509Certificate = settings.CertificateSettings.X509Certificate;
options.X509CertificateFilePath = settings.CertificateSettings.X509CertificateFilePath;
options.X509CertificatePassword = settings.CertificateSettings.X509CertificatePassword;
}
diff --git a/src/WireMock.Net.Minimal/Settings/WireMockCertificateSettings.cs b/src/WireMock.Net.Minimal/Settings/WireMockCertificateSettings.cs
index c9ae39ef..b24fbcf0 100644
--- a/src/WireMock.Net.Minimal/Settings/WireMockCertificateSettings.cs
+++ b/src/WireMock.Net.Minimal/Settings/WireMockCertificateSettings.cs
@@ -1,5 +1,6 @@
// Copyright © WireMock.Net
+using System.Security.Cryptography.X509Certificates;
using JetBrains.Annotations;
namespace WireMock.Settings;
@@ -9,36 +10,44 @@ namespace WireMock.Settings;
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
-/// X509CertificateFilePath and X509CertificatePassword should be defined
+/// X509CertificateFilePath should be defined
+/// OR
+/// X509Certificate should be defined
///
public class WireMockCertificateSettings
{
///
- /// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
+ /// X.509 certificate StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
///
[PublicAPI]
public string? X509StoreName { get; set; }
///
- /// X509 StoreLocation (CurrentUser or LocalMachine)
+ /// X.509 certificate StoreLocation (CurrentUser or LocalMachine)
///
[PublicAPI]
public string? X509StoreLocation { get; set; }
///
- /// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
+ /// X.509 certificate Thumbprint or SubjectName (if not defined, the 'host' is used)
///
[PublicAPI]
public string? X509StoreThumbprintOrSubjectName { get; set; }
///
- /// X509Certificate FilePath
+ /// X.509 certificate FilePath
///
[PublicAPI]
public string? X509CertificateFilePath { get; set; }
///
- /// X509Certificate Password
+ /// A X.509 certificate instance.
+ ///
+ [PublicAPI]
+ public X509Certificate2? X509Certificate { get; set; }
+
+ ///
+ /// X.509 certificate Password
///
[PublicAPI]
public string? X509CertificatePassword { get; set; }
@@ -46,10 +55,13 @@ public class WireMockCertificateSettings
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
- /// X509CertificateFilePath and X509CertificatePassword should be defined
+ /// X509CertificateFilePath should be defined
+ /// OR
+ /// X509Certificate should be defined
///
[PublicAPI]
public bool IsDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
- !string.IsNullOrEmpty(X509CertificateFilePath);
+ !string.IsNullOrEmpty(X509CertificateFilePath) ||
+ X509Certificate != null;
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Minimal/Settings/WireMockServerSettings.cs b/src/WireMock.Net.Minimal/Settings/WireMockServerSettings.cs
index 1066ac6b..8fb01e22 100644
--- a/src/WireMock.Net.Minimal/Settings/WireMockServerSettings.cs
+++ b/src/WireMock.Net.Minimal/Settings/WireMockServerSettings.cs
@@ -235,7 +235,9 @@ public class WireMockServerSettings
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
- /// X509CertificateFilePath and X509CertificatePassword should be defined
+ /// X509CertificateFilePath should be defined
+ /// OR
+ /// X509Certificate should be defined
///
[PublicAPI]
public WireMockCertificateSettings? CertificateSettings { get; set; }