Add support for PEM certificates (#787)

* Support PEM

* net5

* 31

* txt

* FILE

* new

* Fixed

* .

* .

* RSA
This commit is contained in:
Stef Heyenrath
2022-08-16 13:26:00 +02:00
committed by GitHub
parent e2bd56531d
commit 330559b9fd
17 changed files with 354 additions and 131 deletions

View File

@@ -6,15 +6,17 @@ namespace WireMock.HttpsCertificate;
internal static class CertificateLoader
{
private const string ExtensionPem = ".PEM";
/// <summary>
/// Used by the WireMock.Net server
/// </summary>
public static X509Certificate2 LoadCertificate(
string storeName,
string storeLocation,
string thumbprintOrSubjectName,
string filePath,
string password,
string? storeName,
string? storeLocation,
string? thumbprintOrSubjectName,
string? filePath,
string? passwordOrKey,
string host)
{
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
@@ -47,19 +49,41 @@ internal static class CertificateLoader
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
certStore.Close();
#endif
}
}
if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
{
return new X509Certificate2(filePath, password);
}
if (!string.IsNullOrEmpty(filePath))
{
return new X509Certificate2(filePath);
if (filePath!.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))
{
var certPem = File.ReadAllText(filePath);
var cert = X509Certificate2.CreateFromPem(certPem, passwordOrKey);
const string defaultPasswordPem = "WireMock.Net";
return new X509Certificate2(cert.Export(X509ContentType.Pfx, defaultPasswordPem), defaultPasswordPem);
}
return X509Certificate2.CreateFromPemFile(filePath);
#elif NETCOREAPP3_1
var cert = new X509Certificate2(filePath);
if (!string.IsNullOrEmpty(passwordOrKey))
{
var key = System.Security.Cryptography.ECDsa.Create()!;
key.ImportECPrivateKey(System.Text.Encoding.UTF8.GetBytes(passwordOrKey), out _);
return cert.CopyWithPrivateKey(key);
}
return cert;
#else
throw new InvalidOperationException("Loading a PEM Certificate is only supported for .NET Core App 3.1, .NET 5.0 and higher.");
#endif
}
return !string.IsNullOrEmpty(passwordOrKey) ? new X509Certificate2(filePath, passwordOrKey) : new X509Certificate2(filePath);
}
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
@@ -97,7 +121,7 @@ internal static class CertificateLoader
#if NETSTANDARD || NET46
certStore.Dispose();
#else
certStore.Close();
certStore.Close();
#endif
}
}

View File

@@ -5,7 +5,6 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate;
using WireMock.Types;
namespace WireMock.Owin
{

View File

@@ -1,4 +1,4 @@
#if USE_ASPNETCORE && NETSTANDARD1_3
#if USE_ASPNETCORE && NETSTANDARD1_3
using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel;
@@ -6,58 +6,58 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate;
namespace WireMock.Owin
{
internal partial class AspNetCoreSelfHost
{
private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{
options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null;
}
namespace WireMock.Owin;
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
internal partial class AspNetCoreSelfHost
{
private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{
options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null;
}
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{
foreach (var urlDetail in urlDetails)
{
foreach (var urlDetail in urlDetails)
if (urlDetail.IsHttps)
{
if (urlDetail.IsHttps)
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
if (wireMockMiddlewareOptions.CustomCertificateDefined)
{
options.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
options.UseHttps(CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host)
);
}
else
{
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
}
}
}
}
}
internal static class IWebHostBuilderExtensions
internal static class IWebHostBuilderExtensions
{
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder;
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
{
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder;
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
return builder.ConfigureServices(services =>
{
var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
return builder.ConfigureServices(services =>
{
services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel"));
});
}
services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel"));
});
}
}
#endif

View File

@@ -132,7 +132,7 @@ namespace WireMock.Owin
#elif NETSTANDARD2_1
_logger.Info("Server using netstandard2.1");
#elif NETCOREAPP3_1
_logger.Info("Server using .NET Core 3.1");
_logger.Info("Server using .NET Core App 3.1");
#elif NET5_0
_logger.Info("Server using .NET 5.0");
#elif NET6_0

View File

@@ -12,62 +12,61 @@ using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
#endif
namespace WireMock.Owin
namespace WireMock.Owin;
internal interface IWireMockMiddlewareOptions
{
internal interface IWireMockMiddlewareOptions
{
IWireMockLogger Logger { get; set; }
IWireMockLogger Logger { get; set; }
TimeSpan? RequestProcessingDelay { get; set; }
TimeSpan? RequestProcessingDelay { get; set; }
IStringMatcher? AuthenticationMatcher { get; set; }
IStringMatcher? AuthenticationMatcher { get; set; }
bool? AllowPartialMapping { get; set; }
bool? AllowPartialMapping { get; set; }
ConcurrentDictionary<Guid, IMapping> Mappings { get; }
ConcurrentDictionary<Guid, IMapping> Mappings { get; }
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
ConcurrentObservableCollection<LogEntry> LogEntries { get; }
int? RequestLogExpirationDuration { get; set; }
int? RequestLogExpirationDuration { get; set; }
int? MaxRequestLogCount { get; set; }
int? MaxRequestLogCount { get; set; }
Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
CorsPolicyOptions? CorsPolicyOptions { get; set; }
CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
IFileSystemHandler? FileSystemHandler { get; set; }
IFileSystemHandler? FileSystemHandler { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; }
bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
bool? DisableJsonBodyParsing { get; set; }
bool? DisableJsonBodyParsing { get; set; }
bool? DisableRequestBodyDecompressing { get; set; }
bool? DisableRequestBodyDecompressing { get; set; }
bool? HandleRequestsSynchronously { get; set; }
bool? HandleRequestsSynchronously { get; set; }
string? X509StoreName { get; set; }
string? X509StoreName { get; set; }
string? X509StoreLocation { get; set; }
string? X509StoreLocation { get; set; }
string? X509ThumbprintOrSubjectName { get; set; }
string? X509ThumbprintOrSubjectName { get; set; }
string? X509CertificateFilePath { get; set; }
string? X509CertificateFilePath { get; set; }
string? X509CertificatePassword { get; set; }
string? X509CertificatePassword { get; set; }
bool CustomCertificateDefined { get; }
bool CustomCertificateDefined { get; }
bool? SaveUnmatchedRequests { get; set; }
}
bool? SaveUnmatchedRequests { get; set; }
}

View File

@@ -12,77 +12,76 @@ using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
#endif
namespace WireMock.Owin
namespace WireMock.Owin;
internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
{
internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
{
public IWireMockLogger Logger { get; set; }
public IWireMockLogger Logger { get; set; }
public TimeSpan? RequestProcessingDelay { get; set; }
public TimeSpan? RequestProcessingDelay { get; set; }
public IStringMatcher AuthenticationMatcher { get; set; }
public IStringMatcher AuthenticationMatcher { get; set; }
public bool? AllowPartialMapping { get; set; }
public bool? AllowPartialMapping { get; set; }
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new ConcurrentDictionary<string, ScenarioState>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new();
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new ConcurrentObservableCollection<LogEntry>();
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new();
public int? RequestLogExpirationDuration { get; set; }
public int? RequestLogExpirationDuration { get; set; }
public int? MaxRequestLogCount { get; set; }
public int? MaxRequestLogCount { get; set; }
public Action<IAppBuilder> PreWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder>? PreWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
public Action<IAppBuilder>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
public Action<IServiceCollection> AdditionalServiceRegistration { get; set; }
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowBodyForAllHttpMethods"/>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowBodyForAllHttpMethods"/>
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowOnlyDefinedHttpStatusCodeInResponse"/>
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.AllowOnlyDefinedHttpStatusCodeInResponse"/>
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableJsonBodyParsing"/>
public bool? DisableJsonBodyParsing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableJsonBodyParsing"/>
public bool? DisableJsonBodyParsing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableRequestBodyDecompressing"/>
public bool? DisableRequestBodyDecompressing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.DisableRequestBodyDecompressing"/>
public bool? DisableRequestBodyDecompressing { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.HandleRequestsSynchronously"/>
public bool? HandleRequestsSynchronously { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.HandleRequestsSynchronously"/>
public bool? HandleRequestsSynchronously { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreName"/>
public string X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreName"/>
public string? X509StoreName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreLocation"/>
public string X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509StoreLocation"/>
public string? X509StoreLocation { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509ThumbprintOrSubjectName"/>
public string X509ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509ThumbprintOrSubjectName"/>
public string? X509ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificateFilePath"/>
public string X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificateFilePath"/>
public string? X509CertificateFilePath { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificatePassword"/>
public string X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.X509CertificatePassword"/>
public string? X509CertificatePassword { get; set; }
/// <inheritdoc cref="IWireMockMiddlewareOptions.CustomCertificateDefined"/>
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath);
/// <inheritdoc cref="IWireMockMiddlewareOptions.CustomCertificateDefined"/>
public bool CustomCertificateDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath);
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; }
}
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; }
}