2015-12-21 11 views
23

Ok, capisco che la domanda che sto ponendo possa essere abbastanza ovvia, ma sfortunatamente mi manca la conoscenza su questo argomento e questo compito sembra essere piuttosto complicato per me.Verifica del JWT firmato con l'algoritmo RS256 utilizzando la chiave pubblica in C#

Ho un token ID (JWT) restituito dal provider OpenID Connect. Eccolo:

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

La sua intestazione e payload vengono decodificati come questo:

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

Dalla scoperta del provider IODC, ho la chiave pubblica (JWK):

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

Quindi, la domanda è come esattamente in C# posso verificare questo JWT usando la chiave pubblica per l'algoritmo RS256 che ho? Sarebbe fantastico se ci fosse un buon tutorial che descrivesse esplicitamente questa procedura. Tuttavia, un esempio di come farlo utilizzando System.IdentityModel.Tokens.Jwt funzionerà correttamente.

UPDATE: ho capito, che ho bisogno di fare qualcosa di simile il codice qui sotto, ma non ho idea di dove trovare 'chiave' per il calcolo SHA256 hash.

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

Quale ruolo stai adempiendo? (Client, server di risorse?) – jwilleke

+0

È un'applicazione client. Dovrebbe funzionare con un provider OIDC personalizzato (non avere il controllo su di esso) che utilizza l'algoritmo di firma RS256. Tutto funziona bene al momento e la verifica della firma è l'ultimo ostacolo che sto affrontando. –

+1

Questo aiuto? https://msdn.microsoft.com/en-us/library/0h05c7e2(v=vs.110).aspx – jwilleke

risposta

20

Grazie a jwilleke, ho avuto una soluzione. Per verificare la firma RS256 di un JWT, è necessario utilizzare la classe RSAPKCS1SignatureDeformatter e il suo metodo VerifySignature.

Ecco il codice esatto per il mio dati di esempio:

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

Semplicemente curioso del fatto che stai scegliendo di utilizzare qui i costrutti di basso livello piuttosto che "System.IdentityModel.Tokens.Jwt'? – explunit

+3

Prima di tutto, la documentazione di "System.IdentityModel.Tokens.Jwt" è orribile e obsoleta. La versione corrente di questo pacchetto ha altre API diverse da quelle documentate in MSDN. Ho provato a utilizzarlo e ha funzionato in generale, ma non sono riuscito a trovare un modo per eseguire questa semplice operazione di verifica della firma del token. –

+0

E la seconda ragione è che lavorerò con i provider OIDC personalizzati ed è possibile che non riesca a ottenere l'elenco delle chiavi pubbliche per uno di essi. In questa situazione, dovrò saltare la verifica della firma che è, penso, impossibile con "System.IdentityModel.Tokens.Jwt". –

6

Ecco un esempio utilizzando IdentityModel.Tokens.Jwt per la convalida:

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; 
Problemi correlati