我正在尝试验证从C#服务器上的客户端Firebase API发送的令牌。我几乎尝试过所有事情但由于某种原因我无法获得令牌验证方法。我已经实现了从网络获取公钥的一切,但我似乎无法弄清楚RS256算法是否与JWT的第3块相等。
class FirebaseJWTAuth {
public string FirebaseId;
private HttpClient Req;
//initialize all the settings
public FirebaseJWTAuth(string firebaseId) {
firebaseId = FirebaseId;
Req = new HttpClient();
Req.BaseAddress = new Uri("https://www.googleapis.com/robot/v1/metadata/");
}
//given a token, return the user id as a string if valid, null if invalid
public async Task<string> Verify(string token) {
//following instructions from https://firebase.google.com/docs/auth/admin/verify-id-tokens
string hashChunk = token; //keep for hashing later on
hashChunk = hashChunk.Substring(0, hashChunk.LastIndexOf('.'));
token = token.Replace('-', '+').Replace('_', '/'); //sanitize for base64 on C#
string[] sections = token.Split('.'); //split into 3 sections according to JWT standards
JwtHeader header = B64Json<JwtHeader>(sections[0]);
//verify the header
if(header.alg != "RS256") {
return null;
}
//get the public keys
HttpResponseMessage res = await Req.GetAsync("x509/securetoken@system.gserviceaccount.com"); //make async
string keyDictStr = await res.Content.ReadAsStringAsync();
Dictionary<string, string> keyDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(keyDictStr);
string keyStr = null;
keyDict.TryGetValue(header.kid, out keyStr);
if(keyStr == null) {
return null;
}
//Use the RSACryptoServiceProvider to verify the hash
var rsaCrypto = CertFromPem(keyStr);
byte[] plainText = Encoding.UTF8.GetBytes(hashChunk);
byte[] hashed = SHA256Managed.Create().ComputeHash(plainText);
byte[] encrypted = rsaCrypto.Encrypt(hashed, false);
byte[] challenge = SafeB64Decode(sections[2]);
Console.WriteLine(encrypted.SequenceEqual(challenge)); //QUESTION IN THE ISSUE: expect to be true, but always false
//for debugging purposes
Console.WriteLine(Convert.ToBase64String(challenge));
Console.WriteLine(Convert.ToBase64String(encrypted));
return "didn't really get down to this part";
}
//given a string, return the RSACryptoServiceProvider which corresponds to the public key
static RSACryptoServiceProvider CertFromPem(string pemKey) {
X509Certificate2 cert = new X509Certificate2();
cert.Import(Encoding.UTF8.GetBytes(pemKey));
Console.WriteLine(cert.ToString());
return (RSACryptoServiceProvider) cert.PublicKey.Key;
}
//b64 decoding with padding to calm the C# converter
static byte[] SafeB64Decode(string encoded) {
string encodedPad = encoded + new string('=', encoded.Length % 4);
return Convert.FromBase64String(encodedPad);
}
static string SafeB64DecodeStr(string encoded) {
return Encoding.UTF8.GetString(SafeB64Decode(encoded));
}
static T B64Json<T> (string encoded) {
string decoded = SafeB64DecodeStr(encoded);
Console.WriteLine(decoded);
return JsonConvert.DeserializeObject<T>(decoded);
}
//structs representing the first 2 chunks of a JWT
private struct JwtHeader {
public string alg;
public string kid;
}
private struct JwtPayload {
public long exp;
public long iat;
public string aud;
public string iss;
public string sub;
}
}
我知道这很粗糙(没有验证有效载荷)但我无法匹配令牌。我只是一名初级开发人员,但我已经尝试添加额外的评论和空格,以使您的阅读更轻松。
我已经在我的个人令牌上测试了这个代码,由于显而易见的原因我没有在这里上传。但是,如果您想测试代码,请转到the firebase-auth demo site并登录。然后,打开DevTools控制台并键入firebase.auth().currentUser.getIdToken(true).then(console.log)
。您的令牌将在控制台中弹出。
提前致谢。
答案 0 :(得分:1)
/捂脸
我忘记了RSA签名!= RSA加密,显然C#有两个不同的库。魔术课是RSAPKCS1SignatureDeformatter
。
答案是使用RSAPKCS1SignatureDeformatter.VerifySignature
方法而不是基于加密的RSACryptoServiceProvider