Beispiel #1
0
        public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
        {
            //parsedSecret.
            //IdentityServer4.Validation.ClientSecretValidator
            var result = new SecretValidationResult {
                Success = false
            };

            return(Task.FromResult(result));
        }
Beispiel #2
0
        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);
    }
Beispiel #4
0
        /// <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);
        }
Beispiel #7
0
        /// <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);
            }
        }
Beispiel #8
0
        /// <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
        });
    }
Beispiel #10
0
        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);
        }
Beispiel #12
0
        /// <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);
        }
Beispiel #16
0
        /// <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);
        }
Beispiel #18
0
        /// <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);
        }
Beispiel #19
0
        /// <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);
            }
        }
Beispiel #20
0
        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);
        }
Beispiel #21
0
        /// <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
            }));
        }
Beispiel #23
0
        /// <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);
        }
Beispiel #24
0
        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);
        }
Beispiel #26
0
        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);
        }
Beispiel #27
0
        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"
            }));
        }
Beispiel #28
0
        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);
        }
Beispiel #29
0
        /// <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);
            }
        }
Beispiel #30
0
 public Task <SecretValidationResult> ValidateAsync(IEnumerable <Secret> secrets, ParsedSecret parsedSecret)
 {
     throw new NotImplementedException();
 }