Is there any JSON Web Token (JWT) example in C#?

前端 未结 9 2239
悲哀的现实
悲哀的现实 2020-11-28 01:10

I feel like I\'m taking crazy pills here. Usually there\'s always a million library and samples floating around the web for any given task. I\'m trying to implement authenti

相关标签:
9条回答
  • 2020-11-28 01:55

    Here is the list of classes and functions:

    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)
    
    0 讨论(0)
  • 2020-11-28 01:56

    Here is another REST-only working example for Google Service Accounts accessing G Suite Users and Groups, authenticating through JWT. This was only possible through reflection of Google libraries, since Google documentation of these APIs are beyond terrible. Anyone used to code in MS technologies will have a hard time figuring out how everything goes together in Google services.

    $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 讨论(0)
  • 2020-11-28 01:57

    It would be better to use standard and famous libraries instead of writing the code from scratch.

    1. JWT for encoding and decoding JWT tokens
    2. Bouncy Castle supports encryption and decryption, especially RS256 get it here

    Using these libraries you can generate a JWT token and sign it using RS256 as below.

        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: the RSA private key should have the following format:

    -----BEGIN RSA PRIVATE KEY----- {base64 formatted value} -----END RSA PRIVATE KEY-----

    0 讨论(0)
提交回复
热议问题