97

Вопрос по c# – Есть ли какой-нибудь пример JSON Web Token (JWT) в C #?

Я чувствую, что принимаю сумасшедшие таблетки здесь. Обычно в сети всегда есть миллион библиотек и примеров для любой конкретной задачи. Я пытаюсь выполнить аутентификацию с помощью "Сервисной учетной записи Google". с помощью веб-токенов JSON (JWT), как описаноВот.

Однако есть только клиентские библиотеки в PHP, Python и Java. Даже при поиске примеров JWT вне аутентификации Google в концепции JWT присутствуют только крикеты и шашки. Это действительно такая новая и, возможно, запатентованная система Google?

Пример java, который мне ближе всего удается интерпретировать, выглядит довольно интенсивно и пугающе. В C # должно быть что-то, с чего я мог бы начать, по крайней мере. Любая помощь с этим была бы великолепна!

  • @AsifMD Не знаю и не могу проверить это в данный момент. Может быть, это работает без установки IssuerSigningKey. Вам также необходимо изменить код распознавателя для запроса сертификатов, поскольку в противном случае вы получите сообщение об ошибке через несколько дней, когда Google изменит свои сертификаты.

    от
  • Некоторые фрагменты кода, демонстрирующие использование (кодирование / декодирование, симметричный / асимметричный), были бы очень полезны.

    от
  • Похоже, что John не поддерживает алгоритмы шифрования RS (флаг alg), но эта версия поддерживает.

    от
  • +1 за этот самый простой подход. Подержанные PM & gt; Install-Package System.IdentityModel.Tokens.Jwt - Версия 5.2.4 для поддержки System.IdentityModel.

    от
  • @ Считается, что эта ссылка великолепна, но она настроена на конкретную (и теперь уже устаревшую) версию. Это всегда будет указывать на последнюю версию.nuget.org/packages/System.IdentityModel.Tokens.Jwt

    от
  • пакет nuget в этом блоге является устаревшим. Я считаю, что новыйnuget.org/packages/System.IdentityModel.Tokens.Jwt/…

    от
  • Зачем вам нужно установитьIssuerSigningKeys еслиIssuerSigningKeyResolver предоставлен?

    от
  • Приведенный выше код частично подвергается описанной ей атаке безопасности:auth0.com/blog/2015/03/31/… Он уязвим для & # x201C; если сервер ожидает токен, подписанный с помощью RSA, но фактически получает токен, подписанный с помощью HMAC, он будет считать, что открытый ключ на самом деле является секретным ключом HMAC. & # X201D;

    от
  • Наконец, решение, которое было действительно подключи и играй. Спасибо вам большое! Это сработало для меня.

    от
  • @Levitikon Любая идея, как я могу декодировать private_key, который Google предоставляет в файл JSON? Спасибо

    от
  • Эта версия НЕ поддерживает алгоритм подписи RS256 правильно! Он только хэширует ввод с байтами ключа в качестве секрета, а не шифрует хэш должным образом, как это должно быть сделано в PKI. Он просто переключает метку HS256 на метку RS256 без надлежащей реализации.

    от
  • Исходная реализация, похоже, представляет собой библиотеку Джона Шиханса:github.com/johnsheehan/jwt

    от
  • У Питера есть твой ответ. JWT является относительно новым форматом токенов, поэтому образцы все еще немного трудно найти, но он растет очень быстро, потому что JWT - очень необходимая замена SWT. Microsoft поддерживает формат токенов, например, API Live Connect используют JWT.

    от Andrew Lavers
  • Это как-то связано с App Engine?

    от Nick Johnson
  • Возможный дубликатValidating Google OpenID Connect JWT ID Token

    от Thomas
6 ответов
  • 3

    Взгляни на

    Клиентская библиотека Google для .NET.

  • 5

    Это моя реализация валидации JWT (Google) в .NET.

    Он основан на других реализациях на основе стека переполнения и GitHub.

    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<> метод.

  • 11

    Вот рабочий пример:

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

    Потребовалось некоторое время, чтобы собрать фрагменты, разбросанные по сети, документы довольно неполные ...

  • 45

    После того

    как все эти месяцы прошли после первоначального вопроса, теперь стоит указать, что 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- каркасного 4-5.aspx для деталей.

  • 62

    Спасибо всем. Я нашел базовую реализацию веб-токена Json и расширил ег

    о, добавив Google. Я до сих пор не получил его полностью отработанным, но там 97%. Этот проект потерял свою популярность, так что, надеюсь, это поможет кому-то еще получить хороший старт:

    Замечания: Изменения, которые я внес в базовую реализацию (не помню, где я ее нашел):

    Changed HS256 -> RS256 Swapped the JWT and alg order in the header. Not sure who got it wrong, Google or the spec, but google takes it the way It is below according to their docs.
    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":"[email protected]account.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);
        }
    }
    

  • 19

    Я никогда не использовал его, но в 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/ и нажмите «библиотеки».