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

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

                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);
            }
        }
Exemple #2
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);
            }
        }
Exemple #3
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 validAudiences = new[]
        {
            // token endpoint URL
            string.Concat(_urls.BaseUrl.EnsureTrailingSlash(), Constants.ProtocolRoutePaths.Token),
            // TODO: remove the issuer URL in a future major release?
            // issuer URL
            string.Concat((await _issuerNameService.GetCurrentAsync()).EnsureTrailingSlash(), Constants.ProtocolRoutePaths.Token)
        }.Distinct();

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

        var handler = new JsonWebTokenHandler()
        {
            MaximumTokenSizeInBytes = _options.InputLengthRestrictions.Jwt
        };
        var result = handler.ValidateToken(jwtTokenString, tokenValidationParameters);

        if (!result.IsValid)
        {
            _logger.LogError(result.Exception, "JWT token validation error");
            return(fail);
        }

        var jwtToken = (JsonWebToken)result.SecurityToken;

        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.ValidTo;

        if (exp == DateTime.MinValue)
        {
            _logger.LogError("exp is missing.");
            return(fail);
        }

        var jti = jwtToken.Id;

        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, exp.AddMinutes(5));
        }

        return(success);
    }