生成火力地堡一个JWT [英] Generating a JWT for Firebase
问题描述
我试着写C#.NET自定义JWT令牌生成器的Windows在谷歌火力地堡中使用。我从这里StackOverflow的几个不同的地方举起源(我试图重新发现他们,所以我可以相信原作者,问了一些额外的问题也是如此),所以这是一个有点在一起大杂烩。
有关它似乎工作,但它似乎并没有产生有效的(火力)令牌作为预期的大部分。这可能是完全是由于我缺乏的是如何应该摆在首位的工作的理解,但如果我可以从这里得到的第二对世界级的,真棒,工程师眼中的指出我做错了什么,使事情的工作,这将是很好
下面的代码:
/ /令牌生成器设置
使用Newtonsoft.Json;
使用系统;
:使用System.IO;
使用的System.Web;
使用System.Web.Configuration;
命名空间myapp.Utils
{
公共类GoogleJsonWebToken
{
公共静态字符串编码(字符串UID)
{
变种utc0 =新日期时间(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<动态>(firebaseInfJsonContent);
VAR privateKey =(字符串)firebaseInf.private_key;
无功负载=新的
{
ISS = firebaseInf.client_email,
=范围firebaseInf.client_email,
AUD =https://开头identitytoolkit。 googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit,
EXP = EXP,
UID = UID,
IAT = IAT,
=索赔新的{premium_account =真}
};
返回myapp.Utils.JsonWebToken.Encode(有效载荷,privateKey,JwtHashAlgorithm.RS256);
}
}
}
这里是智威汤逊的哈希算法代码
使用Newtonsoft.Json // JsonWebToken.cs
;
使用Newtonsoft.Json.Linq;
使用系统;
使用System.Collections.Generic;
使用System.Security.Cryptography;
使用System.Text;
命名空间myapp.Utils
{
公共枚举JwtHashAlgorithm
{
RS256,
HS384,
HS512
}
公共类JsonWebToken
{
私有静态字典< JwtHashAlgorithm,Func键<字节[],字节[],字节[]>> HashAlgorithms;
静态JsonWebToken()
{
HashAlgorithms =新词典< JwtHashAlgorithm,Func键<字节[],字节[],字节[]>>
{
{JwtHashAlgorithm.RS256,(键,值)=> {使用(VAR SHA =新HMACSHA256(键)){返回sha.ComputeHash(值); }}},
{JwtHashAlgorithm.HS384,(键,值)=> {使用(VAR SHA =新HMACSHA384(键)){返回sha.ComputeHash(值); }}},
{JwtHashAlgorithm.HS512,(键,值)=> {使用(VAR SHA =新HMACSHA512(键)){返回sha.ComputeHash(值); }}}
};
}
公共静态字符串编码(对象的有效载荷,串键,JwtHashAlgorithm算法)
{
VAR keyBytes = Encoding.UTF8.GetBytes(键);
返回编码(有效载荷,keyBytes,算法);
}
公共静态字符串编码(对象的有效载荷,字节[] keyBytes,JwtHashAlgorithm算法)
{
变种段=新的List<串>();
变种头= {新ALG = algorithm.ToString(),典型值=智威汤逊};
字节[] = headerBytes Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(头,Formatting.None));
字节[] = payloadBytes Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(有效载荷,Formatting.None));
//字节[] = payloadBytes Encoding.UTF8.GetBytes(@{ISS:761326798069-r5mljlln1rd4lrbhg75efgigp36m78j5@developer.gserviceaccount.com,范围: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);
字节[] =签名HashAlgorithms [算法(keyBytes,bytesToSign);
segments.Add(Base64UrlEncode(签字));
返回的string.join(,segments.ToArray()。);
}
公共静态字符串解码(字符串标记,字符串键)
{
返回解码(令牌,钥匙,真正的);
}
公共静态字符串解码(字符串标记,串键,布尔确认)
{
VAR部分= token.Split('。');
VAR头=零件[0];
无功负载=部分[1];
字节[] =密码Base64UrlDecode(部件[2]);
VAR headerJson = Encoding.UTF8.GetString(Base64UrlDecode(头));
VAR headerData = JObject.Parse(headerJson);
变种payloadJson = Encoding.UTF8.GetString(Base64UrlDecode(有效负载));
VAR payloadData = JObject.Parse(payloadJson);
如果(验证)
{
VAR bytesToSign = Encoding.UTF8.GetBytes(string.Concat(头,有效载荷)。);
VAR keyBytes = Encoding.UTF8.GetBytes(键);
变种算法=(字符串)headerData [ALG];
VAR签名= HashAlgorithms [GetHashAlgorithm(算法)](keyBytes,bytesToSign);
VAR decodedCrypto = Convert.ToBase64String(加密);
VAR decodedSignature = Convert.ToBase64String(签字);
如果(decodedCrypto!= decodedSignature)
{
抛出新ApplicationException的(的String.Format(无效的签名。预计{0}有{1},decodedCrypto,decodedSignature) );
}
}
返回payloadData.ToString();
}
私有静态JwtHashAlgorithm GetHashAlgorithm(字符串变换算法)
{
开关(算法)
{
案RS256:返回JwtHashAlgorithm .RS256;
案HS384:返回JwtHashAlgorithm.HS384;
案HS512:返回JwtHashAlgorithm.HS512;
默认:抛出新的InvalidOperationException异常(不支持算法。);
}
}
//从智威汤逊规范
私人静态字符串Base64UrlEncode(字节[]输入)
{
无功输出=转换.ToBase64String(输入);
输出= output.Split('=')[0]; //删除任何尾随'=的
输出= output.Replace('+',' - '); //编码
输出的字符62 = output.Replace('/','_'); //编码
返回输出的字符第63;
}
//从智威汤逊规范
私人静态的byte [] Base64UrlDecode(字符串输入)
{
无功输出=输入;
输出= output.Replace(' - ','+'); //编码
输出的字符62 = output.Replace('_','/'); //编码
开关(output.Length%4)//垫的第63届炭尾随'=的
{
的情况下0:打破; //在这种情况下,
2的情况下没有垫字符:输出+ ===;打破; //两个垫字符
案例3:输出+ ==;打破; //一垫字符
默认:抛出新的System.Exception(非法base64url串!);
}
无功转换= Convert.FromBase64String(输出); //标准Base64编码解码器
返回转换;
}
}
}
为什么令牌被拒绝的原因是因为它使用了错误的算法进行签名:您正在使用HMAC-SHA256(对称密钥加密算法),而正确的标记使用RSA-SHA256(非对称或公钥/私钥密钥算法)。 ALG:您可以在例如令牌的头看到这个RS256
我想建议使用类 System.IdentityModel.Tokens.Jwt
来简化代码很多:
公共类GoogleJsonWebToken
{
公共静态字符串编码(字符串UID)
{
VAR firebaseInfPath = HttpContext.Current.Server.MapPath(WebConfigurationManager.AppSettings [ firebaseInf]);
VAR firebaseInfJsonContent = File.ReadAllText(firebaseInfPath);
VAR firebaseInf = JsonConvert.DeserializeObject<动态>(firebaseInfJsonContent);
//注意:您的实际RSA公钥/私钥对替换此!
VAR提供商=新的RSACryptoServiceProvider(2048);
VAR参数= provider.ExportParameters(真);
//建立用于签署JWT
变种signingKey =新RsaSecurityKey(参数)的资格证书;
变种signingCredentials =新SigningCredentials(signingKey,SecurityAlgorithms.RsaSha256);
//创建可选的索赔
变种集合现在= DateTimeOffset.UtcNow;
变种索赔=新[]
{
新的索赔(JwtRegisteredClaimNames.Sub,firebaseInf.client_email),
新的索赔(JwtRegisteredClaimNames.Iat,now.ToUnixTimeSeconds()的ToString( ),ClaimValueTypes.Integer64),
新的索赔(UID,UID,ClaimValueTypes.String),
新的索赔(premium_account,真,ClaimValueTypes.Boolean)
};
//创建并签署了智威汤逊,并把它写入字符串
变种智威汤逊=新JwtSecurityToken(
发行人:firebaseInf.client_email,
观众:HTTPS ://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit,
声称:索赔,
到期:now.AddMinutes(60).DateTime,
signingCredentials:signingCredentials );
返回新JwtSecurityTokenHandler()WriteToken(JWT)。
}
}
使用虚拟按键,这个代码创建一个令牌匹配你的榜样令牌的报头和有效载荷的模式:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmaXJlYmFzZS1hdXRoLXRva2VuQHNheWVyLWNoYXQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJpYXQiOjE0Njc4MzgwODUsInVpZCI6IjUyMTQiLCJwcmVtaXVtX2FjY291bnQiOnRydWUsImV4cCI6MTQ2Nzg2Njg4NSwiaXNzIjoiZmlyZWJhc2UtYXV0aC10b2tlbkBzYXllci1jaGF0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eXRvb2xraXQuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmlkZW50aXR5LmlkZW50aXR5dG9vbGtpdC52MS5JZGVudGl0eVRvb2xraXQifQ.yMSGRpm4r3mPqiA9KnKVILVb8jT2Vbqcy4gvoLIugXnzLlw45F-GepAaBJK-J0-EN34WLsiLiLCRDJwW15TMasbrB1ZX3H4zd1by2GjZ1VmcUL8LITglICvs8CXkamjPjHeQUc4q - jhveKmTby8WHsH4b-HeiYoVl8JhxrAF13buNbiTq66dfvkl9q6mnyuKS_oyFB6_9WNphzHRa2BdJ51olq4qQsUNZ- nzOe4moHHjxpEzQfQpIe-QMJHdqojp9ukOW5eTMhFkQRPs3Bme4jpxIHPknC9j8YRvx_i0FbEJ8qAY2ujWcq80aC6YBKox55iP-AwfX_mEV7Tz14PBQ
块引用>
您应该能够在你的公钥/私钥对删除并用上面的代码来生成有效JWTs
I'm trying to write a custom JWT token generator for C# .NET on Windows for use in Google Firebase. The source I lifted from a few different places here on StackOverflow (I'm trying to re-discover them so I can credit the original authors and ask a couple of additional questions too), so it's a bit of a hodge-podge together.
For the most part it seems to work, but it doesn't seem to generate a valid (firebase) token as expected. This could be entirely due to my lack of understanding of how it should work in the first place, but if I could get a second pair of world-class-awesome-engineers eyes from here to point out what I did wrong and make things work, it would be nice.
Here's the code:
// 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); } } }
Here's the JWT hash algorithm code:
// 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; } } }
解决方案The reason why the token is being rejected is because it's signed using the wrong algorithm: you're using HMAC-SHA256 (a symmetric key encryption algorithm) while the correct token uses RSA-SHA256 (an asymmetric or public/private key algorithm). You can see this in the header of your example token:
"alg": "RS256"
I'd suggest using the classes in
System.IdentityModel.Tokens.Jwt
to simplify your code a lot: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); } }
Using a dummy key, this code creates a token that matches the header and payload schema of your example token:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmaXJlYmFzZS1hdXRoLXRva2VuQHNheWVyLWNoYXQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJpYXQiOjE0Njc4MzgwODUsInVpZCI6IjUyMTQiLCJwcmVtaXVtX2FjY291bnQiOnRydWUsImV4cCI6MTQ2Nzg2Njg4NSwiaXNzIjoiZmlyZWJhc2UtYXV0aC10b2tlbkBzYXllci1jaGF0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eXRvb2xraXQuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmlkZW50aXR5LmlkZW50aXR5dG9vbGtpdC52MS5JZGVudGl0eVRvb2xraXQifQ.yMSGRpm4r3mPqiA9KnKVILVb8jT2Vbqcy4gvoLIugXnzLlw45F-GepAaBJK-j0-EN34WLsiLiLCRDJwW15TMasbrB1ZX3H4zd1by2GjZ1VmcUL8LITglICvs8CXkamjPjHeQUc4q--jhveKmTby8WHsH4b-HeiYoVl8JhxrAF13buNbiTq66dfvkl9q6mnyuKS_oyFB6_9WNphzHRa2BdJ51olq4qQsUNZ-nzOe4moHHjxpEzQfQpIe-QMJHdqojp9ukOW5eTMhFkQRPs3Bme4jpxIHPknC9j8YRvx_i0FbEJ8qAY2ujWcq80aC6YBKox55iP-AwfX_mEV7Tz14PBQ
You should be able to drop in your public/private key pair and use the above code to generate valid JWTs.
这篇关于生成火力地堡一个JWT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!