c# RSA 验证 JWT Token 笔记

 

从认证服务器获取公匙
http://localhost:9000/oauth2/jwks获取公匙,返回数据如下:

 1 {
 2     "keys":[
 3         {
 4             "kty":"RSA",        // 使用RSA算法
 5             "use":"sig",        // 类型为签名
 6             "kid":"03ece87289fe54eee377a1380b0e49a2",        // 与token有关
 7             "e":"AQAB",        // RSA算法中的e,base64url(注意与base64的区别)
 8             "n":"3JjRAiTwPOCDrWFcsHDovYDuBaqWuSSLxSp27Bc6rDnsepkw-yISVRlM4C-MwgL4wiDZnnQBv9-zZi8on0_L1L7UuuN5HFvvd01xApIWGdhJwQl2ijltwOKG8osxQQXtkDLOPOxhV-Fs5K9KQWW0AXdkPUXYulP3854MXmNWOqtC3fhMg78MUZAAKGrj67qk0tRdtxXIgF_Mqn5cMsEq8X7xWcbcPwWuc_NsfSxs43Y-p-7THMrv8vRzIQFUO27WGMZP9cTlXx0_OuhRaNnZUJoamp89-j-XMBfYzsCFyD_a3dBdUMvNtvgDq4IylA1oyT1F_J8xi4SPHwtNvnWxLQ",        // RSA算法中的n,base64url(注意与base64的区别)
 9             "alg":"RS256"        // 哈希算法名称,使用“SHA256”哈希算法。
10         }
11     ]
12 }

 

从认证服务器获取共享Token
eyJhbGciOiJSUzI1NiIsImtpZCI6IjAzZWNlODcyODlmZTU0ZWVlMzc3YTEzODBiMGU0OWEyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NDMxOTkxNTksImV4cCI6MTU0MzIwMjc1OSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9yZXNvdXJjZXMiLCJhcGkxIl0sImNsaWVudF9pZCI6Im12YyIsInN1YiI6IjQxMjciLCJhdXRoX3RpbWUiOjE1NDMxOTkxNTIsImlkcCI6ImxvY2FsIiwibmFtZSI6IjE4ODg4ODg4ODg3Iiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsImFwaTEiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsicHdkIl19.vGn_PiK_aomcVfGBGX0QH6-1ySd5Viv4ZHDA1s2xZ5HstgS07WawQpxbSG83aLAig_NpKiTR3aNZKTQ40s4PgUJLJ7x1WTaJUxJApWYi9oLsPxK6p7sxi3OPPHGWIKpka4ht_p_9SY13oToWvBECkhU6F2Oeo3iEKWmGy4n3aOLHuQe4rNEVrKnVD9DXLUbwK8dgBeRj7DsKyrVtjMzMHG9tpeepzt_EktbRKG-rjezgGRb-421b8b3SNDBT9g_sTjV6VvxHdXS3zHAIU1nbxD66cEFVfoo7F63643RTEtjNJRB1mlicbJdknzoF-d-QFIh_xtTkTZjzoa7ZL7DrQQ
 

Token有三部分组成
第三部分 = 第一部分 + “.” + 第二部分

 

使用RSA验证共享token  方式一

 1 static void Main(string[] args)
 2         {
 3             RSAParameters param = new RSAParameters()
 4             {
 5                 Exponent = FromBase64Url("AQAB"),
 6                 Modulus = FromBase64Url("3JjRAiTwPOCDrWFcsHDovYDuBaqWuSSLxSp27Bc6rDnsepkw-yISVRlM4C-MwgL4wiDZnnQBv9-zZi8on0_L1L7UuuN5HFvvd01xApIWGdhJwQl2ijltwOKG8osxQQXtkDLOPOxhV-Fs5K9KQWW0AXdkPUXYulP3854MXmNWOqtC3fhMg78MUZAAKGrj67qk0tRdtxXIgF_Mqn5cMsEq8X7xWcbcPwWuc_NsfSxs43Y-p-7THMrv8vRzIQFUO27WGMZP9cTlXx0_OuhRaNnZUJoamp89-j-XMBfYzsCFyD_a3dBdUMvNtvgDq4IylA1oyT1F_J8xi4SPHwtNvnWxLQ"),
 7             };
 8 
 9             RSA rSA = RSA.Create(param);
10 
11             var ispass = rSA.VerifyData(UTF8Encoding.UTF8.GetBytes("eyJhbGciOiJSUzI1NiIsImtpZCI6IjAzZWNlODcyODlmZTU0ZWVlMzc3YTEzODBiMGU0OWEyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1NDMxOTkxNTksImV4cCI6MTU0MzIwMjc1OSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NTAwMC9yZXNvdXJjZXMiLCJhcGkxIl0sImNsaWVudF9pZCI6Im12YyIsInN1YiI6IjQxMjciLCJhdXRoX3RpbWUiOjE1NDMxOTkxNTIsImlkcCI6ImxvY2FsIiwibmFtZSI6IjE4ODg4ODg4ODg3Iiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsImFwaTEiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsicHdkIl19"),
12                 FromBase64Url("vGn_PiK_aomcVfGBGX0QH6-1ySd5Viv4ZHDA1s2xZ5HstgS07WawQpxbSG83aLAig_NpKiTR3aNZKTQ40s4PgUJLJ7x1WTaJUxJApWYi9oLsPxK6p7sxi3OPPHGWIKpka4ht_p_9SY13oToWvBECkhU6F2Oeo3iEKWmGy4n3aOLHuQe4rNEVrKnVD9DXLUbwK8dgBeRj7DsKyrVtjMzMHG9tpeepzt_EktbRKG-rjezgGRb-421b8b3SNDBT9g_sTjV6VvxHdXS3zHAIU1nbxD66cEFVfoo7F63643RTEtjNJRB1mlicbJdknzoF-d-QFIh_xtTkTZjzoa7ZL7DrQQ"),
13                 HashAlgorithmName.SHA256,
14                 RSASignaturePadding.Pkcs1);
15         }
16 
17         public static byte[] FromBase64Url(string base64Url)
18         {
19             string padded = base64Url.Length % 4 == 0
20               ? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
21             string base64 = padded.Replace("_", "/")
22                        .Replace("-", "+");
23 
24             return Convert.FromBase64String(base64);
25         }
26  

使用RSA验证共享token  方式二

 1  string tokenStr = "eyJraWQiOiIxZTlnZGs3IiwiYWxnIjoiUlMyNTYifQ.ewogImlzcyI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZfV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5NzAsCiAiY19oYXNoIjogIkxEa3RLZG9RYWszUGswY25YeENsdEEiCn0.XW6uhdrkBgcGx6zVIrCiROpWURs-4goO1sKA4m9jhJIImiGg5muPUcNegx6sSv43c5DSn37sxCRrDZZm4ZPBKKgtYASMcE20SDgvYJdJS0cyuFw7Ijp_7WnIjcrl6B5cmoM6ylCvsLMwkoQAxVublMwH10oAxjzD6NEFsu9nipkszWhsPePf_rM4eMpkmCbTzume-fzZIi5VjdWGGEmzTg32h3jiex-r5WTHbj-u5HL7u_KP3rmbdYNzlzd1xWRYTUs4E8nOTgzAUwvwXkIQhOh5TPcSMBYy6X3E7-_gr9Ue6n4ND7hTFhtjYs3cjNKIA08qm5cpVYFMFMG6PkhzLQ";
 2   string[] tokenParts = tokenStr.Split('.');
 3 
 4   RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
 5   rsa.ImportParameters(
 6     new RSAParameters() {
 7       Modulus = FromBase64Url("w7Zdfmece8iaB0kiTY8pCtiBtzbptJmP28nSWwtdjRu0f2GFpajvWE4VhfJAjEsOcwYzay7XGN0b-X84BfC8hmCTOj2b2eHT7NsZegFPKRUQzJ9wW8ipn_aDJWMGDuB1XyqT1E7DYqjUCEOD1b4FLpy_xPn6oV_TYOfQ9fZdbE5HGxJUzekuGcOKqOQ8M7wfYHhHHLxGpQVgL0apWuP2gDDOdTtpuld4D2LK1MZK99s9gaSjRHE8JDb1Z4IGhEcEyzkxswVdPndUWzfvWBBWXWxtSUvQGBRkuy1BHOa4sP6FKjWEeeF7gm7UMs2Nm2QUgNZw6xvEDGaLk4KASdIxRQ"),
 8       Exponent = FromBase64Url("AQAB")
 9     });
10 
11   SHA256 sha256 = SHA256.Create();
12   byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(tokenParts[0] + '.' + tokenParts[1]));
13 
14   RSAPKCS1SignatureDeformatter rsaDeformatter = new RSAPKCS1SignatureDeformatter(rsa);
15   rsaDeformatter.SetHashAlgorithm("SHA256");
16   if (rsaDeformatter.VerifySignature(hash, FromBase64Url(tokenParts[2])))
17     MessageBox.Show("Signature is verified");
18 
19 //...
20   static byte[] FromBase64Url(string base64Url)
21   {
22     string padded = base64Url.Length % 4 == 0
23         ? base64Url : base64Url + "====".Substring(base64Url.Length % 4);
24     string base64 = padded.Replace("_", "/")
25                           .Replace("-", "+");
26     return Convert.FromBase64String(base64);
27   }

 

 

验证token是否过期
Token第二部分base64解码如下
{
“nbf”:1543199159,
“exp”:1543202759,
“iss”:“http://localhost:5000”,
“aud”:[“http://localhost:5000/resources”,“api1”],
“client_id”:“mvc”,
“sub”:“4127”,
“auth_time”:1543199152,
“idp”:“local”,
“name”:“18888888887”,
“scope”:[“openid”,“profile”,“api1”,“offline_access”],"
amr":[“pwd”]
}
其中exp保存过期时间,为unix时间戳,示例为2018/11/26 11:25:59过期

 

解密token中间部分时,报

Base-64 字符数组或字符串的长度无效等问题解决方案

string dummyData = Base64ToString(tokenParts[1]);
byte[] bytes = Convert.FromBase64String(dummyData);
string decode= Encoding.UTF8.GetString(bytes);

     /// <summary>
        /// 对Base64字符串进行特殊字符替换,并进行PadRight操作,解决问题
        /// </summary>
        /// <param name="base64"></param>
        /// <returns></returns>
        static string Base64ToString(string base64)
        {
            string dummyData = base64.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
            if (dummyData.Length % 4 > 0)
            {
                dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
            }
            return dummyData; 
        }

 


 

   /// <summary>
    /// 将Java安全的base64字符串转换为byte数组
    /// </summary>
    /// <param name="convert"></param>
    /// <param name="javaURLSafeString"></param>
    /// <returns></returns>
    public static byte[] FromBase64StringURLSafe(string javaURLSafeString)
    {
        javaURLSafeString = javaURLSafeString.Replace("-", "+").Replace("_", "/");
        var base64 = Encoding.ASCII.GetBytes(javaURLSafeString);
        var padding = base64.Length * 3 % 4;//(base64.Length*6 % 8)/2
        if (padding != 0)
        {
            javaURLSafeString = javaURLSafeString.PadRight(javaURLSafeString.Length + padding, '=');
        }
        return Convert.FromBase64String(javaURLSafeString);
    }

 

posted @ 2022-06-10 11:45  qingjiawen  阅读(468)  评论(0编辑  收藏  举报