C# 为Firebase生成JWT

C# 为Firebase生成JWT,c#,.net,firebase,jwt,C#,.net,Firebase,Jwt,我正试图在Windows上为C#.NET编写一个自定义JWT令牌生成器,用于Google Firebase。我在StackOverflow上从几个不同的地方找到了这个来源(我正试图重新发现它们,这样我就可以信任原始作者,并提出一些额外的问题),所以这有点像一个大杂烩 在大多数情况下,它似乎是有效的,但它似乎没有像预期的那样生成有效的(firebase)令牌。这可能完全是因为我不了解它应该如何工作,但如果我能从这里得到第二双世界级的优秀工程师的眼睛来指出我做错了什么,并让事情顺利进行,那就太好了

我正试图在Windows上为C#.NET编写一个自定义JWT令牌生成器,用于Google Firebase。我在StackOverflow上从几个不同的地方找到了这个来源(我正试图重新发现它们,这样我就可以信任原始作者,并提出一些额外的问题),所以这有点像一个大杂烩

在大多数情况下,它似乎是有效的,但它似乎没有像预期的那样生成有效的(firebase)令牌。这可能完全是因为我不了解它应该如何工作,但如果我能从这里得到第二双世界级的优秀工程师的眼睛来指出我做错了什么,并让事情顺利进行,那就太好了

代码如下:

// Token generator setup
using Newtonsoft.Json;
using System;
using System.IO;
using System.Web;
using System.Web.Configuration;

namespace myapp.Utils
{
    public class GoogleJsonWebToken
    {
        public static string Encode(string uid)
        {
            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(60).Subtract(utc0).TotalSeconds;

            var firebaseInfPath = HttpContext.Current.Server.MapPath(WebConfigurationManager.AppSettings["firebaseInf"]);
            var firebaseInfJsonContent = File.ReadAllText(firebaseInfPath);
            var firebaseInf = JsonConvert.DeserializeObject<dynamic>(firebaseInfJsonContent);
            var privateKey = (string)firebaseInf.private_key;

            var payload = new
            {
                iss = firebaseInf.client_email,
                scope = firebaseInf.client_email,
                aud = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
                exp = exp,
                uid = uid,
                iat = iat,
                claims = new { premium_account = true }
            };

            return myapp.Utils.JsonWebToken.Encode(payload, privateKey, JwtHashAlgorithm.RS256);
        }
    }
}
//令牌生成器设置
使用Newtonsoft.Json;
使用制度;
使用System.IO;
使用System.Web;
使用System.Web.Configuration;
命名空间myapp.Utils
{
公共类GoogleJsonWebToken
{
公共静态字符串编码(字符串uid)
{
var utc0=新的日期时间(1970年,1年,1年,0年,0年,0年,DateTimeKind.Utc);
var issueTime=DateTime.Now;
var iat=(int)issueTime.Subtract(utc0).TotalSeconds;
var exp=(int)issueTime.AddMinutes(60).减法(utc0).TotalSeconds;
var firebaseInfPath=HttpContext.Current.Server.MapPath(WebConfigurationManager.AppSettings[“firebaseInf”]);
var firebaseInfJsonContent=File.ReadAllText(firebaseInfPath);
var firebaseInf=JsonConvert.DeserializeObject(firebaseInfJsonContent);
var privateKey=(字符串)firebaseInf.private\u密钥;
var有效载荷=新
{
iss=firebaseInf.client\u电子邮件,
scope=firebaseInf.client\u电子邮件,
澳元=”https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
exp=exp,
uid=uid,
iat=iat,
索赔=新{premium_account=true}
};
返回myapp.Utils.JsonWebToken.Encode(有效负载,privateKey,JwtHashAlgorithm.RS256);
}
}
}
以下是JWT哈希算法代码:

// JsonWebToken.cs
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace myapp.Utils
{
    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)
        {
            var keyBytes = Encoding.UTF8.GetBytes(key);
            return Encode(payload, keyBytes, 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;
        }
    }
}
//JsonWebToken.cs
使用Newtonsoft.Json;
使用Newtonsoft.Json.Linq;
使用制度;
使用System.Collections.Generic;
使用System.Security.Cryptography;
使用系统文本;
命名空间myapp.Utils
{
公共枚举JWTH算法
{
RS256,
HS384,
HS512
}
公共类JsonWebToken
{
私有静态字典哈希算法;
静态JsonWebToken()
{
HashAlgorithms=新字典
{
{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);}}}
};
}
公共静态字符串编码(对象有效负载、字符串密钥、JwtHashAlgorithm)
{
var keyBytes=Encoding.UTF8.GetBytes(键);
返回编码(有效载荷、密钥字节、算法);
}
公共静态字符串编码(对象有效负载,字节[]keyBytes,JwtHashAlgorithm)
{
var segments=新列表();
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“范围”:https://www.googleapis.com/auth/prediction,“澳元”:https://accounts.google.com/o/oauth2/token“,“exp”:1328554385,“iat”:1328550785}”);
添加(Base64UrlEncode(headerBytes));
添加(Base64UrlEncode(payloadBytes));
var stringToSign=string.Join(“.”,segments.ToArray());
var bytesToSign=Encoding.UTF8.GetBytes(stringToSign);
byte[]签名=哈希算法[算法](keyBytes,bytesToSign);
添加(Base64UrlEncode(签名));
返回字符串.Join(“.”,segments.ToArray());
}
公共静态字符串解码(字符串令牌、字符串密钥)
{
返回解码(令牌、密钥、真);
}
公共静态字符串解码(字符串令牌、字符串密钥、bool验证)
{
var parts=token.Split('.');
var头=零件[0];
var有效载荷=零件[1];
byte[]crypto=Base64UrlDecode(第[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);
如果(验证)
{
var bytesToSign=Encoding.UTF8.GetBytes(string.Concat(header,“.”,payload));
var keyBytes=Encoding.UTF8.GetBytes(键);
var算法=(字符串)headerData[“alg”];
var signature=HashAlgorithms[GetHashAlgorithm(algorithm)](keyBytes,bytesToSign);
var decodedCrypto=Convert.tobase64字符串(加密);
var decodedSignature=Convert.tobase64字符串(签名);
if(decodedCrypto!=decodedSignature)
{
抛出新的ApplicationException(string.Format(“无效签名。应为{0}得到{1}”,decodedCrypto,decodedSignature));
}
}
返回payloadData.ToString();
}
私有静态JwtHashAl
public class GoogleJsonWebToken
{
    public static string Encode(string uid)
    {
        var firebaseInfPath = HttpContext.Current.Server.MapPath(WebConfigurationManager.AppSettings["firebaseInf"]);
        var firebaseInfJsonContent = File.ReadAllText(firebaseInfPath);
        var firebaseInf = JsonConvert.DeserializeObject<dynamic>(firebaseInfJsonContent);

        // NOTE: Replace this with your actual RSA public/private keypair!
        var provider = new RSACryptoServiceProvider(2048);
        var parameters = provider.ExportParameters(true);

        // Build the credentials used to sign the JWT
        var signingKey = new RsaSecurityKey(parameters);
        var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);

        // Create a collection of optional claims
        var now = DateTimeOffset.UtcNow;
        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, firebaseInf.client_email),
            new Claim(JwtRegisteredClaimNames.Iat, now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64),
            new Claim("uid", uid, ClaimValueTypes.String), 
            new Claim("premium_account", "true", ClaimValueTypes.Boolean)
        };

        // Create and sign the JWT, and write it to a string
        var jwt = new JwtSecurityToken(
            issuer: firebaseInf.client_email,
            audience: "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit",
            claims: claims,
            expires: now.AddMinutes(60).DateTime,
            signingCredentials: signingCredentials);
        return new JwtSecurityTokenHandler().WriteToken(jwt);
    }
}