C#Firebase JWT令牌验证

时间:2018-01-03 07:38:16

标签: c# firebase firebase-authentication jwt openid-connect

我正在尝试验证从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)。您的令牌将在控制台中弹出。

提前致谢。

1 个答案:

答案 0 :(得分:1)

/捂脸

我忘记了RSA签名!= RSA加密,显然C#有两个不同的库。魔术课是RSAPKCS1SignatureDeformatter

答案是使用RSAPKCS1SignatureDeformatter.VerifySignature方法而不是基于加密的RSACryptoServiceProvider