public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { //parsedSecret. //IdentityServer4.Validation.ClientSecretValidator var result = new SecretValidationResult { Success = false }; return(Task.FromResult(result)); }
public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { _logger.LogDebug("Validate client assertion started"); var fail = new SecretValidationResult { Success = false }; if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { _logger.LogDebug($"Secret type is not a JwtBearer, instead it was {parsedSecret.Type}"); return(fail); } if (!(parsedSecret.Credential is string jwtTokenString)) { _logger.LogDebug("Secret is not like a JWT string"); return(fail); } return(await _assertionManager.ValidateAsync(jwtTokenString)); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentNullException">Id or credential</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret) { _logger.LogDebug("Hashed shared secret validator cannot process {type}", parsedSecret.Type ?? "null"); return(fail); } var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret); if (!sharedSecrets.Any()) { _logger.LogDebug("No shared secret configured for client."); return(fail); } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } var secretSha256 = sharedSecret.Sha256(); var secretSha512 = sharedSecret.Sha512(); foreach (var secret in sharedSecrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; bool isValid = false; byte[] secretBytes; try { secretBytes = Convert.FromBase64String(secret.Value); } catch (FormatException) { _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription); return(fail); } catch (ArgumentNullException) { _logger.LogInformation("Secret: {description} is null.", secretDescription); return(fail); } if (secretBytes.Length == 32) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256); } else if (secretBytes.Length == 64) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512); } else { _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription); return(fail); } if (isValid) { return(success); } } _logger.LogDebug("No matching hashed secret found."); return(fail); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not a JWT token</exception> public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = new SecretValidationResult { Success = false }; var success = new SecretValidationResult { Success = true }; if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { return(fail); } if (!(parsedSecret.Credential is string jwtTokenString)) { _logger.LogError("ParsedSecret.Credential is not a string."); return(fail); } List <SecurityKey> trustedKeys; try { trustedKeys = await secrets.GetKeysAsync(); } catch (Exception e) { _logger.LogError(e, "Could not parse secrets"); return(fail); } if (!trustedKeys.Any()) { _logger.LogError("There are no keys available to validate client assertion."); return(fail); } var jwtToken = new JwtSecurityToken(jwtTokenString); // OZ // var x5c = jwtToken.Payload.Claims.FirstOrDefault(c => c.Type == "x5c")?.Value; // string x5c = jwtToken.Header.GetValueOrDefault("x5c"); Console.WriteLine("Client Token:\n" + jwtTokenString + "\n"); object o; if (jwtToken.Header.TryGetValue("x5c", out o)) { // if (o is string) var s = o.GetType().ToString(); if (o is JArray) { // string x5c = o as string; string[] x5c = (o as JArray).ToObject <string[]>(); // if (x5c != null && x5c != "") if (x5c != null) { // Console.WriteLine("x5c:\n" + x5c); Console.WriteLine("Security 2.1a Server: x5c with certificate chain received"); // parsed = JObject.Parse(Jose.JWT.Payload(token)); // user = parsed.SelectToken("user").Value<string>(); // string user = jwtToken.Payload.Claims.FirstOrDefault(c => c.Type == "user")?.Value; X509Store storeCA = new X509Store("CA", StoreLocation.CurrentUser); storeCA.Open(OpenFlags.ReadWrite); bool valid = false; // string[] x5c64 = JsonConvert.DeserializeObject<string[]>(x5c); string[] x5c64 = x5c; X509Certificate2Collection xcc = new X509Certificate2Collection(); Byte[] certFileBytes = Convert.FromBase64String(x5c64[0]); /* * string fileCert = "./temp/" + user + ".cer"; * File.WriteAllBytes(fileCert, certFileBytes); * Console.WriteLine("Security 2.1b Server: " + fileCert + " received"); */ var x509 = new X509Certificate2(certFileBytes); xcc.Add(x509); Console.WriteLine("Security 2.1c Certificate in Chain: " + x509.Subject); StringBuilder builder = new StringBuilder(); builder.AppendLine("-----BEGIN CERTIFICATE-----"); builder.AppendLine( Convert.ToBase64String(x509.RawData, Base64FormattingOptions.InsertLineBreaks)); builder.AppendLine("-----END CERTIFICATE-----"); Console.WriteLine("Client Certificate: "); Console.WriteLine(builder); for (int i = 1; i < x5c64.Length; i++) { var cert = new X509Certificate2(Convert.FromBase64String(x5c64[i])); Console.WriteLine("Security 2.1c Certificate in Chain: " + cert.Subject); if (cert.Subject != cert.Issuer) { xcc.Add(cert); storeCA.Add(cert); } } X509Chain c = new X509Chain(); c.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck; valid = c.Build(x509); // storeCA.RemoveRange(xcc); Console.WriteLine("Security 2.1d Server: Validate chain with root cert"); if (!valid) { Console.WriteLine("ERROR: Certificate " + x509.Subject + " not valid!"); _logger.LogError("Certificate " + x509.Subject + " not valid!"); return(fail); } var xsk = new X509SecurityKey(x509); trustedKeys = new List <SecurityKey> { xsk }; } } } // OZ end var validAudiences = new[] { // issuer URI (tbd) //_contextAccessor.HttpContext.GetIdentityServerIssuerUri(), // token endpoint URL string.Concat(_contextAccessor.HttpContext.GetIdentityServerIssuerUri().EnsureTrailingSlash(), Constants.ProtocolRoutePaths.Token) }; var tokenValidationParameters = new TokenValidationParameters { IssuerSigningKeys = trustedKeys, ValidateIssuerSigningKey = true, ValidIssuer = parsedSecret.Id, ValidateIssuer = true, ValidAudiences = validAudiences, ValidateAudience = true, RequireSignedTokens = true, RequireExpirationTime = true, ClockSkew = TimeSpan.FromMinutes(5) }; try { var handler = new JwtSecurityTokenHandler(); handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token); jwtToken = (JwtSecurityToken)token; if (jwtToken.Subject != jwtToken.Issuer) { _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); return(fail); } var exp = jwtToken.Payload.Exp; if (!exp.HasValue) { _logger.LogError("exp is missing."); return(fail); } var jti = jwtToken.Payload.Jti; if (jti.IsMissing()) { _logger.LogError("jti is missing."); return(fail); } if (await _replayCache.ExistsAsync(Purpose, jti)) { _logger.LogError("jti is found in replay cache. Possible replay attack."); return(fail); } else { await _replayCache.AddAsync(Purpose, jti, DateTimeOffset.FromUnixTimeSeconds(exp.Value).AddMinutes(5)); } return(success); } catch (Exception e) { _logger.LogError(e, "JWT token validation error"); return(fail); } }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentNullException">Id or cedential</exception> public Task<SecretValidationResult> ValidateAsync(IEnumerable<Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != Constants.ParsedSecretTypes.SharedSecret) { Logger.Debug(string.Format("Parsed secret should not be of type {0}", parsedSecret.Type ?? "null")); return fail; } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } var secretSha256 = sharedSecret.Sha256(); var secretSha512 = sharedSecret.Sha512(); foreach (var secret in secrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; // this validator is only applicable to shared secrets if (secret.Type != Constants.SecretTypes.SharedSecret) { Logger.Debug(string.Format("Skipping secret: {0}, secret is not of type {1}.", secretDescription, Constants.SecretTypes.SharedSecret)); continue; } // check if client secret is still valid if (secret.Expiration.HasExpired()) { Logger.Debug(string.Format("Skipping secret: {0}, secret is expired.", secretDescription)); continue; } bool isValid = false; byte[] secretBytes; try { secretBytes = Convert.FromBase64String(secret.Value); } catch (FormatException) { Logger.Error(string.Format("Secret: {0} uses invalid hashing algorithm.", secretDescription)); return fail; } if (secretBytes.Length == 32) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256); } else if (secretBytes.Length == 64) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512); } else { Logger.Error(string.Format("Secret: {0} uses invalid hashing algorithm.", secretDescription)); return fail; } if (isValid) { return success; } } Logger.Debug("No matching hashed secret found."); return fail; }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentNullException">Id or cedential</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type == Constants.ParsedSecretTypes.SharedSecret) { var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentNullException("Id or cedential"); } var secretSha256 = sharedSecret.Sha256(); var secretSha512 = sharedSecret.Sha512(); foreach (var secret in secrets) { // this validator is only applicable to shared secrets if (secret.Type != Constants.SecretTypes.SharedSecret) { continue; } bool isValid = false; byte[] secretBytes; // check if client secret is still valid if (secret.Expiration.HasExpired()) { continue; } try { secretBytes = Convert.FromBase64String(secret.Value); } catch (FormatException) { Logger.Error("Secret uses invalid hashing algorithm"); return(fail); } if (secretBytes.Length == 32) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256); } else if (secretBytes.Length == 64) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512); } else { Logger.Error("Secret uses invalid hashing algorithm"); return(fail); } if (isValid) { return(success); } } } return(fail); }
/// <inheritdoc /> /// <summary> /// Validates the signed JWT. /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="T:System.ArgumentException">ParsedSecret.Credential is not a JWT token</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { return(fail); } if (!(parsedSecret.Credential is string token)) { _logger.LogError("ParsedSecret.Credential is not a string."); return(fail); } var handler = new JwtSecurityTokenHandler(); if (!handler.CanReadToken(token)) { _logger.LogError("ParsedSecret.Credential is not a well formed JWT."); return(fail); } // Collect the potential public keys from the client secrets var secretArray = secrets as Secret[] ?? secrets.ToArray(); var pemKeys = GetPemKeys(secretArray); if (!pemKeys.Any()) { _logger.LogError("There are no keys available to validate the client assertion."); return(fail); } var tokenValidationParameters = new TokenValidationParameters { // The token must be signed to prove the client credentials. RequireSignedTokens = true, RequireExpirationTime = true, IssuerSigningKeys = pemKeys, ValidateIssuerSigningKey = true, // IMS recommendation is to send any unique name as Issuer. The IMS reference // implementation sends the tool name. The tool's own name for this client // is not known by the platform and cannot be validated. ValidateIssuer = false, // IMS recommendation is to send the base url of the authentication server // or the token URL. ValidAudiences = new [] { _audienceUri, string.Concat(_audienceUri.EnsureTrailingSlash(), "connect/token") }, ValidateAudience = true }; try { handler.ValidateToken(token, tokenValidationParameters, out _); return(success); } catch (Exception e) { _logger.LogError(e, "JWT token validation error"); return(fail); } }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">id or credential is missing.</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret) { _logger.LogError("Parsed secret should not be of type: {type}", parsedSecret.Type ?? "null"); return(fail); } var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret); if (!sharedSecrets.Any()) { _logger.LogDebug("No shared secret configured for client."); return(fail); } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } foreach (var secret in sharedSecrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; // use time constant string comparison var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value); if (isValid) { return(success); } } _logger.LogDebug("No matching plain text secret found."); return(fail); }
/// <summary> /// Validates the secret. /// </summary> /// <param name="parsedSecret">The parsed secret.</param> /// <param name="secrets">The secrets.</param> /// <returns></returns> public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var secretsArray = secrets as Secret[] ?? secrets.ToArray(); var expiredSecrets = secretsArray.Where(s => s.Expiration.HasExpired(_clock.UtcNow.UtcDateTime)).ToList(); if (expiredSecrets.Any()) { expiredSecrets.ForEach( ex => _logger.LogInformation("Secret [{description}] is expired", ex.Description ?? "no description")); } var currentSecrets = secretsArray.Where(s => !s.Expiration.HasExpired(_clock.UtcNow.UtcDateTime)).ToArray(); // see if a registered validator can validate the secret foreach (var validator in _validators) { var secretValidationResult = await validator.ValidateAsync(currentSecrets, parsedSecret); if (secretValidationResult.Success) { _logger.LogDebug("Secret validator success: {0}", validator.GetType().Name); return(secretValidationResult); } } _logger.LogDebug("Secret validators could not validate secret"); return(new SecretValidationResult { Success = false }); }
public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var assertion = _assertionManager.Parse(parsedSecret.Credential as string); var result = await _assertionManager.ValidateAsync(assertion); if (!result.Success) { return(result); } var validity = await _certificateTypeValidationService.Validate(assertion.Certificates, parsedSecret.Id); return(new SecretValidationResult { Success = validity }); }
/// <summary> /// Validates the current request. /// </summary> /// <param name="context">The context.</param> /// <returns></returns> public async Task <ClientSecretValidationResult> ValidateAsync(NameValueCollection nvc) { _logger.LogDebug("Start client validation"); var fail = new ClientSecretValidationResult { IsError = true }; var id = nvc.Get("client_id"); var secret = nvc.Get("client_secret"); ParsedSecret parsedSecret = null; // client id must be present if (id.IsPresent()) { if (id.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return(null); } if (secret.IsPresent()) { if (secret.Length > _options.InputLengthRestrictions.ClientSecret) { _logger.LogError("Client secret exceeds maximum length."); return(null); } parsedSecret = new ParsedSecret { Id = id, Credential = secret, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; } } if (parsedSecret == null) { await RaiseFailureEventAsync("unknown", "No client id found"); _logger.LogError("No client identifier found"); return(fail); } // load client var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id); if (client == null) { await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client"); _logger.LogError("No client with id '{clientId}' found. aborting", parsedSecret.Id); return(fail); } if (!client.RequireClientSecret) { _logger.LogDebug("Public Client - skipping secret validation success"); } else { var result = await _validator.ValidateAsync(parsedSecret, client.ClientSecrets); if (result.Success == false) { await RaiseFailureEventAsync(client.ClientId, "Invalid client secret"); _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId); return(fail); } } _logger.LogDebug("Client validation success"); var success = new ClientSecretValidationResult { IsError = false, Client = client, Secret = parsedSecret }; await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type); return(success); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not an X509 Certificate</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != Constants.ParsedSecretTypes.X509Certificate) { return(fail); } var cert = parsedSecret.Credential as X509Certificate2; if (cert == null) { throw new ArgumentException("ParsedSecret.Credential is not an X509 Certificate"); } var thumbprint = cert.Thumbprint; foreach (var secret in secrets) { if (secret.Type == Constants.SecretTypes.X509CertificateThumbprint) { if (TimeConstantComparer.IsEqual(thumbprint.ToLowerInvariant(), secret.Value.ToLowerInvariant())) { return(success); } } } return(fail); }
public virtual Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); if (parsedSecret == null || parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.X509Certificate) { this.Logger.LogDebugIfEnabled($"{this.SecretType} secret validator cannot process {parsedSecret?.Type ?? "null"}."); return(fail); } if (!(parsedSecret.Credential is X509Certificate2 certificate)) { throw new InvalidOperationException("Credential is not a x509 certificate."); } var issuer = certificate.Issuer; if (issuer == null) { this.Logger.LogWarningIfEnabled("The certificate-issuer is null."); return(fail); } var issuerSecrets = secrets.Where(secret => string.Equals(secret.Type, this.SecretType, StringComparison.Ordinal)).ToArray(); if (!issuerSecrets.Any()) { this.Logger.LogDebugIfEnabled($"No {this.SecretType} secrets configured for client."); return(fail); } // ReSharper disable All foreach (var secret in issuerSecrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; if (issuer.Equals(secret.Value, StringComparison.OrdinalIgnoreCase)) { var values = new Dictionary <string, string> { { "x5t#S256", certificate.Thumbprint } }; var confirmation = JsonConvert.SerializeObject(values); var result = new SecretValidationResult { Success = true, Confirmation = confirmation }; return(Task.FromResult(result)); } } // ReSharper restore All this.Logger.LogDebugIfEnabled($"No matching {this.SecretType} secret found."); return(fail); }
public static ClientSecretValidationResult ToValidationResult(this Client client, ParsedSecret secret = null) { return(new ClientSecretValidationResult { Client = client, Secret = secret }); }
/// <summary> /// Tries to find a secret that can be used for authentication /// </summary> /// <returns> /// A parsed secret /// </returns> public Task <ParsedSecret> ParseAsync(HttpContext context) { _logger.LogDebug("Start parsing Basic Authentication secret"); var notfound = Task.FromResult <ParsedSecret>(null); var authorizationHeader = context.Request.Headers["Authorization"].FirstOrDefault(); if (authorizationHeader.IsMissing()) { return(notfound); } if (!authorizationHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) { return(notfound); } var parameter = authorizationHeader.Substring("Basic ".Length); string pair; try { pair = Encoding.UTF8.GetString( Convert.FromBase64String(parameter)); } catch (FormatException) { _logger.LogWarning("Malformed Basic Authentication credential."); return(notfound); } catch (ArgumentException) { _logger.LogWarning("Malformed Basic Authentication credential."); return(notfound); } var ix = pair.IndexOf(':'); if (ix == -1) { _logger.LogWarning("Malformed Basic Authentication credential."); return(notfound); } var clientId = pair.Substring(0, ix); var secret = pair.Substring(ix + 1); if (clientId.IsPresent()) { if (clientId.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return(notfound); } if (secret.IsPresent()) { if (secret.Length > _options.InputLengthRestrictions.ClientSecret) { _logger.LogError("Client secret exceeds maximum length."); return(notfound); } var parsedSecret = new ParsedSecret { Id = Decode(clientId), Credential = Decode(secret), Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; return(Task.FromResult(parsedSecret)); } else { // client secret is optional _logger.LogDebug("client id without secret found"); var parsedSecret = new ParsedSecret { Id = Decode(clientId), Type = IdentityServerConstants.ParsedSecretTypes.NoSecret }; return(Task.FromResult(parsedSecret)); } } _logger.LogDebug("No Basic Authentication secret found"); return(notfound); }
/// <summary>Validate Async</summary> /// <param name="secrets">List of secrets from Vault</param> /// <param name="parsedSecret">Parsed Seccret</param> /// <returns></returns> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != Constants.VaultSharedSecretType) { Logger.Debug($"Parsed secret should not be of type {parsedSecret.Type}"); return(fail); } var sharedSecret = parsedSecret.Credential as string; if (string.IsNullOrWhiteSpace(sharedSecret)) { Logger.Debug("Id or Credential is missing"); return(fail); } foreach (var secret in secrets) { if (secret.Type != Constants.VaultSharedSecretType) { Logger.Debug($"Skipping secret: {secret.Description}, secret is not of type {Constants.VaultSharedSecretType}"); continue; } if (secret.Value == sharedSecret) { return(success); } } return(fail); }
/// <summary> /// Tries to find a secret on the environment that can be used for authentication /// </summary> /// <param name="environment">The environment.</param> /// <returns> /// A parsed secret /// </returns> public Task <ParsedSecret> ParseAsync(IDictionary <string, object> environment) { Logger.Debug("Start parsing Basic Authentication secret"); var notfound = Task.FromResult <ParsedSecret>(null); var context = new OwinContext(environment); var authorizationHeader = context.Request.Headers.Get("Authorization"); if (authorizationHeader == null) { return(notfound); } if (!authorizationHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) { return(notfound); } var parameter = authorizationHeader.Substring("Basic ".Length); string pair; try { pair = Encoding.UTF8.GetString( Convert.FromBase64String(parameter)); } catch (FormatException) { Logger.Debug("Malformed Basic Authentication credential."); return(notfound); } catch (ArgumentException) { Logger.Debug("Malformed Basic Authentication credential."); return(notfound); } var ix = pair.IndexOf(':'); if (ix == -1) { Logger.Debug("Malformed Basic Authentication credential."); return(notfound); } var clientId = pair.Substring(0, ix); var secret = pair.Substring(ix + 1); if (clientId.IsPresent() && secret.IsPresent()) { var parsedSecret = new ParsedSecret { Id = clientId, Credential = secret, Type = Constants.ParsedSecretTypes.SharedSecret }; return(Task.FromResult(parsedSecret)); } Logger.Debug("No Basic Authentication secret found"); return(notfound); }
/// <inheritdoc/> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); if (parsedSecret.Type != ParsedSecretTypes.X509Certificate) { _logger.LogDebug("X509 name secret validator cannot process {type}", parsedSecret.Type ?? "null"); return(fail); } var cert = parsedSecret.Credential as X509Certificate2; if (cert == null) { throw new InvalidOperationException("Credential is not a x509 certificate."); } var name = cert.Subject; if (name == null) { _logger.LogWarning("No subject/name found in X509 certificate."); return(fail); } var nameSecrets = secrets.Where(s => s.Type == SecretTypes.X509CertificateName); if (!nameSecrets.Any()) { _logger.LogDebug("No x509 name secrets configured for client."); return(fail); } foreach (var nameSecret in nameSecrets) { var secretDescription = string.IsNullOrEmpty(nameSecret.Description) ? "no description" : nameSecret.Description; if (name.Equals(nameSecret.Value, StringComparison.Ordinal)) { var values = new Dictionary <string, string> { { "x5t#S256", cert.Thumbprint } }; var cnf = JsonConvert.SerializeObject(values); var result = new SecretValidationResult { Success = true, Confirmation = cnf }; return(Task.FromResult(result)); } } _logger.LogDebug("No matching x509 name secret found."); return(fail); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not a JWT token</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != Constants.ParsedSecretTypes.JwtBearer) { return(fail); } var jwtTokenString = parsedSecret.Credential as string; if (jwtTokenString == null) { throw new ArgumentException("ParsedSecret.Credential is not a string."); } var enumeratedSecrets = secrets.ToList().AsReadOnly(); var trustedKeys = GetTrustedKeys(enumeratedSecrets, jwtTokenString); if (!trustedKeys.Any()) { Logger.Warn("There are no certificates available to validate client assertion."); return(fail); } var tokenValidationParameters = new TokenValidationParameters { IssuerSigningKeys = trustedKeys, ValidateIssuerSigningKey = true, ValidIssuer = parsedSecret.Id, ValidateIssuer = true, ValidAudience = audienceUri, ValidateAudience = true, RequireSignedTokens = true, RequireExpirationTime = true }; try { SecurityToken token; var handler = new EmbeddedCertificateJwtSecurityTokenHandler(); handler.ValidateToken(jwtTokenString, tokenValidationParameters, out token); var jwtToken = (JwtSecurityToken)token; if (jwtToken.Subject != jwtToken.Issuer) { Logger.Warn("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); return(fail); } return(success); } catch (Exception e) { Logger.Debug("JWT token validation error: " + e.Message); return(fail); } }
async public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { foreach (var secret in secrets .Where(s => "SecretsVault-Secret".Equals(s.Type, StringComparison.CurrentCultureIgnoreCase))) { var secretsVersion = await _secretsVaultManager.GetSecretVersion(secret.Value); if (parsedSecret.Credential?.ToString() == secretsVersion.Secret) { return(new SecretValidationResult() { Success = true }); } } var result = new SecretValidationResult() { Success = false }; return(result); }
/// <summary> /// Validate api secrets /// </summary> /// <param name="secrets"></param> /// <param name="parsedSecret"></param> /// <returns></returns> public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { SecretValidationResult validationResult; // Impersonation. If the current user is an admin, we allow any password to be used for the token if (_contextAccessor.HttpContext.User.Identity.IsAuthenticated) { if (_contextAccessor.HttpContext.User.HasClaim(tbl => tbl.Type == ClaimTypes.Role && (tbl.Value.ToLower() == "root" || tbl.Value.ToLower() == "moderator"))) { validationResult = new SecretValidationResult() { IsError = false, Success = true }; return(await Task.FromResult(validationResult)); } } // Owner's credentials var result = _passwordHasher.VerifyHashedPassword( parsedSecret.Id, secrets?.FirstOrDefault()?.Value, parsedSecret.Credential.ToString()); validationResult = new SecretValidationResult { IsError = result == PasswordVerificationResult.Failed, Success = result == PasswordVerificationResult.Success }; return(await Task.FromResult(validationResult)); }
public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { if (secrets.Any()) { if (secrets.First().Type == "confirmation.test") { var cnf = new Dictionary <string, string> { { "x5t#S256", "foo" } }; var result = new SecretValidationResult { Success = true, Confirmation = JsonConvert.SerializeObject(cnf) }; return(Task.FromResult(result)); } } return(Task.FromResult(new SecretValidationResult { Success = false })); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">id or credential is missing.</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != Constants.ParsedSecretTypes.SharedSecret) { Logger.Debug(string.Format("Parsed secret should not be of type: {0}", parsedSecret.Type ?? "null")); return(fail); } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } foreach (var secret in secrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; // this validator is only applicable to shared secrets if (secret.Type != Constants.SecretTypes.SharedSecret) { Logger.Debug(string.Format("Skipping secret: {0}, secret is not of type {1}.", secretDescription, Constants.SecretTypes.SharedSecret)); continue; } // use time constant string comparison var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value); if (isValid) { return(success); } } Logger.Debug("No matching plain text secret found."); return(fail); }
public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { //Identity Server return a success secret validation result if ANY of the secrets is valid //SO though needs to validate both that the Jwt is valid and the extracted data is also valid foreach (var validator in _secretValidators) { var result = await validator.ValidateAsync(secrets, parsedSecret); if (!result.Success) { return(result); } } _logger.LogDebug("All secret validators validate all secrets"); return(new SecretValidationResult { Success = true }); }
/// <inheritdoc/> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); if (parsedSecret.Type != ParsedSecretTypes.X509Certificate) { _logger.LogDebug("X509 thumbprint secret validator cannot process {type}", parsedSecret.Type ?? "null"); return(fail); } var cert = parsedSecret.Credential as X509Certificate2; if (cert == null) { throw new InvalidOperationException("Credential is not a x509 certificate."); } var thumbprint = cert.Thumbprint; if (thumbprint == null) { _logger.LogWarning("No thumbprint found in X509 certificate."); return(fail); } var thumbprintSecrets = secrets.Where(s => s.Type == SecretTypes.X509CertificateThumbprint); if (!thumbprintSecrets.Any()) { _logger.LogDebug("No thumbprint secrets configured for client."); return(fail); } foreach (var thumbprintSecret in thumbprintSecrets) { var secretDescription = string.IsNullOrEmpty(thumbprintSecret.Description) ? "no description" : thumbprintSecret.Description; if (thumbprint.Equals(thumbprintSecret.Value, StringComparison.OrdinalIgnoreCase)) { var result = new SecretValidationResult { Success = true, Confirmation = cert.CreateThumbprintCnf() }; return(Task.FromResult(result)); } } _logger.LogDebug("No matching x509 thumbprint secret found."); return(fail); }
public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { return(fail); } if (!(parsedSecret.Credential is string jwtTokenString)) { _logger.LogError("ParsedSecret.Credential is not a string."); return(fail); } if (!_genericJwtValidator.IsValid(jwtTokenString, parsedSecret.Id, _partyDetailsOptions.ClientId)) { _logger.LogError("ParsedSecret.Credential is not a valid JWT."); return(fail); } return(success); }
public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { if (parsedSecret.Credential.GetType().Name != "String") { return(Task.FromResult(new SecretValidationResult { Success = false, Error = "Invalid data type", ErrorDescription = "Secret should only be of type String" })); } foreach (Secret secret in secrets) { if (secret.Value == (string)parsedSecret.Credential && secret.Type == parsedSecret.Type) { return(Task.FromResult(new SecretValidationResult { Success = true, })); } } return(Task.FromResult(new SecretValidationResult { Success = false, Error = "Invalid credentials", ErrorDescription = "Id or secret is invalid" })); }
public async Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = new SecretValidationResult { Success = false }; var assertion = parsedSecret.Credential as string; if (assertion == null) { _logger.LogInformation("The client assertion is not provided."); return(fail); } var certificates = _assertionParser.GetCertificatesData(assertion); if (!certificates.Any()) { _logger.LogInformation("The x5c certificates chain is not provided."); return(fail); } var validateCertificateResult = await _schemeOwnerClient.ValidateCertificate(new ClientAssertion(), certificates.ToArray()).ConfigureAwait(false); if (!validateCertificateResult.Validity) { _logger.LogError("Scheme Owner didn't validate the certificates."); return(fail); } var secretsIncludingCertificates = secrets.ToList(); secretsIncludingCertificates.AddRange(certificates.Select(c => new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = c })); var result = await _innerValidator.Instance.ValidateAsync(secretsIncludingCertificates, parsedSecret); return(result); }
/// <summary> /// Validates a secret /// </summary> /// <param name="secrets">The stored secrets.</param> /// <param name="parsedSecret">The received secret.</param> /// <returns> /// A validation result /// </returns> /// <exception cref="System.ArgumentException">ParsedSecret.Credential is not a JWT token</exception> public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { return(fail); } var jwtTokenString = parsedSecret.Credential as string; if (jwtTokenString == null) { _logger.LogError("ParsedSecret.Credential is not a string."); return(fail); } var enumeratedSecrets = secrets.ToList().AsReadOnly(); List <SecurityKey> trustedKeys; try { trustedKeys = GetTrustedKeys(enumeratedSecrets); } catch (Exception e) { _logger.LogError(e, "Could not parse assertion as JWT token"); return(fail); } if (!trustedKeys.Any()) { _logger.LogError("There are no certificates available to validate client assertion."); return(fail); } var tokenValidationParameters = new TokenValidationParameters { IssuerSigningKeys = trustedKeys, ValidateIssuerSigningKey = true, IssuerSigningKeyValidator = _issuerSigningKeyValidator.Validate, ValidIssuer = parsedSecret.Id, ValidateIssuer = true, ValidAudience = _audienceUri, ValidateAudience = true, RequireSignedTokens = true, RequireExpirationTime = true }; try { var handler = new JwtSecurityTokenHandler(); handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token); var jwtToken = (JwtSecurityToken)token; if (jwtToken.Subject != jwtToken.Issuer) { _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); return(fail); } return(success); } catch (Exception e) { _logger.LogError(e, "JWT token validation error"); return(fail); } }
public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret) { throw new NotImplementedException(); }