Как проверить асимметрично подписанный JWT в ядре dotnet? - PullRequest
0 голосов
/ 21 июня 2019

Я нашел примеры асимметричного подписания в .NET FW и примеры симметричного подписания в .NET Core, но я не могу понять, как асимметрично проверять JWT в .NET Core.С учетом URL-адреса набора JWK или открытого ключа, как я могу проверить токен в .NET Core?

1 Ответ

0 голосов
/ 21 июня 2019

Единственная разница между ASymmetric Signing и Symmetric Signing - это ключи подписи. Просто создайте новый ASymmetric Security Key, чтобы параметры проверки токена сделали это.

Предположим, вы хотите использовать алгоритм RSA. Давайте используем powershell для экспорта пары ключей RSA, как показано ниже:

$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider -ArgumentList 2048

$rsa.ToXmlString($true) | Out-File key.private.xml
$rsa.ToXmlString($false) | Out-File key.public.xml

Теперь мы будем использовать две клавиши для подписи токена.

Небольшое исправление

Поскольку API rsa.FromXmlString() поддерживается .NET Core, я просто копирую код @ myloveCc , чтобы создать RsaParameters в C # (эта работа выполняется следующим методом ParseXmlString()):

public static class KeyHelper 
{
    public static RSAParameters ParseXmlString( string xml){
        RSAParameters parameters = new RSAParameters();

        System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
        xmlDoc.LoadXml(xml);

        if (xmlDoc.DocumentElement.Name.Equals("RSAKeyValue"))
        {
            foreach (System.Xml.XmlNode node in xmlDoc.DocumentElement.ChildNodes)
            {
                switch (node.Name)
                {
                    case "Modulus": parameters.Modulus = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "Exponent": parameters.Exponent = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "P": parameters.P = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "Q": parameters.Q = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "DP": parameters.DP = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "DQ": parameters.DQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "InverseQ": parameters.InverseQ = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                    case "D": parameters.D = (string.IsNullOrEmpty(node.InnerText) ? null : Convert.FromBase64String(node.InnerText)); break;
                }
            }
        }
        else
        {
            throw new Exception("Invalid XML RSA key.");
        }
        return parameters;
    }


    public static RsaSecurityKey BuildRsaSigningKey(string xml){ 
        var parameters = ParseXmlString(xml);
        var rsaProvider = new RSACryptoServiceProvider(2048);
        rsaProvider.ImportParameters(parameters);
        var key = new RsaSecurityKey(rsaProvider);   
        return key;
    }  
}

Здесь я добавляю вспомогательный метод BuildRsaSigningKey() для генерации SecurityKey.

Генерация токенов

Вот демоверсия для генерации токена с RSA:


public string GenerateToken(DateTime expiry)
{
    var tokenHandler = new JwtSecurityTokenHandler();
    var Identity = new ClaimsIdentity(new[]
    {
        new Claim(ClaimTypes.Name,          "..."),
        // ... other claims
   });

    var xml = "<RSAKeyValue> load...from..local...files...</RSAKeyValue>";
    SecurityKey key =  KeyHelper.BuildRsaSigningKey(xml); 

    var Token = new JwtSecurityToken
    (
        issuer: "test",
        audience: "test-app",
        claims: Identity.Claims,
        notBefore: DateTime.UtcNow,
        expires: expiry,
        signingCredentials: new SigningCredentials(key, SecurityAlgorithms.RsaSha256Signature, SecurityAlgorithms.Sha256Digest)
    );
    var TokenString = tokenHandler.WriteToken(Token);
    return TokenString;
}

Проверка токена

Чтобы проверить его автоматически, настройте аутентификацию канала-носителя JWT, как показано ниже:

Services.AddAuthentication(A =>
{
    A.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    A.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(O =>
{
    var xml = "<RSAKeyValue> load...from..local...files...</RSAKeyValue>";
    var key = KeyHelper.BuildRsaSigningKey(xml);

    O.RequireHttpsMetadata = false;
    O.SaveToken = true;
    O.IncludeErrorDetails = true;
    O.TokenValidationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = key,
        ValidateIssuerSigningKey = true,
        ValidateLifetime = true,   
        // ... other settings
    };
});

Если вы хотите проверить его вручную:

public IActionResult ValidateTokenManually(string jwt)
{
    var xml = "<RSAKeyValue>... the keys ...</RSAKeyValue>";
    SecurityKey key = KeyHelper.BuildRsaSigningKey(xml);    

    var validationParameters = new TokenValidationParameters
    {
        IssuerSigningKey = key,
        RequireSignedTokens = true,
        RequireExpirationTime = true,
        ValidateLifetime = true,
        // ... other settings
    };

    var tokenHandler = new JwtSecurityTokenHandler();
    var principal = tokenHandler.ValidateToken(jwt, validationParameters, out var rawValidatedToken);
    var securityToken = (JwtSecurityToken)rawValidatedToken;
    return Ok(principal);
}
...