C # में कोई JSON वेब टोकन (JWT) उदाहरण है?


101

मुझे ऐसा लग रहा है कि मैं यहां पागल गोलियां ले रहा हूं। आमतौर पर किसी भी कार्य के लिए वेब पर हमेशा एक लाख पुस्तकालय और नमूने तैरते रहते हैं। मैं यहाँ वर्णित के रूप में JSON वेब टोकन (JWT) के उपयोग से Google "सेवा खाता" के साथ प्रमाणीकरण को लागू करने की कोशिश कर रहा हूँ ।

हालाँकि PHP, Python और Java में केवल क्लाइंट लाइब्रेरी है। यहां तक ​​कि Google के प्रमाणीकरण के बाहर JWT उदाहरणों के लिए खोज करना, JWT अवधारणा पर केवल विकेट और ड्राफ्ट हैं। क्या यह वास्तव में इतना नया और संभवतः एक Google स्वामित्व प्रणाली है?

जावा नमूना जो निकटतम है मैं व्याख्या करने के लिए प्रबंधन कर सकता हूं वह बहुत गहन और डराने वाला लगता है। सी # में कुछ ऐसा होना चाहिए जिससे मैं कम से कम शुरुआत कर सकूं। इस के साथ कोई मदद महान होगा!


2
पीटर के पास आपका जवाब है। JWT एक अपेक्षाकृत नया टोकन प्रारूप है, जिसके कारण नमूनों को अभी भी थोड़ा मुश्किल है, लेकिन यह बहुत तेजी से बढ़ रहा है क्योंकि JWT SWT के लिए बहुत आवश्यक प्रतिस्थापन है। Microsoft टोकन प्रारूप का समर्थन कर रहा है, उदाहरण के लिए लाइव कनेक्ट API JWTs का उपयोग करता है।
एंड्रयू लैवर्स

क्या इसका App Engine से कोई लेना देना है?
निक जॉनसन

जवाबों:


74

सबको धन्यवाद। मुझे एक Json वेब टोकन का एक आधार कार्यान्वयन मिला और Google स्वाद के साथ इस पर विस्तार किया गया। मैंने अभी भी पूरी तरह से काम नहीं किया है लेकिन यह 97% है। यह परियोजना भाप बन चुकी है, इसलिए उम्मीद है कि इससे किसी और को अच्छी शुरुआत मिलेगी।

नोट: मैं बेस कार्यान्वयन के लिए किए गए परिवर्तन (याद नहीं कर सकता कि मुझे यह कहाँ मिला है):

  1. परिवर्तित HS256 -> RS256
  2. हेडर में JWT और alg ऑर्डर को स्वैप किया। सुनिश्चित नहीं है कि यह गलत कौन है, Google या कल्पना, लेकिन Google इसे अपने डॉक्स के अनुसार नीचे ले जाता है।
public enum JwtHashAlgorithm
{
    RS256,
    HS384,
    HS512
}

public class JsonWebToken
{
    private static Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>> HashAlgorithms;

    static JsonWebToken()
    {
        HashAlgorithms = new Dictionary<JwtHashAlgorithm, Func<byte[], byte[], byte[]>>
            {
                { JwtHashAlgorithm.RS256, (key, value) => { using (var sha = new HMACSHA256(key)) { return sha.ComputeHash(value); } } },
                { JwtHashAlgorithm.HS384, (key, value) => { using (var sha = new HMACSHA384(key)) { return sha.ComputeHash(value); } } },
                { JwtHashAlgorithm.HS512, (key, value) => { using (var sha = new HMACSHA512(key)) { return sha.ComputeHash(value); } } }
            };
    }

    public static string Encode(object payload, string key, JwtHashAlgorithm algorithm)
    {
        return Encode(payload, Encoding.UTF8.GetBytes(key), algorithm);
    }

    public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm)
    {
        var segments = new List<string>();
        var header = new { alg = algorithm.ToString(), typ = "JWT" };

        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
        //byte[] payloadBytes = Encoding.UTF8.GetBytes(@"{"iss":"761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com","scope":"https://www.googleapis.com/auth/prediction","aud":"https://accounts.google.com/o/oauth2/token","exp":1328554385,"iat":1328550785}");

        segments.Add(Base64UrlEncode(headerBytes));
        segments.Add(Base64UrlEncode(payloadBytes));

        var stringToSign = string.Join(".", segments.ToArray());

        var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

        byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);
        segments.Add(Base64UrlEncode(signature));

        return string.Join(".", segments.ToArray());
    }

    public static string Decode(string token, string key)
    {
        return Decode(token, key, true);
    }

    public static string Decode(string token, string key, bool verify)
    {
        var parts = token.Split('.');
        var header = parts[0];
        var payload = parts[1];
        byte[] crypto = Base64UrlDecode(parts[2]);

        var headerJson = Encoding.UTF8.GetString(Base64UrlDecode(header));
        var headerData = JObject.Parse(headerJson);
        var payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(payload));
        var payloadData = JObject.Parse(payloadJson);

        if (verify)
        {
            var bytesToSign = Encoding.UTF8.GetBytes(string.Concat(header, ".", payload));
            var keyBytes = Encoding.UTF8.GetBytes(key);
            var algorithm = (string)headerData["alg"];

            var signature = HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes, bytesToSign);
            var decodedCrypto = Convert.ToBase64String(crypto);
            var decodedSignature = Convert.ToBase64String(signature);

            if (decodedCrypto != decodedSignature)
            {
                throw new ApplicationException(string.Format("Invalid signature. Expected {0} got {1}", decodedCrypto, decodedSignature));
            }
        }

        return payloadData.ToString();
    }

    private static JwtHashAlgorithm GetHashAlgorithm(string algorithm)
    {
        switch (algorithm)
        {
            case "RS256": return JwtHashAlgorithm.RS256;
            case "HS384": return JwtHashAlgorithm.HS384;
            case "HS512": return JwtHashAlgorithm.HS512;
            default: throw new InvalidOperationException("Algorithm not supported.");
        }
    }

    // from JWT spec
    private static string Base64UrlEncode(byte[] input)
    {
        var output = Convert.ToBase64String(input);
        output = output.Split('=')[0]; // Remove any trailing '='s
        output = output.Replace('+', '-'); // 62nd char of encoding
        output = output.Replace('/', '_'); // 63rd char of encoding
        return output;
    }

    // from JWT spec
    private static byte[] Base64UrlDecode(string input)
    {
        var output = input;
        output = output.Replace('-', '+'); // 62nd char of encoding
        output = output.Replace('_', '/'); // 63rd char of encoding
        switch (output.Length % 4) // Pad with trailing '='s
        {
            case 0: break; // No pad chars in this case
            case 2: output += "=="; break; // Two pad chars
            case 3: output += "="; break; // One pad char
            default: throw new System.Exception("Illegal base64url string!");
        }
        var converted = Convert.FromBase64String(output); // Standard base64 decoder
        return converted;
    }
}

और फिर मेरा Google विशिष्ट JWT वर्ग:

public class GoogleJsonWebToken
{
    public static string Encode(string email, string certificateFilePath)
    {
        var utc0 = new DateTime(1970,1,1,0,0,0,0, DateTimeKind.Utc);
        var issueTime = DateTime.Now;

        var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
        var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 hour, but lets play on safe side

        var payload = new
        {
            iss = email,
            scope = "https://www.googleapis.com/auth/gan.readonly",
            aud = "https://accounts.google.com/o/oauth2/token",
            exp = exp,
            iat = iat
        };

        var certificate = new X509Certificate2(certificateFilePath, "notasecret");

        var privateKey = certificate.Export(X509ContentType.Cert);

        return JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256);
    }
}

9
मूल कार्यान्वयन जॉन शेहन्स JWT पुस्तकालय लगता है: github.com/johnsheehan/jwt
Torbjørn

ऐसा लगता है कि जॉन आरएस एन्क्रिप्शन एल्गोरिदम (एल्ग फ्लैग) का समर्थन नहीं करता है, लेकिन यह संस्करण करता है।
रयान

15
यह संस्करण RS256 हस्ताक्षर एल्गोरिथ्म को सही ढंग से समर्थन नहीं करता है! यह केवल कुंजी बाइट्स के साथ इनपुट को हैश करता है क्योंकि गुप्त रूप से हैश को ठीक से एन्क्रिप्ट करने के बजाय पीकेआई में किया जाना चाहिए। यह केवल उचित कार्यान्वयन के बिना RS256 लेबल के लिए HS256 लेबल को स्विच करता है।
हंस जेड।

1
उपरोक्त कोड आंशिक रूप से सुरक्षा हमले के अधीन है जिसका वर्णन उसने किया है: Cort0.com/blog/2015/03/31/… यह "के लिए असुरक्षित है" यदि कोई सर्वर RSA के साथ हस्ताक्षरित टोकन की उम्मीद कर रहा है, लेकिन वास्तव में HMAC के साथ हस्ताक्षरित टोकन प्राप्त करता है , यह सोचेंगे कि सार्वजनिक कुंजी वास्तव में एक एचएमएसी गुप्त कुंजी है। "
BennyBechDk

@Levitikon किसी भी विचारधारा मैं JSON फ़ाइल में प्राइवेट_की कैसे गूगल आपूर्ति कर सकता है? धन्यवाद
bibscy

46

मूल प्रश्न के बाद इन सभी महीनों के बीत जाने के बाद, अब यह इंगित करने योग्य है कि Microsoft ने स्वयं का एक समाधान तैयार किया है। Http://blogs.msdn.com/b/vbertocci/archive/2012/11/20/introducing-the-developer-preview-of-the-json-web-token-handler-for-the-microsoft-net देखें विवरण के लिए -framework-4-5.aspx


7
उस ब्लॉग में नगेट पैकेज मूल्यह्रास है। मेरा मानना ​​है कि नया एक nuget.org/packages/System.IdentityModel.Tokens.Jwt/…
Stan

3
@ वह लिंक महान है, लेकिन एक विशिष्ट (और अब आउट-डेटेड) संस्करण पर सेट है। यह हमेशा नवीनतम संस्करण की ओर इशारा करेगा। nuget.org/packages/System.IdentityModel.Tokens.Jwt
जेफरी हार्मन

3
उपयोग (एन्कोडिंग / डिकोडिंग, सममित / असममित) प्रदर्शित करने वाले कुछ कोड स्निपेट्स बहुत उपयोगी होंगे।
ओहद श्नाइडर

20

मैंने इसका कभी उपयोग नहीं किया है लेकिन NuGet पर JWT कार्यान्वयन है।

पैकेज: https://nuget.org/packages/JWT

स्रोत: https://github.com/johnsheehan/jwt

.NET 4.0 संगत: https://www.nuget.org/packages/jose-jwt/

आप यहां भी जा सकते हैं: https://jwt.io/ और "लाइब्रेरी" पर क्लिक करें।


12

यहाँ एक काम कर उदाहरण है:

http://zavitax.wordpress.com/2012/12/17/logging-in-with-google-service-account-in-c-jwt/

वेब पर बिखरे हुए टुकड़ों को इकट्ठा करने में कुछ समय लगा, डॉक्स अधूरे हैं ...


अंत में एक समाधान जो वास्तव में प्लग एंड प्ले था। आपको बहुत - बहुत धन्यवाद! इसने मेरे लिए काम किया।
एमके

6

यह मेरे (Google) JWT सत्यापन का .NET में कार्यान्वयन है। यह स्टैक ओवरफ्लो और गिटहब जिस्ट पर अन्य कार्यान्वयन पर आधारित है।

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace QuapiNet.Service
{
    public class JwtTokenValidation
    {
        public async Task<Dictionary<string, X509Certificate2>> FetchGoogleCertificates()
        {
            using (var http = new HttpClient())
            {
                var response = await http.GetAsync("https://www.googleapis.com/oauth2/v1/certs");

                var dictionary = await response.Content.ReadAsAsync<Dictionary<string, string>>();
                return dictionary.ToDictionary(x => x.Key, x => new X509Certificate2(Encoding.UTF8.GetBytes(x.Value)));
            }
        }

        private string CLIENT_ID = "xxx.apps.googleusercontent.com";

        public async Task<ClaimsPrincipal> ValidateToken(string idToken)
        {
            var certificates = await this.FetchGoogleCertificates();

            TokenValidationParameters tvp = new TokenValidationParameters()
            {
                ValidateActor = false, // check the profile ID

                ValidateAudience = true, // check the client ID
                ValidAudience = CLIENT_ID,

                ValidateIssuer = true, // check token came from Google
                ValidIssuers = new List<string> { "accounts.google.com", "https://accounts.google.com" },

                ValidateIssuerSigningKey = true,
                RequireSignedTokens = true,
                IssuerSigningKeys = certificates.Values.Select(x => new X509SecurityKey(x)),
                IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
                {
                    return certificates
                    .Where(x => x.Key.ToUpper() == kid.ToUpper())
                    .Select(x => new X509SecurityKey(x.Value));
                },
                ValidateLifetime = true,
                RequireExpirationTime = true,
                ClockSkew = TimeSpan.FromHours(13)
            };

            JwtSecurityTokenHandler jsth = new JwtSecurityTokenHandler();
            SecurityToken validatedToken;
            ClaimsPrincipal cp = jsth.ValidateToken(idToken, tvp, out validatedToken);

            return cp;
        }
    }
}

ध्यान दें कि, इसका उपयोग करने के लिए, आपको NuGet पैकेज का संदर्भ जोड़ना होगा System.Net.Http.Formatting.Extension। इसके बिना, कंपाइलर ReadAsAsync<>विधि को नहीं पहचान पाएगा ।


IssuerSigningKeysयदि IssuerSigningKeyResolverप्रदान किया गया है तो आपको सेट करने की आवश्यकता क्यों होगी ?
आसिफ

@AsifMD वास्तव में नहीं जानता है और फिलहाल इसका परीक्षण नहीं कर सकता है। हो सकता है कि यह जारी करने वाले के बिना काम करता है। आपको प्रमाणपत्रों का अनुरोध करने के लिए रिज़ॉल्वर कोड को बदलने की भी आवश्यकता है क्योंकि Google द्वारा प्रमाण पत्र बदलने पर आपको कुछ दिनों में एक त्रुटि मिलती है।
थॉमस

इस सरल दृष्टिकोण के लिए +1। प्रयुक्त PM> इंस्टाल-पैकेज System.IdentityModel.Tokens.Jwt -Version 5.2.4 System.IdentityModel का समर्थन करने के लिए
Karthick Jayaraman


1

स्क्रैच से कोड लिखने के बजाय मानक और प्रसिद्ध पुस्तकालयों का उपयोग करना बेहतर होगा।

  1. JWT एन्कोडिंग और डिकोडिंग के लिए JWT टोकन
  2. बाउंसी कैसल एन्क्रिप्शन और डिक्रिप्शन का समर्थन करता है, खासकर RS256 इसे यहां प्राप्त करें

इन पुस्तकालयों का उपयोग करके आप JWT टोकन उत्पन्न कर सकते हैं और नीचे RS256 का उपयोग करके इस पर हस्ताक्षर कर सकते हैं।

    public string GenerateJWTToken(string rsaPrivateKey)
    {
        var rsaParams = GetRsaParameters(rsaPrivateKey);
        var encoder = GetRS256JWTEncoder(rsaParams);

        // create the payload according to the Google's doc
        var payload = new Dictionary<string, object>
        {
            { "iss", ""},
            { "sub", "" },
            // and other key-values according to the doc
        };

        // add headers. 'alg' and 'typ' key-values are added automatically.
        var header = new Dictionary<string, object>
        {
            { "kid", "{your_private_key_id}" },
        };

        var token = encoder.Encode(header,payload, new byte[0]);

        return token;
    }

    private static IJwtEncoder GetRS256JWTEncoder(RSAParameters rsaParams)
    {
        var csp = new RSACryptoServiceProvider();
        csp.ImportParameters(rsaParams);

        var algorithm = new RS256Algorithm(csp, csp);
        var serializer = new JsonNetSerializer();
        var urlEncoder = new JwtBase64UrlEncoder();
        var encoder = new JwtEncoder(algorithm, serializer, urlEncoder);

        return encoder;
    }

    private static RSAParameters GetRsaParameters(string rsaPrivateKey)
    {
        var byteArray = Encoding.ASCII.GetBytes(rsaPrivateKey);
        using (var ms = new MemoryStream(byteArray))
        {
            using (var sr = new StreamReader(ms))
            {
                // use Bouncy Castle to convert the private key to RSA parameters
                var pemReader = new PemReader(sr);
                var keyPair = pemReader.ReadObject() as AsymmetricCipherKeyPair;
                return DotNetUtilities.ToRSAParameters(keyPair.Private as RsaPrivateCrtKeyParameters);
            }
        }
    }

PS: RSA निजी कुंजी में निम्न प्रारूप होना चाहिए:

----- BEGIN RSA निजी कुंजी ----- {base64 स्वरूपित मूल्य} ----- END RSA निजी कुंजी -----


0

जीडब्ल्यूटी के माध्यम से प्रमाणीकरण करने वाले जी सूट उपयोगकर्ताओं और समूहों तक पहुंच बनाने वाले Google सेवा खातों के लिए यहां एक और केवल- उदाहरण काम कर रहा है । यह केवल Google पुस्तकालयों के प्रतिबिंब के माध्यम से संभव था, क्योंकि इन एपीआई के Google प्रलेखन भयानक से परे हैं । MS तकनीकों में कोड करने के लिए उपयोग किए जाने वाले किसी व्यक्ति के पास यह जानने में कठिन समय होगा कि Google सेवाओं में सब कुछ एक साथ कैसे चलता है।

$iss = "<name>@<serviceaccount>.iam.gserviceaccount.com"; # The email address of the service account.
$sub = "impersonate.user@mydomain.com"; # The user to impersonate (required).
$scope = "https://www.googleapis.com/auth/admin.directory.user.readonly https://www.googleapis.com/auth/admin.directory.group.readonly";
$certPath = "D:\temp\mycertificate.p12";
$grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer";

# Auxiliary functions
function UrlSafeEncode([String] $Data) {
    return $Data.Replace("=", [String]::Empty).Replace("+", "-").Replace("/", "_");
}

function UrlSafeBase64Encode ([String] $Data) {
    return (UrlSafeEncode -Data ([Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($Data))));
}

function KeyFromCertificate([System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate) {
    $privateKeyBlob = $Certificate.PrivateKey.ExportCspBlob($true);
    $key = New-Object System.Security.Cryptography.RSACryptoServiceProvider;
    $key.ImportCspBlob($privateKeyBlob);
    return $key;
}

function CreateSignature ([Byte[]] $Data, [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate) {
    $sha256 = [System.Security.Cryptography.SHA256]::Create();
    $key = (KeyFromCertificate $Certificate);
    $assertionHash = $sha256.ComputeHash($Data);
    $sig = [Convert]::ToBase64String($key.SignHash($assertionHash, "2.16.840.1.101.3.4.2.1"));
    $sha256.Dispose();
    return $sig;
}

function CreateAssertionFromPayload ([String] $Payload, [System.Security.Cryptography.X509Certificates.X509Certificate2] $Certificate) {
    $header = @"
{"alg":"RS256","typ":"JWT"}
"@;
    $assertion = New-Object System.Text.StringBuilder;

    $assertion.Append((UrlSafeBase64Encode $header)).Append(".").Append((UrlSafeBase64Encode $Payload)) | Out-Null;
    $signature = (CreateSignature -Data ([System.Text.Encoding]::ASCII.GetBytes($assertion.ToString())) -Certificate $Certificate);
    $assertion.Append(".").Append((UrlSafeEncode $signature)) | Out-Null;
    return $assertion.ToString();
}

$baseDateTime = New-Object DateTime(1970, 1, 1, 0, 0, 0, [DateTimeKind]::Utc);
$timeInSeconds = [Math]::Truncate([DateTime]::UtcNow.Subtract($baseDateTime).TotalSeconds);

$jwtClaimSet = @"
{"scope":"$scope","email_verified":false,"iss":"$iss","sub":"$sub","aud":"https://oauth2.googleapis.com/token","exp":$($timeInSeconds + 3600),"iat":$timeInSeconds}
"@;


$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($certPath, "notasecret", [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable);
$jwt = CreateAssertionFromPayload -Payload $jwtClaimSet -Certificate $cert;


# Retrieve the authorization token.
$authRes = Invoke-WebRequest -Uri "https://oauth2.googleapis.com/token" -Method Post -ContentType "application/x-www-form-urlencoded" -UseBasicParsing -Body @"
assertion=$jwt&grant_type=$([Uri]::EscapeDataString($grantType))
"@;
$authInfo = ConvertFrom-Json -InputObject $authRes.Content;

$resUsers = Invoke-WebRequest -Uri "https://www.googleapis.com/admin/directory/v1/users?domain=<required_domain_name_dont_trust_google_documentation_on_this>" -Method Get -Headers @{
    "Authorization" = "$($authInfo.token_type) $($authInfo.access_token)"
}

$users = ConvertFrom-Json -InputObject $resUsers.Content;

$users.users | ft primaryEmail, isAdmin, suspended;

0

यहाँ वर्गों और कार्यों की सूची है:

open System
open System.Collections.Generic
open System.Linq
open System.Threading.Tasks
open Microsoft.AspNetCore.Mvc
open Microsoft.Extensions.Logging
open Microsoft.AspNetCore.Authorization
open Microsoft.AspNetCore.Authentication
open Microsoft.AspNetCore.Authentication.JwtBearer
open Microsoft.IdentityModel.Tokens
open System.IdentityModel.Tokens
open System.IdentityModel.Tokens.Jwt
open Microsoft.IdentityModel.JsonWebTokens
open System.Text
open Newtonsoft.Json
open System.Security.Claims
    let theKey = "VerySecretKeyVerySecretKeyVerySecretKey"
    let securityKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes(theKey))
    let credentials = SigningCredentials(securityKey, SecurityAlgorithms.RsaSsaPssSha256)
    let expires = DateTime.UtcNow.AddMinutes(123.0) |> Nullable
    let token = JwtSecurityToken(
                    "lahoda-pro-issuer", 
                    "lahoda-pro-audience",
                    claims = null,
                    expires =  expires,
                    signingCredentials = credentials
        )

    let tokenString = JwtSecurityTokenHandler().WriteToken(token)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.