diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs
index 537808cfe..60dccb50c 100644
--- a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs
+++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/IdentityServerBuilderExtensions.cs
@@ -1,7 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
using System;
+using System.Collections.Generic;
using System.IO;
+using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
+using CertificateManager;
+using CertificateManager.Models;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Skoruba.IdentityServer4.Shared.Configuration.Configuration.Common;
using Skoruba.IdentityServer4.Shared.Configuration.Helpers;
@@ -21,9 +26,10 @@ public static class IdentityServerBuilderExtensions
/// Add custom signing certificate from certification store according thumbprint or from file
///
///
+ ///
///
///
- public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentityServerBuilder builder, IConfiguration configuration)
+ public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentityServerBuilder builder, IWebHostEnvironment webHostEnvironment, IConfiguration configuration)
{
var certificateConfiguration = configuration.GetSection(nameof(CertificateConfiguration)).Get();
var azureKeyVaultConfiguration = configuration.GetSection(nameof(AzureKeyVaultConfiguration)).Get();
@@ -79,12 +85,17 @@ public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentitySe
throw new Exception(SigningCertificatePathIsNotSpecified);
}
- if (File.Exists(certificateConfiguration.SigningCertificatePfxFilePath))
+ string certificateFilePath = Path.Combine(webHostEnvironment.ContentRootPath, certificateConfiguration.SigningCertificatePfxFilePath);
+ if (!File.Exists(certificateFilePath) || certificateConfiguration.NeedToCreateNewOne)
+ {
+ CreateRsaCertificate(builder.Services, certificateConfiguration, certificateFilePath);
+ }
+
+ if (File.Exists(certificateFilePath))
{
-
try
{
- builder.AddSigningCredential(new X509Certificate2(certificateConfiguration.SigningCertificatePfxFilePath, certificateConfiguration.SigningCertificatePfxFilePassword));
+ builder.AddSigningCredential(new X509Certificate2(certificateFilePath, certificateConfiguration.SigningCertificatePfxFilePassword));
}
catch (Exception e)
{
@@ -108,6 +119,64 @@ public static IIdentityServerBuilder AddCustomSigningCredential(this IIdentitySe
return builder;
}
+ ///
+ /// Create RSA Certificate For Signing Credential - https://damienbod.com/2020/02/10/create-certificates-for-identityserver4-signing-using-net-core/
+ ///
+ ///
+ ///
+ ///
+ private static void CreateRsaCertificate(IServiceCollection services, CertificateConfiguration certificateConfiguration, string certificateFilePath)
+ {
+ services.AddCertificateManager();
+ var createCertificates = services.BuildServiceProvider().GetRequiredService();
+
+ var basicConstraints = new BasicConstraints
+ {
+ CertificateAuthority = false,
+ HasPathLengthConstraint = false,
+ PathLengthConstraint = 0,
+ Critical = false
+ };
+
+ var subjectAlternativeName = new SubjectAlternativeName
+ {
+ DnsName = new List
+ {
+ certificateConfiguration.DnsName
+ }
+ };
+
+ const X509KeyUsageFlags x509KeyUsageFlags = X509KeyUsageFlags.DigitalSignature;
+
+ // only if certification authentication is used
+ var enhancedKeyUsages = new OidCollection
+ {
+ OidLookup.ClientAuthentication,
+ OidLookup.ServerAuthentication
+ };
+
+ var certificate = createCertificates.NewRsaSelfSignedCertificate(
+ new DistinguishedName { CommonName = certificateConfiguration.DnsName },
+ basicConstraints,
+ new ValidityPeriod
+ {
+ ValidFrom = DateTimeOffset.UtcNow,
+ ValidTo = DateTimeOffset.UtcNow.AddYears(certificateConfiguration.ValidityPeriodInYear)
+ },
+ subjectAlternativeName,
+ enhancedKeyUsages,
+ x509KeyUsageFlags,
+ new RsaConfiguration
+ {
+ KeySize = 2048,
+ HashAlgorithmName = HashAlgorithmName.SHA512
+ });
+
+ var iec = services.BuildServiceProvider().GetRequiredService();
+ var rsaCertPfxBytes = iec.ExportSelfSignedCertificatePfx(certificateConfiguration.SigningCertificatePfxFilePassword, certificate);
+ File.WriteAllBytes(certificateFilePath, rsaCertPfxBytes);
+ }
+
///
/// Add custom validation key for signing key rollover
/// http://docs.identityserver.io/en/latest/topics/crypto.html#signing-key-rollover
diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs
index aeb2a9d6e..7e48cb3e6 100644
--- a/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs
+++ b/src/Skoruba.IdentityServer4.STS.Identity/Helpers/StartupHelpers.cs
@@ -23,6 +23,7 @@
using Skoruba.IdentityServer4.Admin.EntityFramework.Interfaces;
using Skoruba.IdentityServer4.Admin.EntityFramework.Helpers;
using Microsoft.AspNetCore.DataProtection.EntityFrameworkCore;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Identity.Web;
using Skoruba.IdentityServer4.Admin.EntityFramework.Configuration.Configuration;
@@ -40,6 +41,7 @@ public static class StartupHelpers
/// Register services for MVC and localization including available languages
///
///
+ ///
public static IMvcBuilder AddMvcWithLocalization(this IServiceCollection services, IConfiguration configuration)
where TUser : IdentityUser
where TKey : IEquatable
@@ -48,10 +50,10 @@ public static IMvcBuilder AddMvcWithLocalization(this IServiceColle
services.TryAddTransient(typeof(IGenericControllerLocalizer<>), typeof(GenericControllerLocalizer<>));
- var mvcBuilder = services.AddControllersWithViews(o =>
- {
- o.Conventions.Add(new GenericControllerRouteConvention());
- })
+ var mvcBuilder = services.AddControllersWithViews(o =>
+ {
+ o.Conventions.Add(new GenericControllerRouteConvention());
+ })
.AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = ConfigurationConsts.ResourcesPath; })
@@ -76,7 +78,7 @@ public static IMvcBuilder AddMvcWithLocalization(this IServiceColle
// If the default culture is specified use it, otherwise use CultureConfiguration.DefaultRequestCulture ("en")
var defaultCultureCode = string.IsNullOrEmpty(cultureConfiguration?.DefaultCulture) ?
- CultureConfiguration.DefaultRequestCulture : cultureConfiguration?.DefaultCulture;
+ CultureConfiguration.DefaultRequestCulture : cultureConfiguration.DefaultCulture;
// If the default culture is not among the supported cultures, use the first supported culture as default
if (!supportedCultureCodes.Contains(defaultCultureCode)) defaultCultureCode = supportedCultureCodes.FirstOrDefault();
@@ -186,6 +188,7 @@ public static void UseSecurityHeaders(this IApplicationBuilder app, IConfigurati
///
///
///
+ ///
///
///
public static void RegisterDbContexts(this IServiceCollection services, IConfiguration configuration)
@@ -224,6 +227,7 @@ public static void RegisterDbContexts
///
///
+ ///
///
public static void RegisterDbContextsStaging(
this IServiceCollection services)
@@ -345,9 +349,10 @@ private static RegisterConfiguration GetRegistrationConfiguration(IConfiguration
///
///
///
+ ///
///
public static IIdentityServerBuilder AddIdentityServer(
- this IServiceCollection services,
+ this IServiceCollection services, IWebHostEnvironment webHostEnvironment,
IConfiguration configuration)
where TPersistedGrantDbContext : DbContext, IAdminPersistedGrantDbContext
where TConfigurationDbContext : DbContext, IAdminConfigurationDbContext
@@ -360,7 +365,7 @@ public static IIdentityServerBuilder AddIdentityServer()
.AddAspNetIdentity();
- builder.AddCustomSigningCredential(configuration);
+ builder.AddCustomSigningCredential(webHostEnvironment, configuration);
builder.AddCustomValidationKey(configuration);
builder.AddExtensionGrantValidator();
@@ -398,7 +403,7 @@ private static void AddExternalProviders(AuthenticationBuilder authenticationBui
options.Instance = externalProviderConfiguration.AzureInstance;
options.Domain = externalProviderConfiguration.AzureDomain;
options.CallbackPath = externalProviderConfiguration.AzureAdCallbackPath;
- }, cookieScheme: null);
+ }, cookieScheme: null);
}
}
@@ -409,7 +414,8 @@ private static void AddExternalProviders(AuthenticationBuilder authenticationBui
public static void UseMvcLocalizationServices(this IApplicationBuilder app)
{
var options = app.ApplicationServices.GetService>();
- app.UseRequestLocalization(options.Value);
+ if (options != null)
+ app.UseRequestLocalization(options.Value);
}
///
diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj b/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj
index 6c18c37b9..3e72cd222 100644
--- a/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj
+++ b/src/Skoruba.IdentityServer4.STS.Identity/Skoruba.IdentityServer4.STS.Identity.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs
index 38fb4ece0..3ba30522a 100644
--- a/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs
+++ b/src/Skoruba.IdentityServer4.STS.Identity/Startup.cs
@@ -100,7 +100,7 @@ public virtual void RegisterDbContexts(IServiceCollection services)
public virtual void RegisterAuthentication(IServiceCollection services)
{
services.AddAuthenticationServices(Configuration);
- services.AddIdentityServer(Configuration);
+ services.AddIdentityServer(Environment, Configuration);
}
public virtual void RegisterAuthorization(IServiceCollection services)
diff --git a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json
index d598dc54b..9c5339647 100644
--- a/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json
+++ b/src/Skoruba.IdentityServer4.STS.Identity/appsettings.json
@@ -18,9 +18,12 @@
"UseSigningCertificateThumbprint": false,
"SigningCertificateThumbprint": "",
- "UseSigningCertificatePfxFile": false,
- "SigningCertificatePfxFilePath": "",
- "SigningCertificatePfxFilePassword": "",
+ "DnsName": "localhost",
+ "UseSigningCertificatePfxFile": true,
+ "SigningCertificatePfxFilePath": "certificate.pfx",
+ "SigningCertificatePfxFilePassword": "JrUZVzv05ZaYkSh%I)u",
+ "ValidityPeriodInYear": 5,
+ "NeedToCreateNewOne": false,
"UseValidationCertificatePfxFile": false,
"ValidationCertificatePfxFilePath": "",
diff --git a/src/Skoruba.IdentityServer4.Shared.Configuration/Configuration/Common/CertificateConfiguration.cs b/src/Skoruba.IdentityServer4.Shared.Configuration/Configuration/Common/CertificateConfiguration.cs
index 7a7df65fc..40f14698e 100644
--- a/src/Skoruba.IdentityServer4.Shared.Configuration/Configuration/Common/CertificateConfiguration.cs
+++ b/src/Skoruba.IdentityServer4.Shared.Configuration/Configuration/Common/CertificateConfiguration.cs
@@ -11,12 +11,18 @@ public class CertificateConfiguration
public string SigningCertificateThumbprint { get; set; }
+ public string DnsName { get; set; }
+
public bool UseSigningCertificatePfxFile { get; set; }
public string SigningCertificatePfxFilePath { get; set; }
public string SigningCertificatePfxFilePassword { get; set; }
+ public int ValidityPeriodInYear { get; set; }
+
+ public bool NeedToCreateNewOne { get; set; }
+
public bool UseValidationCertificateThumbprint { get; set; }
public string ValidationCertificateThumbprint { get; set; }