26

We are using IdentityServer4 with .NET Core Web Application("http://docs.identityserver.io/en/release/quickstarts/0_overview.html"). We have replaced AddDeveloperSigningCredential with AddSigningCredential(CreateSigningCredential()). As we cannot use AddDeveloperSigningCredential for production environment because on production needs to be replaced by some persistent key material. We are new to IdentityServer4 and our question is that, Is following approach fine to create signing credentials on production environment? Or do we need to made some changes in this?

Here is our startup.cs file:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<IConfiguration>(Configuration);

    //connection string
    string connectionString = Configuration.GetConnectionString("IdentityServer");

    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;

    services.AddIdentityServer().AddDeveloperSigningCredential
    .AddSigningCredential(CreateSigningCredential())
    // this adds the config data from DB (clients, resources)
    .AddConfigurationStore(options =>
    {
        options.ConfigureDbContext = builder =>
        builder.UseSqlServer(connectionString,
            sql => sql.MigrationsAssembly(migrationsAssembly));
                }) // this adds the operational data from DB (codes, tokens, consents)
    .AddOperationalStore(options =>
    {
        options.ConfigureDbContext = builder =>
        builder.UseSqlServer(connectionString,
            sql => sql.MigrationsAssembly(migrationsAssembly));

        // this enables automatic token cleanup. this is optional.
        options.EnableTokenCleanup = true;
        options.TokenCleanupInterval = 30;
        });
}

private SigningCredentials CreateSigningCredential()
{
    var credentials = new SigningCredentials(GetSecurityKey(), SecurityAlgorithms.RsaSha256Signature);

    return credentials;
}
private RSACryptoServiceProvider GetRSACryptoServiceProvider()
{
    return new RSACryptoServiceProvider(2048);
}
private SecurityKey GetSecurityKey()
{
    return new RsaSecurityKey(GetRSACryptoServiceProvider());
}
Askolein
  • 3,250
  • 3
  • 28
  • 40
Rakesh Kumar
  • 2,701
  • 9
  • 38
  • 66

3 Answers3

11

Here is a gist that should help for Ids4 with asp.net core 2.x.

It contains an RsaKeyService class that can be injected into the service provider like:

var rsa = new RsaKeyService(Environment, TimeSpan.FromDays(30));
services.AddTransient<RsaKeyService>(provider => rsa);

This makes sure, that an RSA key is used for 30 days at most, before a new one is re-generated.

To use the key, you can call rsa.GetKey(), and to register as a signing credential, use:

builder.AddSigningCredential(rsa.GetKey());
mykeels
  • 590
  • 5
  • 9
9

Here is a simple way of using the X509 self-signed certificate.

One way to use a self-signed certificate to use for token signing with IdentityServer4 is to store the certificate with the application under the 'wwwroot' folder.

public void ConfigureServices(IServiceCollection services)
{
        .....other code .....

        var fileName = Path.Combine(env.WebRootPath, "YOUR_FileName" );            

        if (!File.Exists(fileName))
        {
            throw new FileNotFoundException("Signing Certificate is missing!");
        }

        var cert = new X509Certificate2(fileName, "Your_PassPhrase" );

        services.AddIdentityServer().AddSigningCredential(cert)

        ...other code.....
}
aaronR
  • 1,557
  • 2
  • 16
  • 26
  • 1
    Could you explain what role the "Pass Phrase" plays? – Anton Toshik Oct 12 '18 at 06:56
  • The pass phrase the secret to the certificate. – aaronR Oct 12 '18 at 20:25
  • 14
    Do not put your private certificate file in the `WebRootPath` this will make your private key readable to the whole internet. – Fred Nov 26 '18 at 13:45
  • @Fred Only if you choose to serve that folder. This isn't the default behaviour of .Net projects (from provided templates). – Astravagrant Dec 18 '18 at 16:13
  • 8
    @Astravagrant I think `WebRootPath` is always served in the default configuration if the StaticFiles middleware is used. I think `ContentRootPath` should be used instead of `WebRootPath`. – Fred Dec 19 '18 at 09:03
  • 3
    Sorry, @Fred, you're absolutely right! I'm using ContentRootPath and was too caffeinated to spot the difference. – Astravagrant Dec 19 '18 at 14:32
  • So, is storing it in Content Root Path (project folder) safe? – Junaid Jan 08 '21 at 15:16
  • 1
    @Junaid not for production. – aaronR Mar 18 '21 at 21:15
  • Suppose that I want to keep it stupid simple and instead of a file I'm fine passing fixed strings that do never change (yes, **stupid** was the word but it's for laboratory purposes). How would I go about it with certificate generation then? Is it possible to begin with? I don't even want to try to pass `new X509Certificate2()` with an empty constructor because I'm sure it's going to cause more confusion than clarity. – Konrad Viltersten Oct 02 '21 at 14:28
2

I don't see anything persistent being loaded here so I'd have to say no, this is not suitable. I provided an example of loading a certificate here:

How we can replace AddDeveloperSigningCredential on AWS Serverless Lambda environment?

I suggest following that approach. You can deploy the certificate in the OS cert store, as a file or as an embedded resource within the app itself.

ETA: Since you've said that X509 certs are off the table (interested to know why) then you'd need to provide the RSAParameters to RsaSecurityKey yourself.

See here for the test data used in the Microsoft.IdentityModel.Tokens library:

https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/d771b5c3ef22b7ff065e8fad1a63d6a2937b7d7f/test/Microsoft.IdentityModel.Tests/KeyingMaterial.cs

E.g.

RsaParameters_2048 = new RSAParameters
{
        D = Base64UrlEncoder.DecodeBytes("C6EGZYf9U6RI5Z0BBoSlwy_gKumVqRx-dBMuAfPM6KVbwIUuSJKT3ExeL5P0Ky1b4p-j2S3u7Afnvrrj4HgVLnC1ks6rEOc2ne5DYQq8szST9FMutyulcsNUKLOM5cVromALPz3PAqE2OCLChTiQZ5XZ0AiH-KcG-3hKMa-g1MVnGW-SSmm27XQwRtUtFQFfxDuL0E0fyA9O9ZFBV5201ledBaLdDcPBF8cHC53Gm5G6FRX3QVpoewm3yGk28Wze_YvNl8U3hvbxei2Koc_b9wMbFxvHseLQrxvFg_2byE2em8FrxJstxgN7qhMsYcAyw1qGJY-cYX-Ab_1bBCpdcQ"),
        DP = Base64UrlEncoder.DecodeBytes("ErP3OpudePAY3uGFSoF16Sde69PnOra62jDEZGnPx_v3nPNpA5sr-tNc8bQP074yQl5kzSFRjRlstyW0TpBVMP0ocbD8RsN4EKsgJ1jvaSIEoP87OxduGkim49wFA0Qxf_NyrcYUnz6XSidY3lC_pF4JDJXg5bP_x0MUkQCTtQE"),
        DQ = Base64UrlEncoder.DecodeBytes("YbBsthPt15Pshb8rN8omyfy9D7-m4AGcKzqPERWuX8bORNyhQ5M8JtdXcu8UmTez0j188cNMJgkiN07nYLIzNT3Wg822nhtJaoKVwZWnS2ipoFlgrBgmQiKcGU43lfB5e3qVVYUebYY0zRGBM1Fzetd6Yertl5Ae2g2CakQAcPs"),
        Exponent = Base64UrlEncoder.DecodeBytes("AQAB"),
        InverseQ = Base64UrlEncoder.DecodeBytes("lbljWyVY-DD_Zuii2ifAz0jrHTMvN-YS9l_zyYyA_Scnalw23fQf5WIcZibxJJll5H0kNTIk8SCxyPzNShKGKjgpyZHsJBKgL3iAgmnwk6k8zrb_lqa0sd1QWSB-Rqiw7AqVqvNUdnIqhm-v3R8tYrxzAqkUsGcFbQYj4M5_F_4"),
        Modulus = Base64UrlEncoder.DecodeBytes("6-FrFkt_TByQ_L5d7or-9PVAowpswxUe3dJeYFTY0Lgq7zKI5OQ5RnSrI0T9yrfnRzE9oOdd4zmVj9txVLI-yySvinAu3yQDQou2Ga42ML_-K4Jrd5clMUPRGMbXdV5Rl9zzB0s2JoZJedua5dwoQw0GkS5Z8YAXBEzULrup06fnB5n6x5r2y1C_8Ebp5cyE4Bjs7W68rUlyIlx1lzYvakxSnhUxSsjx7u_mIdywyGfgiT3tw0FsWvki_KYurAPR1BSMXhCzzZTkMWKE8IaLkhauw5MdxojxyBVuNY-J_elq-HgJ_dZK6g7vMNvXz2_vT-SykIkzwiD9eSI9UWfsjw"),
        P = Base64UrlEncoder.DecodeBytes("_avCCyuo7hHlqu9Ec6R47ub_Ul_zNiS-xvkkuYwW-4lNnI66A5zMm_BOQVMnaCkBua1OmOgx7e63-jHFvG5lyrhyYEmkA2CS3kMCrI-dx0fvNMLEXInPxd4np_7GUd1_XzPZEkPxBhqf09kqryHMj_uf7UtPcrJNvFY-GNrzlJk"),
        Q = Base64UrlEncoder.DecodeBytes("7gvYRkpqM-SC883KImmy66eLiUrGE6G6_7Y8BS9oD4HhXcZ4rW6JJKuBzm7FlnsVhVGro9M-QQ_GSLaDoxOPQfHQq62ERt-y_lCzSsMeWHbqOMci_pbtvJknpMv4ifsQXKJ4Lnk_AlGr-5r5JR5rUHgPFzCk9dJt69ff3QhzG2c"),
};
mackie
  • 4,996
  • 1
  • 17
  • 17