2015-12-21 14 views
23

Хорошо, я понимаю, что вопрос, который я задаю, может быть довольно очевидным, но, к сожалению, мне не хватает знаний по этому вопросу, и эта задача для меня довольно сложна.Проверка JWT, подписанная алгоритмом RS256 с использованием открытого ключа в C#

У меня есть токен идентификатора (JWT), который возвращает поставщик OpenID Connect. Вот оно:

eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ

Его заголовок и полезную нагрузку декодируются как это:

{ 
"kid":"1e9gdk7", 
"alg":"RS256" 
}. 
{ 
"iss": "http://server.example.com", 
"sub": "248289761001", 
"aud": "s6BhdRkqt3", 
"nonce": "n-0S6_WzA2Mj", 
"exp": 1311281970, 
"iat": 1311280970, 
"c_hash": "LDktKdoQak3Pk0cnXxCltA" 
} 

От открытия провайдера ИОДС, я уже получил открытый ключ (JWK):

{ 
"kty":"RSA", 
"kid":"1e9gdk7", 
"n":"w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ", 
"e":"AQAB" 
} 

Таким образом, вопрос заключается в том, как именно в C# могу ли я проверить этот JWT с помощью открытого ключа для алгоритма RS256, который у меня есть? Было бы замечательно, если бы был хороший учебник, описывающий эту процедуру явно. Однако пример того, как это сделать, используя System.IdentityModel.Tokens.Jwt, также отлично работает.

UPDATE: Я понимаю, что мне нужно сделать что-то вроде кода ниже, но я понятия не имею, где, чтобы получить «ключ» для расчета SHA256 хэш.

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; 
    string[] tokenParts = tokenStr.Split('.'); 

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
    rsa.ImportParameters(
    new RSAParameters() { 
     Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), 
     Exponent = FromBase64Url("AQAB") 
    }); 

    HMACSHA256 sha = new HMACSHA256(key); 
    byte[] hash = sha.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + '.' + tokenParts[1])); 
    byte[] signature = rsa.Encrypt(hash, false); 
    string strSignature = Base64UrlEncode(signature); 
    if (String.Compare(strSignature, tokenParts[2], false) == 0) 
    return true; 
+0

Какую роль вы выполнять? (Клиент, Сервер ресурсов?) – jwilleke

+0

Это клиентское приложение. Он должен работать с обычным OIDC-провайдером (не иметь контроля над ним), который использует алгоритм подписи RS256. На данный момент все отлично работает, и проверка подписи является последним препятствием, с которым я столкнулся. –

+1

Помогает ли это? https://msdn.microsoft.com/en-us/library/0h05c7e2(v=vs.110).aspx – jwilleke

ответ

20

Благодаря jwilleke, у меня есть решение. Чтобы проверить подпись RS256 JWT, необходимо использовать класс RSAPKCS1SignatureDeformatter и его метод VerifySignature.

Вот точный код для моих данных образца:

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; 
    string[] tokenParts = tokenStr.Split('.'); 

    RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
    rsa.ImportParameters(
    new RSAParameters() { 
     Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), 
     Exponent = FromBase64Url("AQAB") 
    }); 

    SHA256 sha256 = SHA256.Create(); 
    byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + '.' + tokenParts[1])); 

    RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa); 
    rsaDeformatter.SetHashAlgorithm("SHA256"); 
    if (rsaDeformatter.VerifySignature(hash, FromBase64Url(tokenParts[2]))) 
    MessageBox.Show("Signature is verified"); 

//... 
    static byte[] FromBase64Url(string base64Url) 
    { 
    string padded = base64Url.Length % 4 == 0 
     ? base64Url : base64Url + "====".Substring(base64Url.Length % 4); 
    string base64 = padded.Replace("_", "/") 
          .Replace("-", "+"); 
    return Convert.FromBase64String(base64); 
    } 
+0

Любопытно, почему вы предпочитаете использовать низкоуровневые конструкции здесь, а не 'System.IdentityModel.Tokens.Jwt'? – explunit

+3

Прежде всего, документация для «System.IdentityModel.Tokens.Jwt» ужасна и устарела. Текущая версия этого пакета имеет другие API, отличные от тех, которые описаны в MSDN. Я попытался использовать его, и это сработало для меня в целом, но я не смог найти способ выполнить эту простую задачу проверки подписи токенов. –

+0

И вторая причина в том, что я буду работать с пользовательскими поставщиками OIDC, и возможно, что я не смогу получить список открытых ключей для одного из них. В этой ситуации мне нужно будет пропустить проверку подписи, которая, я думаю, невозможна с помощью «System.IdentityModel.Tokens.Jwt». –

6

Вот пример использования IdentityModel.Tokens.Jwt для проверки:

string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ"; 

RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); 
rsa.ImportParameters(
    new RSAParameters() 
    { 
     Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"), 
     Exponent = FromBase64Url("AQAB") 
    }); 

var validationParameters = new TokenValidationParameters 
       { 
        RequireExpirationTime = true, 
        RequireSignedTokens = true, 
        ValidateAudience = false, 
        ValidateIssuer = false, 
        ValidateLifetime = false, 
        IssuerSigningKey = new RsaSecurityKey(rsa) 
       }; 

SecurityToken validatedSecurityToken = null; 
var handler = new JwtSecurityTokenHandler(); 
handler.ValidateToken(tokenStr, validationParameters, out validatedSecurityToken); 
JwtSecurityToken validatedJwt = validatedSecurityToken as JwtSecurityToken; 
Смежные вопросы