AddSigningCredential for IdentityServer4

27,907

Solution 1

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());

Solution 2

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.....
}

Solution 3

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"),
};
Share:
27,907

Related videos on Youtube

Rakesh Kumar
Author by

Rakesh Kumar

C#/.NET Core Developer passionate about cloud solutions

Updated on July 09, 2022

Comments

  • Rakesh Kumar
    Rakesh Kumar almost 2 years

    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());
    }
    
    • aaronR
      aaronR about 6 years
      Are you on a PaaS service in Azure or AWS?
  • Rakesh Kumar
    Rakesh Kumar about 6 years
    Hi @mackie, actually now we are not going to use X509 certificates, so we need to use something else in place of X509 certificates.
  • Rakesh Kumar
    Rakesh Kumar about 6 years
    Could you please provide any example how we can use signing credentials instead of x509 certificates?
  • mackie
    mackie about 6 years
    Edited my answer above. Why not use self generated X509 certs?
  • Scott Brady
    Scott Brady about 6 years
    "now we are not going to use X509 certificates" - why?
  • Rakesh Kumar
    Rakesh Kumar about 6 years
    actually we facing some issues, when deploying the identity server as a serverless lambda on aws environment. so that we changed the core plan from x509 to signing certificates.
  • Anton Toshik
    Anton Toshik over 5 years
    Could you explain what role the "Pass Phrase" plays?
  • aaronR
    aaronR over 5 years
    The pass phrase the secret to the certificate.
  • Fred
    Fred over 5 years
    Do not put your private certificate file in the WebRootPath this will make your private key readable to the whole internet.
  • Astravagrant
    Astravagrant over 5 years
    @Fred Only if you choose to serve that folder. This isn't the default behaviour of .Net projects (from provided templates).
  • Fred
    Fred over 5 years
    @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.
  • Astravagrant
    Astravagrant over 5 years
    Sorry, @Fred, you're absolutely right! I'm using ContentRootPath and was too caffeinated to spot the difference.
  • Junaid
    Junaid over 3 years
    So, is storing it in Content Root Path (project folder) safe?
  • aaronR
    aaronR about 3 years
    @Junaid not for production.
  • Konrad Viltersten
    Konrad Viltersten over 2 years
    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.