/// <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}"); } }
/// <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 }; }
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)); }
/// <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)); }
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); }
/// <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)); }
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); }
/// <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); }