示例#1
0
        /// <summary>
        /// Initializes a new instance
        /// </summary>
        /// <param name="ospIdentityOptions">The osp identity options.</param>
        /// <exception cref="System.ArgumentNullException">keys</exception>
        public SigningCredentialService(IOptions <OspIdentityOptions> ospIdentityOptions)
        {
            ArgumentValidation.Validate(nameof(ospIdentityOptions), ospIdentityOptions);

            ArgumentValidation.ValidateString(nameof(ospIdentityOptions.Value.KeyFilePath), ospIdentityOptions.Value.KeyFilePath);
            ArgumentValidation.ValidateString(nameof(ospIdentityOptions.Value.KeyFilePassword), ospIdentityOptions.Value.KeyFilePassword);

            if (File.Exists(ospIdentityOptions.Value.KeyFilePath))
            {
                Logger.Debug($"SigninCredentialExtension adding key from file {ospIdentityOptions.Value.KeyFilePath}");

                var certificate = new X509Certificate2(ospIdentityOptions.Value.KeyFilePath, ospIdentityOptions.Value.KeyFilePassword);
                _credential = new SigningCredentials(new X509SecurityKey(certificate), SecurityAlgorithms.RsaSha256);

                var keyInfo = new SecurityKeyInfo
                {
                    Key = _credential.Key,
                    SigningAlgorithm = _credential.Algorithm
                };
                _keys = new[] { keyInfo };
            }
            else
            {
                Logger.Error($"SigninCredentialExtension cannot find key file {ospIdentityOptions.Value.KeyFilePath}");
            }
        }
示例#2
0
        /// <summary>
        /// Sets the signing credential.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="credential">The credential.</param>
        /// <returns></returns>
        public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, SigningCredentials credential)
        {
            if (!(credential.Key is AsymmetricSecurityKey ||
                  credential.Key is IdentityModel.Tokens.JsonWebKey && ((IdentityModel.Tokens.JsonWebKey)credential.Key).HasPrivateKey))
            {
                throw new InvalidOperationException("Signing key is not asymmetric");
            }

            if (!IdentityServerConstants.SupportedSigningAlgorithms.Contains(credential.Algorithm, StringComparer.Ordinal))
            {
                throw new InvalidOperationException($"Signing algorithm {credential.Algorithm} is not supported.");
            }

            if (credential.Key is ECDsaSecurityKey key && !CryptoHelper.IsValidCurveForAlgorithm(key, credential.Algorithm))
            {
                throw new InvalidOperationException("Invalid curve for signing algorithm");
            }

            builder.Services.AddSingleton <ISigningCredentialStore>(new InMemorySigningCredentialsStore(credential));

            var keyInfo = new SecurityKeyInfo
            {
                Key = credential.Key,
                SigningAlgorithm = credential.Algorithm
            };

            builder.Services.AddSingleton <IValidationKeysStore>(new InMemoryValidationKeysStore(new[] { keyInfo }));

            return(builder);
        }
        private RsaJsonWebKeyPair(string privateKeyXml, DateTime?createdTime = null)
        {
            CreatedTime = createdTime ?? DateTime.Now;

            _privateKeyXml = privateKeyXml;

            var rsaPrivateKey = RSA.Create();

            rsaPrivateKey.FromXmlString(privateKeyXml);

            var rsaPublicKey = RSA.Create();

            rsaPublicKey.FromXmlString(rsaPrivateKey.ToXmlString(false));

            var privateRsaSecurityKey = new RsaSecurityKey(rsaPrivateKey);

            PrivateKey = JsonWebKeyConverter.ConvertFromRSASecurityKey(privateRsaSecurityKey);
            PublicKey  = JsonWebKeyConverter.ConvertFromRSASecurityKey(new RsaSecurityKey(rsaPublicKey));

            SigningCredentials = new SigningCredentials(privateRsaSecurityKey, SecurityAlgorithms.RsaSha256Signature);
            SecurityKeyInfo    = new SecurityKeyInfo
            {
                Key = privateRsaSecurityKey,
                SigningAlgorithm = PrivateKey.Alg
            };
        }
示例#4
0
        public static TokenValidator CreateTokenValidator(
            IReferenceTokenStore store           = null,
            IRefreshTokenStore refreshTokenStore = null,
            IProfileService profile       = null,
            IdentityServerOptions options = null, ISystemClock clock = null)
        {
            if (options == null)
            {
                options = TestIdentityServerOptions.Create();
            }

            if (profile == null)
            {
                profile = new TestProfileService();
            }

            if (store == null)
            {
                store = CreateReferenceTokenStore();
            }

            clock = clock ?? new StubClock();

            if (refreshTokenStore == null)
            {
                refreshTokenStore = CreateRefreshTokenStore();
            }

            var clients = CreateClientStore();
            var context = new MockHttpContextAccessor(options);
            var logger  = TestLogger.Create <TokenValidator>();

            var keyInfo = new SecurityKeyInfo
            {
                Key = TestCert.LoadSigningCredentials().Key,
                SigningAlgorithm = "RS256"
            };

            var validator = new TokenValidator(
                clients: clients,
                clock: clock,
                profile: profile,
                referenceTokenStore: store,
                refreshTokenStore: refreshTokenStore,
                customValidator: new DefaultCustomTokenValidator(),
                keys: new DefaultKeyMaterialService(
                    new[] { new InMemoryValidationKeysStore(new[] { keyInfo }) },
                    Enumerable.Empty <ISigningCredentialStore>(),
                    new NopAutomaticKeyManagerKeyStore()
                    ),
                logger: logger,
                options: options,
                context: context);

            return(validator);
        }
    public Task <IEnumerable <SecurityKeyInfo> > GetValidationKeysAsync()
    {
        var credential = Load();
        var keyInfo    = new SecurityKeyInfo
        {
            Key = credential.Key,
            SigningAlgorithm = credential.Algorithm
        };
        var res = (IEnumerable <SecurityKeyInfo>) new[] { keyInfo };

        return(Task.FromResult(res));
    }
示例#6
0
        /// <summary>
        /// Adds an ECDSA-based validation key.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="key">The ECDSA key</param>
        /// <param name="signingAlgorithm">The ECDSA-based signing algorithm</param>
        /// <returns></returns>
        public static IIdentityServerBuilder AddValidationKey(
            this IIdentityServerBuilder builder,
            ECDsaSecurityKey key,
            IdentityServerConstants.ECDsaSigningAlgorithm signingAlgorithm = IdentityServerConstants.ECDsaSigningAlgorithm.ES256)
        {
            var keyInfo = new SecurityKeyInfo
            {
                Key = key,
                SigningAlgorithm = CryptoHelper.GetECDsaSigningAlgorithmValue(signingAlgorithm)
            };

            return(builder.AddValidationKey(keyInfo));
        }
示例#7
0
        internal async Task <SecurityKeyInfo> GetSecurityKeyFromCertificateAsync(Microsoft.Azure.KeyVault.Models.CertificateItem certificateItem)
        {
            var certificateVersionBundle = await _keyVaultClient.GetCertificateAsync(certificateItem.Identifier.Identifier);

            var certificatePrivateKeySecretBundle = await _keyVaultClient.GetSecretAsync(certificateVersionBundle.SecretIdentifier.Identifier);

            var privateKeyBytes           = Convert.FromBase64String(certificatePrivateKeySecretBundle.Value);
            var certificateWithPrivateKey = new X509Certificate2(privateKeyBytes, (string)null, X509KeyStorageFlags.MachineKeySet);
            var tp = new SecurityKeyInfo();

            //certificateWithPrivateKey.SignatureAlgorithm
            tp.Key = new X509SecurityKey(certificateWithPrivateKey);
            tp.SigningAlgorithm = SecurityAlgorithms.RsaSha256;
            return(tp);
        }
示例#8
0
        /// <summary>
        /// Adds the validation key.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="certificate">The certificate.</param>
        /// <param name="signingAlgorithm">The signing algorithm</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IIdentityServerBuilder AddValidationKey(
            this IIdentityServerBuilder builder,
            X509Certificate2 certificate,
            string signingAlgorithm = SecurityAlgorithms.RsaSha256)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }

            var keyInfo = new SecurityKeyInfo
            {
                Key = new X509SecurityKey(certificate),
                SigningAlgorithm = signingAlgorithm
            };

            return(builder.AddValidationKey(keyInfo));
        }
示例#9
0
    public static TokenValidator CreateTokenValidator(
        IReferenceTokenStore store           = null,
        IRefreshTokenStore refreshTokenStore = null,
        IProfileService profile = null,
        IIssuerNameService issuerNameService = null,
        IdentityServerOptions options        = null,
        ISystemClock clock = null)
    {
        options ??= TestIdentityServerOptions.Create();
        profile ??= new TestProfileService();
        store ??= CreateReferenceTokenStore();
        clock ??= new StubClock();
        refreshTokenStore ??= CreateRefreshTokenStore();
        issuerNameService ??= new TestIssuerNameService(options.IssuerUri);

        var clients = CreateClientStore();

        var logger = TestLogger.Create <TokenValidator>();

        var keyInfo = new SecurityKeyInfo
        {
            Key = TestCert.LoadSigningCredentials().Key,
            SigningAlgorithm = "RS256"
        };

        var validator = new TokenValidator(
            clients: clients,
            clock: clock,
            profile: profile,
            referenceTokenStore: store,
            customValidator: new DefaultCustomTokenValidator(),
            keys: new DefaultKeyMaterialService(
                new[] { new InMemoryValidationKeysStore(new[] { keyInfo }) },
                Enumerable.Empty <ISigningCredentialStore>(),
                new NopAutomaticKeyManagerKeyStore()
                ),
            sessionCoordinationService: new StubSessionCoordinationService(),
            logger: logger,
            options: options,
            issuerNameService: issuerNameService);

        return(validator);
    }
示例#10
0
        /// <summary>
        /// Adds the validation key.
        /// </summary>
        /// <param name="builder">The builder.</param>
        /// <param name="certificate">The certificate.</param>
        /// <param name="signingAlgorithm">The signing algorithm</param>
        /// <returns></returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static IIdentityServerBuilder AddValidationKey(
            this IIdentityServerBuilder builder,
            X509Certificate2 certificate,
            string signingAlgorithm = SecurityAlgorithms.RsaSha256)
        {
            if (certificate == null)
            {
                throw new ArgumentNullException(nameof(certificate));
            }

            // add signing algorithm name to key ID to allow using the same key for two different algorithms (e.g. RS256 and PS56);
            var key = new X509SecurityKey(certificate);

            key.KeyId += signingAlgorithm;

            var keyInfo = new SecurityKeyInfo
            {
                Key = key,
                SigningAlgorithm = signingAlgorithm
            };

            return(builder.AddValidationKey(keyInfo));
        }
        private async Task <IEnumerable <KeyInfoContainer> > GetKeyInfoContainersAsync()
        {
            var utcNow = DateTime.UtcNow;

            var tenantName = _scopedTenantRequestContext.Context.TenantName;
            var cacheKey   = $"{tenantName}-GetKeyInfoContainersAsync";

            TimedLock.LockReleaser releaser = await _lockGetKeyInfoContainersAsync.Lock(new TimeSpan(0, 0, 30));

            try
            {
                var cacheEntry = await _memoryCache.GetOrCreate(cacheKey, async entry =>
                {
                    /*
                     * There is a 60 month overlap, so we show the previous key infos for 3 months to give
                     * downstream validators time to catchup and pull the new public certs.
                     */
                    entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1);

                    var certificates = await _adminServices.GetAllCertificatesAsync(
                        tenantName, "ECDsa",
                        utcNow.AddMonths(-9), // 3 month showing for keys we don't use anymore
                        utcNow.AddMonths(12)  // here we pick up keys we are going to use in the future.
                        );

                    /*
                     * typically you should see 3 key infos in the JWKS discovery.
                     * 1. The one we stopped using
                     * 2. the one we are using
                     * 3. the one we are going to use in the future.
                     */

                    List <KeyInfoContainer> keys = new List <KeyInfoContainer>();
                    foreach (var cert in certificates)
                    {
                        byte[] ecdsaCertPfxBytes = Convert.FromBase64String(cert.PFXBase64);
                        var ecdsaCertificate     = new X509Certificate2(ecdsaCertPfxBytes, _selfManagedCertificatesOptions.Password);
                        ECDsaSecurityKey ecdsaCertificatePublicKey =
                            new ECDsaSecurityKey(ecdsaCertificate.GetECDsaPrivateKey());
                        ecdsaCertificatePublicKey.KeyId = cert.PFXBase64.Sha256();
                        var credential = new SigningCredentials(ecdsaCertificatePublicKey,
                                                                GetECDsaSigningAlgorithmValue(IdentityServerConstants.ECDsaSigningAlgorithm.ES256));

                        var keyInfo = new SecurityKeyInfo
                        {
                            Key = credential.Key,
                            SigningAlgorithm = credential.Algorithm
                        };
                        keys.Add(new KeyInfoContainer
                        {
                            Expiration         = cert.Expiration,
                            NotBefore          = cert.NotBefore,
                            SecurityKeyInfo    = keyInfo,
                            SigningCredentials = credential
                        });
                    }

                    return(keys);
                });

                return(cacheEntry);
            }
            finally
            {
                releaser.Dispose();
            }
        }
        /// <summary>
        /// Add Signing credential by file
        /// </summary>
        /// <param name="builder">IIdentityServerBuilder</param>
        /// <param name="appSettings">AppSettings</param>
        /// <returns>IIdentityServerBuilder</returns>
        public static IIdentityServerBuilder AddSigningCredentialsByFile(
            this IIdentityServerBuilder builder, AppSettings appSettings)
        {
            // Variables
            const int    DEFAULT_EXPIRY_YEAR     = 1;
            const string DIR_NAME_KEYS           = "Keys";
            const string FILE_NAME_WORKING_SC    = "SigningCredential.rsa";
            const string FILE_NAME_DEPRECATED_SC = "DeprecatedSigningCredentials.rsa";
            var          rootDir         = Path.Combine(AppContext.BaseDirectory, DIR_NAME_KEYS);
            var          workingScDir    = Path.Combine(rootDir, FILE_NAME_WORKING_SC);
            var          deprecatedScDir = Path.Combine(rootDir, FILE_NAME_DEPRECATED_SC);
            var          utcNow          = DateTimeOffset.UtcNow;

            // RSA key object
            Microsoft.IdentityModel.Tokens.RsaSecurityKey key = null; // The Key for Idsrv

            // Signing credetial object
            SigningCredential        credential            = null; // The Signing credential stored in file
            List <SigningCredential> deprecatedCredentials = null; // The Deprecated Signing credentials stored in file

            #region Add exist or new Signing Credentials

            var strWorkingSc     = FileUtils.ReadFileAsync(workingScDir).Result;
            var strDeprecatedScs = FileUtils.ReadFileAsync(deprecatedScDir).Result;
            if (!string.IsNullOrEmpty(strWorkingSc))
            {
                // Use the RSA key pair stored in file
                credential = JsonConvert.DeserializeObject <SigningCredential>(strWorkingSc);
                key        = CryptoHelper.CreateRsaSecurityKey(credential.Parameters, credential.KeyId);

                // Warning if key expires
                if (credential.ExpireOn < utcNow)
                {
                    logger.Warn($"Auth Server's Signing Credential (ID: {credential.KeyId}) is expired at {credential.ExpireOn.ToLocalTime()}!");
                }
            }
            else
            {
                // Create new RSA key pair
                key = CryptoHelper.CreateRsaSecurityKey();

                RSAParameters parameters = key.Rsa == null ?
                                           parameters = key.Parameters :
                                                        key.Rsa.ExportParameters(includePrivateParameters: true);

                var expireOn = appSettings.Global?.SigningCredential?.DefaultExpiry == null?
                               utcNow.AddYears(DEFAULT_EXPIRY_YEAR) :
                                   appSettings.Global.SigningCredential.DefaultExpiry.GetExpireOn();

                credential = new SigningCredential
                {
                    Parameters = parameters,
                    KeyId      = key.KeyId,
                    ExpireOn   = expireOn
                };

                // Save to fiile
                FileUtils.SaveFileAsync(workingScDir, JsonConvert.SerializeObject(credential)).Wait();
            }

            // Add the key as the Signing credential for Idsrv
            builder.AddSigningCredential(key, IdentityServerConstants.RsaSigningAlgorithm.RS256);
            #endregion

            #region Add Deprecated Signing Credentials for clients' old tokens

            deprecatedCredentials = string.IsNullOrEmpty(strDeprecatedScs) ? new List <SigningCredential>() : JsonConvert.DeserializeObject <List <SigningCredential> >(strDeprecatedScs);

            IList <SecurityKeyInfo> deprecatedKeyInfos = new List <SecurityKeyInfo>();
            deprecatedCredentials.ForEach(dc =>
            {
                var deprecatedKeyInfo = new SecurityKeyInfo
                {
                    Key = CryptoHelper.CreateRsaSecurityKey(dc.Parameters, dc.KeyId),
                    SigningAlgorithm = SecurityAlgorithms.RsaSha256
                };
                deprecatedKeyInfos.Add(deprecatedKeyInfo);

                // builder.AddValidationKey(deprecatedKey, IdentityServerConstants.RsaSigningAlgorithm.RS256));
            });

            builder.AddValidationKey(deprecatedKeyInfos.ToArray());

            #endregion

            return(builder);
        }
        /// <summary>
        /// Add Signing credential by Redis
        /// </summary>
        /// <param name="builder">IIdentityServerBuilder</param>
        /// <param name="appSettings">AppSettings</param>
        /// <returns>IIdentityServerBuilder</returns>
        public static IIdentityServerBuilder AddSigningCredentialByRedis(
            this IIdentityServerBuilder builder, AppSettings appSettings)
        {
            // Variables
            const int DEFAULT_EXPIRY_YEAR = 1;
            var       utcNow               = DateTimeOffset.UtcNow;
            var       redisKeyWorkingSk    = CacheKeyFactory.SigningCredential();
            var       redisKeyDeprecatedSk = CacheKeyFactory.SigningCredential(isDeprecated: true);

            // RSA key object
            Microsoft.IdentityModel.Tokens.RsaSecurityKey key = null; // The Key for Idsrv

            // Signing credetial object from Redis
            SigningCredential        credential            = null; // The Signing credential stored in Redis
            List <SigningCredential> deprecatedCredentials = null; // The Deprecated Signing credentials stored in Redis

            using (ICacheService redis = new RedisService(appSettings))
            {
                bool isSigningCredentialExists = redis.GetCache(redisKeyWorkingSk, out credential);

                if (isSigningCredentialExists && credential.ExpireOn >= utcNow)
                {
                    // Use the RSA key pair stored in redis
                    key = CryptoHelper.CreateRsaSecurityKey(credential.Parameters, credential.KeyId);
                }
                else if (isSigningCredentialExists && credential.ExpireOn < utcNow)
                {
                    #region Move the expired Signing credential to Redis's Decprecated-Signing-Credential key

                    _ = redis.GetCache(redisKeyDeprecatedSk, out deprecatedCredentials);

                    if (deprecatedCredentials == null)
                    {
                        deprecatedCredentials = new List <SigningCredential>();
                    }

                    deprecatedCredentials.Add(credential);

                    redis.SaveCache(redisKeyDeprecatedSk, deprecatedCredentials);
                    #endregion

                    #region Clear the expired Signing credential from Redis's Signing-Credential key

                    redis.ClearCache(redisKeyWorkingSk);
                    #endregion

                    // Set flag as False
                    isSigningCredentialExists = false;
                }

                #region If no available Signing credial, create a new one

                if (!isSigningCredentialExists)
                {
                    key = CryptoHelper.CreateRsaSecurityKey();

                    RSAParameters parameters = key.Rsa == null ?
                                               parameters = key.Parameters :
                                                            key.Rsa.ExportParameters(includePrivateParameters: true);

                    var expireOn = appSettings.Global?.SigningCredential?.DefaultExpiry == null?
                                   utcNow.AddYears(DEFAULT_EXPIRY_YEAR) :
                                       appSettings.Global.SigningCredential.DefaultExpiry.GetExpireOn();

                    credential = new SigningCredential
                    {
                        Parameters = parameters,
                        KeyId      = key.KeyId,
                        ExpireOn   = expireOn
                    };

                    // Save to Redis
                    redis.SaveCache(redisKeyWorkingSk, credential);
                }
                #endregion

                // Add the key as the Signing credential for Idsrv
                builder.AddSigningCredential(key, IdentityServerConstants.RsaSigningAlgorithm.RS256);

                // Also add the expired key for clients' old tokens
                if (redis.GetCache(redisKeyDeprecatedSk, out deprecatedCredentials))
                {
                    IList <SecurityKeyInfo> deprecatedKeyInfos = new List <SecurityKeyInfo>();
                    deprecatedCredentials.ForEach(dc =>
                    {
                        var deprecatedKeyInfo = new SecurityKeyInfo
                        {
                            Key = CryptoHelper.CreateRsaSecurityKey(dc.Parameters, dc.KeyId),
                            SigningAlgorithm = SecurityAlgorithms.RsaSha256
                        };
                        deprecatedKeyInfos.Add(deprecatedKeyInfo);

                        // builder.AddValidationKey(deprecatedKey, IdentityServerConstants.RsaSigningAlgorithm.RS256));
                    });

                    builder.AddValidationKey(deprecatedKeyInfos.ToArray());
                }
            }

            return(builder);
        }