ถอดรหัสและตรวจสอบโทเค็น JWT โดยใช้ System.IdentityModel.Tokens.Jwt


103

ฉันได้รับการใช้JWTห้องสมุดถอดรหัส Json เว็บ Token และต้องการที่จะเปลี่ยนการดำเนิน JWT ไมโครซอฟท์อย่างเป็นทางการSystem.IdentityModel.Tokens.Jwt

เอกสารประกอบมีน้อยมากดังนั้นฉันจึงมีช่วงเวลาที่ยากลำบากในการหาวิธีทำสิ่งที่ฉันทำกับไลบรารี JWT ให้สำเร็จ ด้วยไลบรารี JWT มีเมธอด Decode ที่ใช้ JWT ที่เข้ารหัส base64 และเปลี่ยนเป็น JSON ซึ่งสามารถ deserialized ได้ ฉันต้องการทำสิ่งที่คล้ายกันโดยใช้ System.IdentityModel.Tokens.Jwt แต่หลังจากขุดไปพอสมควรก็ไม่สามารถหาวิธีได้

สิ่งที่คุ้มค่าฉันกำลังอ่านโทเค็น JWT จากคุกกี้เพื่อใช้กับกรอบข้อมูลประจำตัวของ Google

ความช่วยเหลือใด ๆ จะได้รับการชื่นชม



นี่คือคำตอบโดยตรงเกี่ยวกับวิธีดึงใบรับรองของ Google และตรวจสอบโทเค็น - stackoverflow.com/questions/29757140/…
rothschild86

คำตอบ:


153

ภายในแพคเกจมีระดับที่เรียกว่าซึ่งเกิดขึ้นจากJwtSecurityTokenHandler System.IdentityModel.Tokens.SecurityTokenHandlerใน WIF นี่คือคลาสหลักสำหรับโทเค็นการรักษาความปลอดภัยที่แยกส่วนและอนุกรม

คลาสมีReadToken(String)เมธอดที่จะใช้สตริง JWT ที่เข้ารหัส base64 ของคุณและส่งกลับค่าSecurityTokenที่แสดงถึง JWT

SecurityTokenHandlerนอกจากนี้ยังมีValidateToken(SecurityToken)วิธีการที่จะใช้เวลาของคุณและสร้างSecurityToken ReadOnlyCollection<ClaimsIdentity>โดยปกติสำหรับ JWT สิ่งนี้จะมีClaimsIdentityอ็อบเจ็กต์เดียวที่มีชุดการอ้างสิทธิ์ที่แสดงถึงคุณสมบัติของ JWT ดั้งเดิม

JwtSecurityTokenHandlerกำหนดโอเวอร์โหลดเพิ่มเติมสำหรับValidateTokenโดยเฉพาะอย่างยิ่งมีClaimsPrincipal ValidateToken(JwtSecurityToken, TokenValidationParameters)โอเวอร์โหลด TokenValidationParametersอาร์กิวเมนต์ช่วยให้คุณระบุใบรับรองการลงนามโทเค็น (เป็นรายการX509SecurityTokens) นอกจากนี้ยังมีโอเวอร์โหลดที่ใช้ JWT stringแทนที่จะเป็นSecurityTokenไฟล์.

โค้ดในการดำเนินการนี้ค่อนข้างซับซ้อน แต่สามารถพบได้ในโค้ด Global.asax.cx ( TokenValidationHandlerคลาส) ในตัวอย่างนักพัฒนาที่เรียกว่า "ADAL - Native App to REST service - Authentication with ACS via Browser Dialog" ซึ่งอยู่ที่

http://code.msdn.microsoft.com/AAL-Native-App-to-REST-de57f2cc

หรืออีกวิธีหนึ่งคือJwtSecurityTokenคลาสมีวิธีการเพิ่มเติมที่ไม่ได้อยู่ในSecurityTokenคลาสพื้นฐานเช่นClaimsคุณสมบัติที่ได้รับการอ้างสิทธิ์ที่มีอยู่โดยไม่ต้องดำเนินการผ่านClaimsIdentityคอลเลกชัน นอกจากนี้ยังมีPayloadคุณสมบัติที่ส่งคืนJwtPayloadอ็อบเจ็กต์ที่ให้คุณได้รับที่ JSON ดิบของโทเค็น ขึ้นอยู่กับสถานการณ์ของคุณว่าแนวทางใดเหมาะสมที่สุด

เอกสารทั่วไป (เช่นไม่ใช่เฉพาะ JWT) สำหรับSecurityTokenHandlerคลาสอยู่ที่

http://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.securitytokenhandler.aspx

ขึ้นอยู่กับแอปพลิเคชันของคุณคุณสามารถกำหนดค่าตัวจัดการ JWT ในไปป์ไลน์ WIF เหมือนกับตัวจัดการอื่น ๆ

มี 3 ตัวอย่างที่ใช้ในการใช้งานประเภทต่างๆที่

http://code.msdn.microsoft.com/site/search?f%5B0%5D.Type=SearchText&f%5B0%5D.Value=aal&f%5B1%5D.Type=User&f%5B1%5D.Value=Azure% 20AD% 20Developer% 20Experience% 20Team & f% 5B1% 5D.Text = Azure% 20AD% 20Developer% 20Experience% 20Team

อาจจะเหมาะกับความต้องการของคุณหรืออย่างน้อยก็ปรับให้เข้ากับสิ่งเหล่านี้ได้


3
ฉันขอขอบคุณสำหรับคำตอบของคุณ ดังนั้นเมื่อฉันมี ClaimsIdentity ฉันจะตรวจสอบกับคีย์สาธารณะได้อย่างไร โดยเฉพาะอย่างยิ่งฉันกำลังพยายามยืนยันชุดเครื่องมือข้อมูลประจำตัวของ Google JWT กับคีย์สาธารณะ ( gstatic.com/authtoolkit/cert/gitkit_cert.pem )
w.brian

4
อัปเดตคำตอบของฉัน - ฉันไม่สามารถใส่แหล่งที่มาทั้งหมดสำหรับสิ่งนี้ได้ แต่ฉันชี้ให้คุณเห็นในทิศทางของตัวอย่างนักพัฒนาที่เหมาะสม หวังว่าจะช่วยได้
Mike Goodwin

4
@ w.brian - ฉันพยายามทำแบบเดียวกัน ฉันมีโทเค็นที่ฉันสามารถถอดรหัสได้และคีย์สาธารณะที่ฉันต้องการตรวจสอบ แต่ถึงแม้จะดูตัวอย่างเหล่านี้ฉันก็ดิ้นรนเพื่อดูว่าฉันทำสิ่งนี้ได้อย่างไร คุณมีตัวชี้รหัสใดที่ช่วยคุณได้จริงหรือไม่? ขอบคุณ.
Barguast

28

ฉันแค่สงสัยว่าทำไมต้องใช้ไลบรารีบางส่วนสำหรับการถอดรหัสและการตรวจสอบโทเค็น JWT เลย

โทเค็น JWT ที่เข้ารหัสสามารถสร้างได้โดยใช้รหัสเทียมต่อไปนี้

var headers = base64URLencode(myHeaders);
var claims = base64URLencode(myClaims);
var payload = header + "." + claims;

var signature = base64URLencode(HMACSHA256(payload, secret));

var encodedJWT = payload + "." + signature;

มันง่ายมากที่จะทำโดยไม่ต้องมีไลบรารีเฉพาะ ใช้รหัสต่อไปนี้:

using System;
using System.Text;
using System.Security.Cryptography;

public class Program
{   
    // More info: https://stormpath.com/blog/jwt-the-right-way/
    public static void Main()
    {           
        var header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
        var claims = "{\"sub\":\"1047986\",\"email\":\"jon.doe@eexample.com\",\"given_name\":\"John\",\"family_name\":\"Doe\",\"primarysid\":\"b521a2af99bfdc65e04010ac1d046ff5\",\"iss\":\"http://example.com\",\"aud\":\"myapp\",\"exp\":1460555281,\"nbf\":1457963281}";

        var b64header = Convert.ToBase64String(Encoding.UTF8.GetBytes(header))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");
        var b64claims = Convert.ToBase64String(Encoding.UTF8.GetBytes(claims))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        var payload = b64header + "." + b64claims;
        Console.WriteLine("JWT without sig:    " + payload);

        byte[] key = Convert.FromBase64String("mPorwQB8kMDNQeeYO35KOrMMFn6rFVmbIohBphJPnp4=");
        byte[] message = Encoding.UTF8.GetBytes(payload);

        string sig = Convert.ToBase64String(HashHMAC(key, message))
            .Replace('+', '-')
            .Replace('/', '_')
            .Replace("=", "");

        Console.WriteLine("JWT with signature: " + payload + "." + sig);        
    }

    private static byte[] HashHMAC(byte[] key, byte[] message)
    {
        var hash = new HMACSHA256(key);
        return hash.ComputeHash(message);
    }
}

การถอดรหัสโทเค็นเป็นเวอร์ชันย้อนกลับของรหัสด้านบนในการตรวจสอบลายเซ็นคุณจะต้องเหมือนกันและเปรียบเทียบส่วนลายเซ็นกับลายเซ็นจากการคำนวณ

อัปเดต: สำหรับผู้ที่กำลังดิ้นรนในการเข้ารหัส / ถอดรหัส base64 urlsafe โปรดดูที่อื่น คำถาม SOรวมถึง wiki และ RFCs


2
คำตอบที่ดี แม้ว่าคุณจะแสดงการลงนามโดยใช้ HMAC ที่นี่คุณควรระวังช่องโหว่ที่สำคัญบางอย่างในไลบรารีที่ใช้การตรวจสอบ HMAC ตามรายละเอียดในไซต์ Auth0 ที่นี่: auth0.com/blog/2015/03/31/…
Sudhanshu Mishra

2
ฉันรู้สึกว่านี่คือคำตอบที่ดีที่สุด OP ขอข้อมูลเกี่ยวกับ JWT โดยเฉพาะซึ่งบทความนี้กล่าวถึงด้วยตัวอย่างที่ชัดเจน ..
webworm

15
คำตอบนี้จะอธิบายและแสดงให้เห็นถึงวิธีการที่จะ enรหัส JWT เมื่อคำถามคือค่อนข้างชัดเจนเกี่ยวกับเดเข้ารหัส นี้อาจจะเป็นคำตอบที่ดี แต่มันเป็นคำตอบให้กับคำถามที่แตกต่างอย่างสิ้นเชิง
Deltics

34
แนวคิดของ "คำตอบ" คือการตอบคำถามไม่ใช่สร้างปริศนาโดยคาดหวังให้ใครสักคนไขปริศนาแบบย้อนกลับ นอกจากนี้การรู้วิธีเข้ารหัสไม่ได้หมายความว่าคุณจะต้องรู้วิธีถอดรหัสเนื่องจากอาจเกี่ยวข้องกับการจัดการกับโทเค็นของบุคคลที่สามและการเรียกค้นคีย์เพื่อตรวจสอบลายเซ็นของพวกเขาแทนที่จะใช้คีย์เพื่อลงนามของคุณเอง ในกรณีใด ๆ คำตอบที่ไม่ไม่จริงตอบคำถามโดยความหมายคือไม่ว่า " ดีกว่า " คำตอบเมื่อเทียบกับคนที่ไม่ซึ่งเป็นข้อสังเกตที่ผมได้รับการตอบสนอง
Deltics

3
อย่าเขียนโค้ดส่วนที่เกี่ยวข้องกับความปลอดภัยด้วยตัวคุณเองเว้นแต่คุณจะรู้ว่าคุณกำลังทำอะไร มีบางสิ่งที่คุณอาจทำผิดได้ง่ายๆ ความจริงที่ชัดเจนว่าห้องสมุด jwt ที่มีช่องโหว่มีข้อพิสูจน์ว่าเป็นความคิดที่ดีที่จะเขียนโค้ดนี้ด้วยตัวเองเว้นแต่คุณจะคิดว่าคุณรู้จัก JWT ดีกว่าผู้เขียนของไลบรารีเหล่านี้ อย่างไรก็ตามคำตอบนี้ยังคงมีประโยชน์ในการทำความเข้าใจกับ JWT
Christopher K.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.