diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 104064bd3..e295c7e7c 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -71,6 +71,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.WebApplication.NETCore3", "examples\WireMock.Net.WebApplication.NETCore3\WireMock.Net.WebApplication.NETCore3.csproj", "{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.NETCoreApp3WithCertificate", "examples\WireMock.Net.Console.NETCoreApp3WithCertificate\WireMock.Net.Console.NETCoreApp3WithCertificate.csproj", "{925E421A-1B3F-4202-B48F-734743573A4B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -173,6 +175,10 @@ Global
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {925E421A-1B3F-4202-B48F-734743573A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {925E421A-1B3F-4202-B48F-734743573A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {925E421A-1B3F-4202-B48F-734743573A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {925E421A-1B3F-4202-B48F-734743573A4B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -202,6 +208,7 @@ Global
{B6269AAC-170A-4346-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{6F38CB3A-6DA1-408A-AECD-E434523C2838} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{E1C56967-3DC7-46CB-A1DF-B13167A0D9D4} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
+ {925E421A-1B3F-4202-B48F-734743573A4B} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
diff --git a/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/Program.cs b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/Program.cs
new file mode 100644
index 000000000..54878ddc2
--- /dev/null
+++ b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/Program.cs
@@ -0,0 +1,36 @@
+using WireMock.Logging;
+using WireMock.Server;
+using WireMock.Settings;
+
+namespace WireMock.Net.Console.NETCoreApp3WithCertificate
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ string url = "https://localhost:8433/";
+
+ var server = WireMockServer.Start(new WireMockServerSettings
+ {
+ Urls = new[] { url },
+ StartAdminInterface = true,
+ Logger = new WireMockConsoleLogger(),
+ CertificateSettings = new WireMockCertificateSettings
+ {
+ X509StoreName = "My",
+ X509StoreLocation = "CurrentUser",
+ X509StoreThumbprintOrSubjectName = "FE16586076A8B3F3E2F1466803A6C4C7CA35455B"
+
+ // X509CertificateFilePath = "example.pfx",
+ // X509CertificatePassword = "wiremock"
+ }
+
+ });
+ System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
+
+ System.Console.WriteLine("Press any key to stop the server");
+ System.Console.ReadKey();
+ server.Stop();
+ }
+ }
+}
\ No newline at end of file
diff --git a/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/WireMock.Net.Console.NETCoreApp3WithCertificate.csproj b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/WireMock.Net.Console.NETCoreApp3WithCertificate.csproj
new file mode 100644
index 000000000..3e04aa7e3
--- /dev/null
+++ b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/WireMock.Net.Console.NETCoreApp3WithCertificate.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
diff --git a/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.cer b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.cer
new file mode 100644
index 000000000..4826bb012
--- /dev/null
+++ b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.cer
@@ -0,0 +1,28 @@
+-----BEGIN CERTIFICATE-----
+MIIEsDCCApigAwIBAgIQJbH6hSGKdoFI0B7qCIOK7jANBgkqhkiG9w0BAQUFADAU
+MRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMjAxMDMwMjMwMDAwWhcNMzAxMTA2MjMw
+MDAwWjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4IC
+DwAwggIKAoICAQCl5fQSrRgT3Q6WoULR98Y+rrDWtTTgVpbLU04G0hLZ4yUeP7Wa
+yuVbvx7zX8XT4lA8Hu5T/GG91U077JcSSEjnPBFsh4hE7FkRoSYIEW6BFG7D7eUG
+dGHnDV8UkSRQ97LJPyjXuHVDJzNDJ9xQGMzOZ4n8vQ7SEKBw9hRG2ugkP5b2jVIN
+e1E549tq2jnIVpKCZ4+prf64ZLsaokX7VHe+b/CW3GoAqUUaUjdTpAQ7LpypJuFz
+415enOrKQe+UEBdqhGlgcC/O/Bw0uq4qVk+NNe5DEINVwoYs9XjNdzxuIkkAtcCt
+avTEzhHf8zWYLb5Nt2DIOcRGVELvRhsBX4um5f7dOGzMbXzBfUdjkP2O4hi6crhm
+Hba5bNkj4Zw2EHR9Xua3nadGCj22z0vpMKP2gXdFVnxFqQlaUWBLtwwN9p6tCQHl
+kU7wypvOHUsMa2Ojg5eZP4RpYFvZG3kkc9zTZCSakgw2n0ampBbvxPP11/AYIXtz
+HKu3CKcpjVQ+lE0DAU/Mm77QJ24TMbXmAydwCf1UCdFbDUZhdM9lspHvA0J9eiCv
+LOE94BrpVKuZ6TrAW0LZjAmBnkqYQAewhTW7GSgARE+QQcwfyu03Ck7id3Zt4FeQ
+sQDo0NNj7zQOy3Y1GK0ZYAVZv/GUeHMkxpClSWPoub/f5SJ4YzD5Il0cQQIDAQAB
+MA0GCSqGSIb3DQEBBQUAA4ICAQBd91xfUepnWcKwmupie2h1CAAQZEunyW78i++t
+evABfBu0TgV4s6Xe0umFv9V4r+O+rrF3ddSudbSOPBEb0Ooe+e3YGlNk1JrI1EEn
+fhb0YI8bMfBNpl85yNqxgByra7JF2mG4qbAnjrCs/PZkXo/34N29SY6dyZ7mffR3
+r/l01Rdm3ogRwGkiMUeKb3iGwLUy1T55svuI3Zc13N+NJT1s9NqpwWeK/jFK/WRN
+5Hi9W3DmlGCYAwFPCyBaQagxpGuGIpNsU0hKp86W5EvJpBpmCihfwlydH8ZbkHJ9
+jx2UDgTCaDzmaiKysiTP2HHDBsReL4tjakBksa9jkTfy5ajB53F3aUVs4jvTA46L
+w8wcAJlRPBz5siBrv4CH/0lBMyNeYzuqmDY3ulF4IMKNb5Kk9Ye4Pt0474z50A4v
+fSah+9iwI/mubaJ5tK522AtWtUoOIAswIwpDQyNeJPOggyzT2Y2OYZdGuFAoMYuq
+ZD58k4Yo+vky9K88l8NuzNJJvtgTKtT+/9qfMucxFmnvwbKEEULP3sw1FUKkPtM4
+f242FIV/XnOeloDmhGGeTB7aODB+gGCvgmOH92njjUEIv+SnYQkflQaRhhyNIACi
+ZvWlP/96H+X4fUG5kVNBHY021ZWmurUDqVxWUaswg63+DfsZcYtt6wgxiAN4ssXG
+wLnLPw==
+-----END CERTIFICATE-----
diff --git a/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.privatekey b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.privatekey
new file mode 100644
index 000000000..3e6949a59
Binary files /dev/null and b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/base64_encoded.privatekey differ
diff --git a/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/example.pfx b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/example.pfx
new file mode 100644
index 000000000..434ba1321
Binary files /dev/null and b/examples/WireMock.Net.Console.NETCoreApp3WithCertificate/example.pfx differ
diff --git a/src/WireMock.Net/Http/HttpClientHelper.cs b/src/WireMock.Net/Http/HttpClientHelper.cs
index 1eb163e41..eab1ac385 100644
--- a/src/WireMock.Net/Http/HttpClientHelper.cs
+++ b/src/WireMock.Net/Http/HttpClientHelper.cs
@@ -41,7 +41,7 @@ public static HttpClient CreateHttpClient(IProxyAndRecordSettings settings)
{
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
- var x509Certificate2 = ClientCertificateHelper.GetCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
+ var x509Certificate2 = CertificateLoader.LoadCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
handler.ClientCertificates.Add(x509Certificate2);
}
diff --git a/src/WireMock.Net/HttpsCertificate/CertificateLoader.cs b/src/WireMock.Net/HttpsCertificate/CertificateLoader.cs
new file mode 100644
index 000000000..409bbb6c6
--- /dev/null
+++ b/src/WireMock.Net/HttpsCertificate/CertificateLoader.cs
@@ -0,0 +1,100 @@
+using System;
+using System.IO;
+using System.Security.Cryptography.X509Certificates;
+
+namespace WireMock.HttpsCertificate
+{
+ internal static class CertificateLoader
+ {
+ ///
+ /// Used by the WireMock.Net server
+ ///
+ public static X509Certificate2 LoadCertificate(
+ string storeName,
+ string storeLocation,
+ string thumbprintOrSubjectName,
+ string filePath,
+ string password,
+ string host)
+ {
+ if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
+ {
+ var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
+
+ var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
+ try
+ {
+ certStore.Open(OpenFlags.ReadOnly);
+
+ // Attempt to find by Thumbprint first
+ var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
+ if (matchingCertificates.Count == 0)
+ {
+ // Fallback to SubjectName
+ matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
+ 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}'.");
+ }
+ }
+
+ // Use the first matching certificate.
+ return matchingCertificates[0];
+ }
+ finally
+ {
+#if NETSTANDARD || NET46
+ certStore.Dispose();
+#else
+ certStore.Close();
+#endif
+ }
+ }
+
+ if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
+ {
+ return new X509Certificate2(filePath, password);
+ }
+
+ throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath and X509CertificatePassword are mandatory.");
+ }
+
+ ///
+ /// Used for Proxy
+ ///
+ public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
+ {
+ var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
+ try
+ {
+ // Certificate must be in the local machine store
+ certStore.Open(OpenFlags.ReadOnly);
+
+ // Attempt to find by Thumbprint first
+ var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
+ if (matchingCertificates.Count == 0)
+ {
+ // Fallback to SubjectName
+ matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
+ if (matchingCertificates.Count == 0)
+ {
+ // No certificates matched the search criteria.
+ throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
+ }
+ }
+
+ // Use the first matching certificate.
+ return matchingCertificates[0];
+ }
+ finally
+ {
+#if NETSTANDARD || NET46
+ certStore.Dispose();
+#else
+ certStore.Close();
+#endif
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/HttpsCertificate/ClientCertificateHelper.cs b/src/WireMock.Net/HttpsCertificate/ClientCertificateHelper.cs
deleted file mode 100644
index bd81e4146..000000000
--- a/src/WireMock.Net/HttpsCertificate/ClientCertificateHelper.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-using System.IO;
-using System.Security.Cryptography.X509Certificates;
-
-namespace WireMock.HttpsCertificate
-{
- internal static class ClientCertificateHelper
- {
- public static X509Certificate2 GetCertificate(string thumbprintOrSubjectName)
- {
- X509Store certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
- try
- {
- // Certificate must be in the local machine store
- certStore.Open(OpenFlags.ReadOnly);
-
- // Attempt to find by thumbprint first
- var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
- if (matchingCertificates.Count == 0)
- {
- // Fallback to subject name
- matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
- if (matchingCertificates.Count == 0)
- {
- // No certificates matched the search criteria.
- throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
- }
- }
-
- // Use the first matching certificate.
- return matchingCertificates[0];
- }
- finally
- {
-#if NETSTANDARD || NET46
- certStore.Dispose();
-#else
- certStore.Close();
-#endif
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs
index 19b9a15a0..aefc35e59 100644
--- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs
+++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard.cs
@@ -1,10 +1,10 @@
#if USE_ASPNETCORE && !NETSTANDARD1_3
-using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
+using WireMock.HttpsCertificate;
namespace WireMock.Owin
{
@@ -18,20 +18,34 @@ private static void SetKestrelOptionsLimits(KestrelServerOptions options)
options.Limits.MaxResponseBufferSize = null;
}
- private static void SetHttpsAndUrls(KestrelServerOptions options, ICollection<(string Url, int Port)> urlDetails)
+ private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable urlDetails)
{
- foreach (var detail in urlDetails)
+ foreach (var urlDetail in urlDetails)
{
- if (detail.Url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
+ if (urlDetail.IsHttps)
{
- options.Listen(System.Net.IPAddress.Any, detail.Port, listenOptions =>
+ kestrelOptions.Listen(System.Net.IPAddress.Any, urlDetail.Port, listenOptions =>
{
- listenOptions.UseHttps();
+ if (wireMockMiddlewareOptions.CustomCertificateDefined)
+ {
+ listenOptions.UseHttps(CertificateLoader.LoadCertificate(
+ wireMockMiddlewareOptions.X509StoreName,
+ wireMockMiddlewareOptions.X509StoreLocation,
+ wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
+ wireMockMiddlewareOptions.X509CertificateFilePath,
+ wireMockMiddlewareOptions.X509CertificatePassword,
+ urlDetail.Host)
+ );
+ }
+ else
+ {
+ listenOptions.UseHttps();
+ }
});
}
else
{
- options.Listen(System.Net.IPAddress.Any, detail.Port);
+ kestrelOptions.Listen(System.Net.IPAddress.Any, urlDetail.Port);
}
}
}
diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard13.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard13.cs
index 80798cb7a..0d2bf3512 100644
--- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard13.cs
+++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.NETStandard13.cs
@@ -1,7 +1,5 @@
#if USE_ASPNETCORE && NETSTANDARD1_3
-using System;
using System.Collections.Generic;
-using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.Extensions.Configuration;
@@ -19,12 +17,28 @@ private static void SetKestrelOptionsLimits(KestrelServerOptions options)
options.Limits.MaxResponseBufferSize = null;
}
- private static void SetHttpsAndUrls(KestrelServerOptions options, ICollection<(string Url, int Port)> urlDetails)
+ private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable urlDetails)
{
- var urls = urlDetails.Select(u => u.Url);
- if (urls.Any(u => u.StartsWith("https://", StringComparison.OrdinalIgnoreCase)))
+ foreach (var urlDetail in urlDetails)
{
- options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
+ if (urlDetail.IsHttps)
+ {
+ if (wireMockMiddlewareOptions.CustomCertificateDefined)
+ {
+ options.UseHttps(CertificateLoader.LoadCertificate(
+ wireMockMiddlewareOptions.X509StoreName,
+ wireMockMiddlewareOptions.X509StoreLocation,
+ wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
+ wireMockMiddlewareOptions.X509CertificateFilePath,
+ wireMockMiddlewareOptions.X509CertificatePassword,
+ urlDetail.Host)
+ );
+ }
+ else
+ {
+ options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
+ }
+ }
}
}
}
diff --git a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs
index 651831591..717200d69 100644
--- a/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs
+++ b/src/WireMock.Net/Owin/AspNetCoreSelfHost.cs
@@ -18,7 +18,7 @@ namespace WireMock.Owin
internal partial class AspNetCoreSelfHost : IOwinSelfHost
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
- private readonly IWireMockMiddlewareOptions _options;
+ private readonly IWireMockMiddlewareOptions _wireMockMiddlewareOptions;
private readonly IWireMockLogger _logger;
private readonly HostUrlOptions _urlOptions;
@@ -33,14 +33,14 @@ internal partial class AspNetCoreSelfHost : IOwinSelfHost
public Exception RunningException => _runningException;
- public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions options, [NotNull] HostUrlOptions urlOptions)
+ public AspNetCoreSelfHost([NotNull] IWireMockMiddlewareOptions wireMockMiddlewareOptions, [NotNull] HostUrlOptions urlOptions)
{
- Check.NotNull(options, nameof(options));
+ Check.NotNull(wireMockMiddlewareOptions, nameof(wireMockMiddlewareOptions));
Check.NotNull(urlOptions, nameof(urlOptions));
- _logger = options.Logger ?? new WireMockConsoleLogger();
+ _logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
- _options = options;
+ _wireMockMiddlewareOptions = wireMockMiddlewareOptions;
_urlOptions = urlOptions;
}
@@ -61,7 +61,7 @@ public Task StartAsync()
.ConfigureAppConfigurationUsingEnvironmentVariables()
.ConfigureServices(services =>
{
- services.AddSingleton(_options);
+ services.AddSingleton(_wireMockMiddlewareOptions);
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
@@ -70,17 +70,17 @@ public Task StartAsync()
{
appBuilder.UseMiddleware();
- _options.PreWireMockMiddlewareInit?.Invoke(appBuilder);
+ _wireMockMiddlewareOptions.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware();
- _options.PostWireMockMiddlewareInit?.Invoke(appBuilder);
+ _wireMockMiddlewareOptions.PostWireMockMiddlewareInit?.Invoke(appBuilder);
})
.UseKestrel(options =>
{
SetKestrelOptionsLimits(options);
- SetHttpsAndUrls(options, _urlOptions.GetDetails());
+ SetHttpsAndUrls(options, _wireMockMiddlewareOptions, _urlOptions.GetDetails());
})
.ConfigureKestrelServerOptions()
@@ -107,7 +107,7 @@ private Task RunHost(CancellationToken token)
{
Urls.Add(address.Replace("0.0.0.0", "localhost"));
- PortUtils.TryExtract(address, out string protocol, out string host, out int port);
+ PortUtils.TryExtract(address, out bool isHttps, out string protocol, out string host, out int port);
Ports.Add(port);
}
diff --git a/src/WireMock.Net/Owin/HostUrlDetails.cs b/src/WireMock.Net/Owin/HostUrlDetails.cs
new file mode 100644
index 000000000..6bd8826ca
--- /dev/null
+++ b/src/WireMock.Net/Owin/HostUrlDetails.cs
@@ -0,0 +1,15 @@
+namespace WireMock.Owin
+{
+ internal class HostUrlDetails
+ {
+ public bool IsHttps { get; set; }
+
+ public string Url { get; set; }
+
+ public string Protocol { get; set; }
+
+ public string Host { get; set; }
+
+ public int Port { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Owin/HostUrlOptions.cs b/src/WireMock.Net/Owin/HostUrlOptions.cs
index e85c03245..dd94ce331 100644
--- a/src/WireMock.Net/Owin/HostUrlOptions.cs
+++ b/src/WireMock.Net/Owin/HostUrlOptions.cs
@@ -5,26 +5,29 @@ namespace WireMock.Owin
{
internal class HostUrlOptions
{
+ private const string LOCALHOST = "localhost";
+
public ICollection Urls { get; set; }
public int? Port { get; set; }
public bool UseSSL { get; set; }
- public ICollection<(string Url, int Port)> GetDetails()
+ public ICollection GetDetails()
{
- var list = new List<(string Url, int Port)>();
+ var list = new List();
if (Urls == null)
{
int port = Port > 0 ? Port.Value : FindFreeTcpPort();
- list.Add(($"{(UseSSL ? "https" : "http")}://localhost:{port}", port));
+ string protocol = UseSSL ? "https" : "http";
+ list.Add(new HostUrlDetails { IsHttps = UseSSL, Url = $"{protocol}://{LOCALHOST}:{port}", Protocol = protocol, Host = LOCALHOST, Port = port });
}
else
{
foreach (string url in Urls)
{
- PortUtils.TryExtract(url, out string protocol, out string host, out int port);
- list.Add((url, port));
+ PortUtils.TryExtract(url, out bool isHttps, out string protocol, out string host, out int port);
+ list.Add(new HostUrlDetails { IsHttps = isHttps, Url = url, Protocol = protocol, Host = host, Port = port });
}
}
diff --git a/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs
index 44f900e2f..f56eb877c 100644
--- a/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs
+++ b/src/WireMock.Net/Owin/IWireMockMiddlewareOptions.cs
@@ -47,5 +47,17 @@ internal interface IWireMockMiddlewareOptions
bool? DisableRequestBodyDecompressing { get; set; }
bool? HandleRequestsSynchronously { get; set; }
+
+ string X509StoreName { get; set; }
+
+ string X509StoreLocation { get; set; }
+
+ string X509ThumbprintOrSubjectName { get; set; }
+
+ string X509CertificateFilePath { get; set; }
+
+ string X509CertificatePassword { get; set; }
+
+ bool CustomCertificateDefined { get; }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs
index e6b1b138b..8b9698b15 100644
--- a/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs
+++ b/src/WireMock.Net/Owin/WireMockMiddlewareOptions.cs
@@ -53,5 +53,25 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
///
public bool? HandleRequestsSynchronously { get; set; }
+
+ ///
+ public string X509StoreName { get; set; }
+
+ ///
+ public string X509StoreLocation { get; set; }
+
+ ///
+ public string X509ThumbprintOrSubjectName { get; set; }
+
+ ///
+ public string X509CertificateFilePath { get; set; }
+
+ ///
+ public string X509CertificatePassword { get; set; }
+
+ ///
+ public bool CustomCertificateDefined =>
+ !string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
+ !string.IsNullOrEmpty(X509CertificateFilePath) && !string.IsNullOrEmpty(X509CertificatePassword);
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Server/WireMockServer.cs b/src/WireMock.Net/Server/WireMockServer.cs
index 8702c338f..a58adde89 100644
--- a/src/WireMock.Net/Server/WireMockServer.cs
+++ b/src/WireMock.Net/Server/WireMockServer.cs
@@ -230,6 +230,15 @@ protected WireMockServer(IWireMockServerSettings settings)
_options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing;
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
+ if (settings.CustomCertificateDefined)
+ {
+ _options.X509StoreName = settings.CertificateSettings.X509StoreName;
+ _options.X509StoreLocation = settings.CertificateSettings.X509StoreLocation;
+ _options.X509ThumbprintOrSubjectName = settings.CertificateSettings.X509StoreThumbprintOrSubjectName;
+ _options.X509CertificateFilePath = settings.CertificateSettings.X509CertificateFilePath;
+ _options.X509CertificatePassword = settings.CertificateSettings.X509CertificatePassword;
+ }
+
_matcherMapper = new MatcherMapper(_settings);
_mappingConverter = new MappingConverter(_matcherMapper);
diff --git a/src/WireMock.Net/Settings/IWireMockCertificateSettings.cs b/src/WireMock.Net/Settings/IWireMockCertificateSettings.cs
new file mode 100644
index 000000000..d2d476bb4
--- /dev/null
+++ b/src/WireMock.Net/Settings/IWireMockCertificateSettings.cs
@@ -0,0 +1,44 @@
+namespace WireMock.Settings
+{
+ ///
+ /// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
+ ///
+ /// X509StoreName and X509StoreLocation should be defined
+ /// OR
+ /// X509CertificateFilePath and X509CertificatePassword should be defined
+ ///
+ public interface IWireMockCertificateSettings
+ {
+ ///
+ /// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
+ ///
+ string X509StoreName { get; set; }
+
+ ///
+ /// X509 StoreLocation (CurrentUser or LocalMachine)
+ ///
+ string X509StoreLocation { get; set; }
+
+ ///
+ /// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
+ ///
+ string X509StoreThumbprintOrSubjectName { get; set; }
+
+ ///
+ /// X509Certificate FilePath
+ ///
+ string X509CertificateFilePath { get; set; }
+
+ ///
+ /// X509Certificate Password
+ ///
+ string X509CertificatePassword { get; set; }
+
+ ///
+ /// X509StoreName and X509StoreLocation should be defined
+ /// OR
+ /// X509CertificateFilePath and X509CertificatePassword should be defined
+ ///
+ bool IsDefined { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/IWireMockServerSettings.cs b/src/WireMock.Net/Settings/IWireMockServerSettings.cs
index 65ce9a30c..ac511676d 100644
--- a/src/WireMock.Net/Settings/IWireMockServerSettings.cs
+++ b/src/WireMock.Net/Settings/IWireMockServerSettings.cs
@@ -170,5 +170,21 @@ public interface IWireMockServerSettings
///
[PublicAPI]
bool? ThrowExceptionWhenMatcherFails { get; set; }
+
+ ///
+ /// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
+ ///
+ /// X509StoreName and X509StoreLocation should be defined
+ /// OR
+ /// X509CertificateFilePath and X509CertificatePassword should be defined
+ ///
+ [PublicAPI]
+ IWireMockCertificateSettings CertificateSettings { get; set; }
+
+ ///
+ /// Defines if custom CertificateSettings are defined
+ ///
+ [PublicAPI]
+ bool CustomCertificateDefined { get; }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockCertificateSettings.cs b/src/WireMock.Net/Settings/WireMockCertificateSettings.cs
new file mode 100644
index 000000000..a9234c308
--- /dev/null
+++ b/src/WireMock.Net/Settings/WireMockCertificateSettings.cs
@@ -0,0 +1,36 @@
+using JetBrains.Annotations;
+
+namespace WireMock.Settings
+{
+ ///
+ ///
+ ///
+ public class WireMockCertificateSettings : IWireMockCertificateSettings
+ {
+ ///
+ [PublicAPI]
+ public string X509StoreName { get; set; }
+
+ ///
+ [PublicAPI]
+ public string X509StoreLocation { get; set; }
+
+ ///
+ [PublicAPI]
+ public string X509StoreThumbprintOrSubjectName { get; set; }
+
+ ///
+ [PublicAPI]
+ public string X509CertificateFilePath { get; set; }
+
+ ///
+ [PublicAPI]
+ public string X509CertificatePassword { get; set; }
+
+ ///
+ [PublicAPI]
+ public bool IsDefined =>
+ !string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
+ !string.IsNullOrEmpty(X509CertificateFilePath) && !string.IsNullOrEmpty(X509CertificatePassword);
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs
index ac1beb14c..1f655c4cf 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettings.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs
@@ -1,6 +1,6 @@
-using HandlebarsDotNet;
+using System;
+using HandlebarsDotNet;
using JetBrains.Annotations;
-using System;
using Newtonsoft.Json;
using WireMock.Handlers;
using WireMock.Logging;
@@ -121,5 +121,13 @@ public class WireMockServerSettings : IWireMockServerSettings
///
[PublicAPI]
public bool? ThrowExceptionWhenMatcherFails { get; set; }
+
+ ///
+ [PublicAPI]
+ public IWireMockCertificateSettings CertificateSettings { get; set; }
+
+ ///
+ [PublicAPI]
+ public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
index be16b68a3..ff00bac5b 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
@@ -60,12 +60,12 @@ public static IWireMockServerSettings ParseArguments([NotNull] string[] args, [C
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
}
- string proxyURL = parser.GetStringValue("ProxyURL");
- if (!string.IsNullOrEmpty(proxyURL))
+ string proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
+ if (!string.IsNullOrEmpty(proxyUrl))
{
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
{
- Url = proxyURL,
+ Url = proxyUrl,
SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern"),
@@ -87,6 +87,19 @@ public static IWireMockServerSettings ParseArguments([NotNull] string[] args, [C
}
}
+ var certificateSettings = new WireMockCertificateSettings
+ {
+ X509StoreName = parser.GetStringValue("X509StoreName"),
+ X509StoreLocation = parser.GetStringValue("X509StoreLocation"),
+ X509StoreThumbprintOrSubjectName = parser.GetStringValue("X509StoreThumbprintOrSubjectName"),
+ X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
+ X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
+ };
+ if (certificateSettings.IsDefined)
+ {
+ settings.CertificateSettings = certificateSettings;
+ }
+
return settings;
}
}
diff --git a/src/WireMock.Net/Util/PortUtils.cs b/src/WireMock.Net/Util/PortUtils.cs
index 83ffe39f6..6bb189984 100644
--- a/src/WireMock.Net/Util/PortUtils.cs
+++ b/src/WireMock.Net/Util/PortUtils.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System;
+using System.Net;
using System.Net.Sockets;
using System.Text.RegularExpressions;
@@ -32,21 +33,23 @@ public static int FindFreeTcpPort()
}
///
- /// Extract the protocol, host and port from a URL.
+ /// Extract the if-isHttps, protocol, host and port from a URL.
///
- public static bool TryExtract(string url, out string protocol, out string host, out int port)
+ public static bool TryExtract(string url, out bool isHttps, out string protocol, out string host, out int port)
{
+ isHttps = false;
protocol = null;
host = null;
- port = default(int);
+ port = default;
- Match m = UrlDetailsRegex.Match(url);
- if (m.Success)
+ var match = UrlDetailsRegex.Match(url);
+ if (match.Success)
{
- protocol = m.Groups["proto"].Value;
- host = m.Groups["host"].Value;
+ protocol = match.Groups["proto"].Value;
+ isHttps = protocol.StartsWith("https", StringComparison.OrdinalIgnoreCase);
+ host = match.Groups["host"].Value;
- return int.TryParse(m.Groups["port"].Value, out port);
+ return int.TryParse(match.Groups["port"].Value, out port);
}
return false;
diff --git a/test/WireMock.Net.Tests/Util/PortUtilsTests.cs b/test/WireMock.Net.Tests/Util/PortUtilsTests.cs
index 27f10df8f..af3c18efc 100644
--- a/test/WireMock.Net.Tests/Util/PortUtilsTests.cs
+++ b/test/WireMock.Net.Tests/Util/PortUtilsTests.cs
@@ -1,3 +1,4 @@
+using FluentAssertions;
using NFluent;
using WireMock.Util;
using Xunit;
@@ -13,13 +14,14 @@ public void PortUtils_TryExtract_InvalidUrl_Returns_False()
string url = "test";
// Act
- bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
+ bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
- Check.That(result).IsFalse();
- Check.That(proto).IsNull();
- Check.That(host).IsNull();
- Check.That(port).IsEqualTo(default(int));
+ result.Should().BeFalse();
+ isHttps.Should().BeFalse();
+ proto.Should().BeNull();
+ host.Should().BeNull();
+ port.Should().Be(default(int));
}
[Fact]
@@ -29,39 +31,58 @@ public void PortUtils_TryExtract_UrlIsMissingPort_Returns_False()
string url = "http://0.0.0.0";
// Act
- bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
+ bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
- Check.That(result).IsFalse();
- Check.That(proto).IsNull();
- Check.That(host).IsNull();
- Check.That(port).IsEqualTo(default(int));
+ result.Should().BeFalse();
+ isHttps.Should().BeFalse();
+ proto.Should().BeNull();
+ host.Should().BeNull();
+ port.Should().Be(default(int));
}
[Fact]
- public void PortUtils_TryExtract_ValidUrl1_Returns_True()
+ public void PortUtils_TryExtract_Http_Returns_True()
+ {
+ // Assign
+ string url = "http://wiremock.net:1234";
+
+ // Act
+ bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
+
+ // Assert
+ result.Should().BeTrue();
+ isHttps.Should().BeFalse();
+ proto.Should().Be("http");
+ host.Should().Be("wiremock.net");
+ port.Should().Be(1234);
+ }
+
+ [Fact]
+ public void PortUtils_TryExtract_Https_Returns_True()
{
// Assign
string url = "https://wiremock.net:5000";
// Act
- bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
+ bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
- Check.That(result).IsTrue();
- Check.That(proto).IsEqualTo("https");
- Check.That(host).IsEqualTo("wiremock.net");
- Check.That(port).IsEqualTo(5000);
+ result.Should().BeTrue();
+ isHttps.Should().BeTrue();
+ proto.Should().Be("https");
+ host.Should().Be("wiremock.net");
+ port.Should().Be(5000);
}
[Fact]
- public void PortUtils_TryExtract_ValidUrl2_Returns_True()
+ public void PortUtils_TryExtract_Https0_0_0_0_Returns_True()
{
// Assign
string url = "https://0.0.0.0:5000";
// Act
- bool result = PortUtils.TryExtract(url, out string proto, out string host, out int port);
+ bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
// Assert
Check.That(result).IsTrue();