我正在尝试为 Windows 上的 C# .NET 编写自定义 JWT 令牌生成器,以便在 Google Firebase 中使用.我从 StackOverflow 上的几个不同地方提取的源代码(我正在尝试重新发现它们,以便我可以信任原作者并提出一些额外的问题),所以它有点像大杂烩.
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.
在大多数情况下,它似乎可以工作,但它似乎没有按预期生成有效的(firebase)令牌.这可能完全是因为我最初对它应该如何工作缺乏了解,但如果我能从这里获得第二双世界级的工程师的眼睛,指出我做错了什么并让事情顺利进行,那会很好.
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.
代码如下:
// 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);
}
}
}
JWT 哈希算法代码如下:
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;
}
}
}
令牌被拒绝的原因是因为它使用了错误的算法签名:你使用的是 HMAC-SHA256(一种对称密钥加密算法),而正确的令牌使用 RSA-SHA256(一种非对称或公钥/私钥算法).您可以在示例令牌的标题中看到这一点:"alg": "RS256"
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"
我建议使用 System.IdentityModel.Tokens.Jwt 中的类来大大简化您的代码:
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
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmaXJlYmFzZS1hdXRoLXRva2VuQHNheWVyLWNoYXQuaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJpYXQiOjE0Njc4MzgwODUsInVpZCI6IjUyMTQiLCJwcmVtaXVtX2FjY291bnQiOnRydWUsImV4cCI6MTQ2Nzg2Njg4NSwiaXNzIjoiZmlyZWJhc2UtYXV0aC10b2tlbkBzYXllci1jaGF0LmlhbS5nc2VydmljZWFjY291bnQuY29tIiwiYXVkIjoiaHR0cHM6Ly9pZGVudGl0eXRvb2xraXQuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmlkZW50aXR5LmlkZW50aXR5dG9vbGtpdC52MS5JZGVudGl0eVRvb2xraXQifQ.yMSGRpm4r3mPqiA9KnKVILVb8jT2Vbqcy4gvoLIugXnzLlw45F-GepAaBJK-j0-EN34WLsiLiLCRDJwW15TMasbrB1ZX3H4zd1by2GjZ1VmcUL8LITglICvs8CXkamjPjHeQUc4q--jhveKmTby8WHsH4b-HeiYoVl8JhxrAF13buNbiTq66dfvkl9q6mnyuKS_oyFB6_9WNphzHRa2BdJ51olq4qQsUNZ-nzOe4moHHjxpEzQfQpIe-QMJHdqojp9ukOW5eTMhFkQRPs3Bme4jpxIHPknC9j8YRvx_i0FbEJ8qAY2ujWcq80aC6YBKox55iP-AwfX_mEV7Tz14PBQ
您应该能够放入您的公钥/私钥对并使用上述代码生成有效的 JWT.
You should be able to drop in your public/private key pair and use the above code to generate valid JWTs.
这篇关于为 Firebase 生成 JWT的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持html5模板网!
车牌检测有哪些好的算法?What are good algorithms for vehicle license plate detection?(车牌检测有哪些好的算法?)
Unity中图像的onClick事件onClick event for Image in Unity(Unity中图像的onClick事件)
运行总 C#Running Total C#(运行总 C#)
单击带有 JAvascript.ASP.NET C# 的超链接时删除目录Deleting a directory when clicked on a hyperlink with JAvascript.ASP.NET C#(单击带有 JAvascript.ASP.NET C# 的超链接时删除目录)
asp.net listview 在单击时突出显示行asp.net listview highlight row on click(asp.net listview 在单击时突出显示行)
从函数调用按钮 OnClickCalling A Button OnClick from a function(从函数调用按钮 OnClick)