Sfoglia il codice sorgente

调整JWT对RSA的认证方式

CrazyIter 5 anni fa
parent
commit
1471cdc3b0

+ 3 - 0
HiTeachCE/Controllers/LoginController.cs

@@ -405,6 +405,9 @@ namespace HiTeachCE.Controllers
                             claimModel.Claims.Add(new Claim(JwtClaimTypes.ClientId, activationCodes[0].clientId));
                             claimModel.Claims.Add(new Claim("org", orgCode));
                             JwtResponse jwtResponse = JwtHelper.IssueJWT(claimModel);
+                            //string sha = ShaHashHelper.GetSHA1(jwtResponse.Access_token);
+                            //RedisHelper.HSet("jwt:"+sha,sha,jwtResponse.Access_token );
+                            //RedisHelper.Expire("jwt:" + sha, 86400);
                             return jwtResponse;
                         }
                         else

+ 5 - 3
HiTeachCE/Extension/BlackListJwtSecurityTokenHandler.cs

@@ -6,6 +6,7 @@ using System.Linq;
 using System.Security.Claims;
 using System.Threading.Tasks;
 using TEAMModelOS.SDK.Context.Exception;
+using TEAMModelOS.SDK.Helper.Security.ShaHash;
 
 namespace HiTeachCE.Extension
 {
@@ -23,14 +24,15 @@ namespace HiTeachCE.Extension
         {
             var claimsPrincipal = base.ValidateToken(token, validationParameters, out validatedToken);
 
-             //解析ClaimsPrincipal取出UserId、Iat和Jti
+            //解析ClaimsPrincipal取出UserId、Iat和Jti
             //具体的验证步骤有两个:
             //- 到Redis查找该用户的Token失效时间,如果当前Token的颁发时间在此之前就是无效的;
             //- 到Redis的黑名单里判断是否存在该Token; 
             //通过Redis验证Token
-            if (!RedisHelper.Exists("key"))
+            string sha = ShaHashHelper.GetSHA1(token);
+            if (RedisHelper.Exists("jwt:"+sha))
             {
-                throw new BizException("登录凭证失效!",401);
+                throw new BizException("登录失效!",401);
             }
             return claimsPrincipal;
         }

+ 1 - 1
HiTeachCE/Extension/JwtAuth.cs

@@ -21,7 +21,7 @@ namespace HiTeachCE.Extension
             // var creds = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"]), SecurityAlgorithms.RsaSha256Signature);
             //var creds = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"]));
             string path = BaseConfigModel.ContentRootPath;
-            RsaSecurityKey creds = new RsaSecurityKey(RsaHelper.LoadCertificateFile(path + "/public.pem"));
+            SecurityKey creds = RsaHelper.GenerateValidationKey(path + "/public.pem");
             //RsaSecurityKey creds = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"])), SecurityAlgorithms.RsaSha256Signature);
             // 令牌验证参数
             var tokenValidationParameters = new TokenValidationParameters

+ 1 - 3
HiTeachCE/Extension/JwtHelper.cs

@@ -46,9 +46,7 @@ namespace HiTeachCE.Extension
             //claims.AddRange(claimModel.Roles.Select(s=>new Claim(JwtClaimTypes.Role, s)));
             //claims.AddRange(claimModel.Claims.Select(s => new Claim(ClaimTypes.Role, s)));
             string path = BaseConfigModel.ContentRootPath;
-            RSACryptoServiceProvider provider = RsaHelper.LoadCertificateFile(path + "/private.pem");
-            RsaSecurityKey rsaSecurity = new RsaSecurityKey(provider);
-            var creds = new SigningCredentials(rsaSecurity, SecurityAlgorithms.RsaSha256);
+            var creds = RsaHelper.GenerateSigningCredentials(path + "/private.pem");
             var jwt = new JwtSecurityToken(
                 claims: claims,
                 signingCredentials: creds

+ 212 - 0
HiTeachCE/Extension/RSAUtils.cs

@@ -0,0 +1,212 @@
+using System;
+using System.IO;
+using System.Security.Cryptography;
+
+namespace HiTeachCE.Extension
+{
+    public static class RSAUtils
+    {
+        private const string PrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----";
+        private const string PrivateKeyFooter = "-----END RSA PRIVATE KEY-----";
+
+        public static RSA? FromPrivateKey(string key)
+        {
+            key = key.Trim();
+
+            if (!key.StartsWith(PrivateKeyHeader) || !key.EndsWith(PrivateKeyFooter))
+                throw new ArgumentException("Expect PKCS#1 PEM format key");
+
+            key = key.Substring(PrivateKeyHeader.Length, key.Length - PrivateKeyHeader.Length - PrivateKeyFooter.Length);
+
+            // Convert.FromBase64String ignores whitespace
+            byte[] keyBytes = Convert.FromBase64String(key);
+            return DecodeRSAPrivateKey(keyBytes);
+        }
+
+        //------- Parses binary ans.1 RSA private key; returns RSA ---
+        static RSA? DecodeRSAPrivateKey(byte[] privateKey)
+        {
+
+            // ---------  Set up stream to decode the asn.1 encoded RSA private key  ------
+            using var mem = new MemoryStream(privateKey);
+            using var binr = new BinaryReader(mem);
+            byte bt = 0;
+            ushort twobytes = 0;
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8230)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes != 0x0102) //version number
+                return null;
+            bt = binr.ReadByte();
+            if (bt != 0x00)
+                return null;
+
+            // ------- create RSA from private key RSAParameters -----
+            var rsaParameters = new RSAParameters
+            {
+                Modulus = binr.ReadBytes(GetIntegerSize(binr)),
+                Exponent = binr.ReadBytes(GetIntegerSize(binr)),
+                D = binr.ReadBytes(GetIntegerSize(binr)),
+                P = binr.ReadBytes(GetIntegerSize(binr)),
+                Q = binr.ReadBytes(GetIntegerSize(binr)),
+                DP = binr.ReadBytes(GetIntegerSize(binr)),
+                DQ = binr.ReadBytes(GetIntegerSize(binr)),
+                InverseQ = binr.ReadBytes(GetIntegerSize(binr)),
+            };
+            return RSA.Create(rsaParameters);
+        }
+
+        private const string PublicKeyHeader = "-----BEGIN PUBLIC KEY-----";
+        private const string PublicKeyFooter = "-----END PUBLIC KEY-----";
+
+        public static RSA? FromPublicKey(string key)
+        {
+            key = key.Trim();
+
+            if (!key.StartsWith(PublicKeyHeader) || !key.EndsWith(PublicKeyFooter))
+                throw new ArgumentException("Expect PKCS#1 PEM format key");
+
+            key = key.Substring(PublicKeyHeader.Length, key.Length - PublicKeyHeader.Length - PublicKeyFooter.Length);
+
+            // Convert.FromBase64String ignores whitespace
+            byte[] keyBytes = Convert.FromBase64String(key);
+            return DecodeX509PublicKey(keyBytes);
+        }
+
+        static RSA? DecodeX509PublicKey(byte[] x509Key)
+        {
+            // encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
+            byte[] seqOid = { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
+            // ---------  Set up stream to read the asn.1 encoded SubjectPublicKeyInfo blob  ------
+            using var mem = new MemoryStream(x509Key);
+            using var binr = new BinaryReader(mem);
+            ushort twobytes = binr.ReadUInt16();
+            switch (twobytes)
+            {
+                case 0x8130:
+                    binr.ReadByte();    //advance 1 byte
+                    break;
+                case 0x8230:
+                    binr.ReadInt16();   //advance 2 bytes
+                    break;
+                default:
+                    return null;
+            }
+
+            byte[] seq = binr.ReadBytes(15);
+            if (!CompareBytearrays(seq, seqOid))  //make sure Sequence for OID is correct
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8103) //data read as little endian order (actual data order for Bit String is 03 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8203)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            byte bt = binr.ReadByte();
+            if (bt != 0x00)     //expect null byte next
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81)
+                binr.ReadByte();    //advance 1 byte
+            else if (twobytes == 0x8230)
+                binr.ReadInt16();   //advance 2 bytes
+            else
+                return null;
+
+            twobytes = binr.ReadUInt16();
+            byte lowbyte = 0x00;
+            byte highbyte = 0x00;
+
+            if (twobytes == 0x8102) //data read as little endian order (actual data order for Integer is 02 81)
+                lowbyte = binr.ReadByte();  // read next bytes which is bytes in modulus
+            else if (twobytes == 0x8202)
+            {
+                highbyte = binr.ReadByte(); //advance 2 bytes
+                lowbyte = binr.ReadByte();
+            }
+            else
+                return null;
+            byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };   //reverse byte order since asn.1 key uses big endian order
+            int modsize = BitConverter.ToInt32(modint, 0);
+
+            byte firstbyte = binr.ReadByte();
+            binr.BaseStream.Seek(-1, SeekOrigin.Current);
+
+            if (firstbyte == 0x00)
+            {   //if first byte (highest order) of modulus is zero, don't include it
+                binr.ReadByte();    //skip this null byte
+                modsize -= 1;   //reduce modulus buffer size by 1
+            }
+
+            byte[] modulus = binr.ReadBytes(modsize); //read the modulus bytes
+
+            if (binr.ReadByte() != 0x02)            //expect an Integer for the exponent data
+                return null;
+            int expbytes = binr.ReadByte();        // should only need one byte for actual exponent data (for all useful values)
+            byte[] exponent = binr.ReadBytes(expbytes);
+
+            // ------- create RSA from public key RSAParameters -----
+            var rsaParameters = new RSAParameters
+            {
+                Modulus = modulus,
+                Exponent = exponent
+            };
+            return RSA.Create(rsaParameters);
+        }
+
+        private static int GetIntegerSize(BinaryReader binr)
+        {
+            byte bt = binr.ReadByte();
+            if (bt != 0x02)     //expect integer
+                return 0;
+            bt = binr.ReadByte();
+
+            int count;
+            if (bt == 0x81)
+                count = binr.ReadByte();    // data size in next byte
+            else
+                if (bt == 0x82)
+            {
+                byte highbyte = binr.ReadByte();
+                byte lowbyte = binr.ReadByte();
+                byte[] modint = { lowbyte, highbyte, 0x00, 0x00 };
+                count = BitConverter.ToInt32(modint, 0);
+            }
+            else
+            {
+                count = bt;     // we already have the data size
+            }
+
+            while (binr.ReadByte() == 0x00)
+            {   //remove high order zeros in data
+                count -= 1;
+            }
+            binr.BaseStream.Seek(-1, SeekOrigin.Current);     //last ReadByte wasn't a removed zero, so back up a byte
+            return count;
+        }
+
+        static bool CompareBytearrays(byte[] a, byte[] b)
+        {
+            if (a.Length != b.Length)
+                return false;
+            int i = 0;
+            foreach (byte c in a)
+            {
+                if (c != b[i])
+                    return false;
+                i++;
+            }
+            return true;
+        }
+    }
+}

+ 25 - 0
HiTeachCE/Extension/RsaHelper.cs

@@ -0,0 +1,25 @@
+using Microsoft.IdentityModel.Tokens;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace HiTeachCE.Extension
+{
+    public class RsaHelper
+    {
+        public static SigningCredentials GenerateSigningCredentials(string file) {
+            var privateRSA = RSAUtils.FromPrivateKey(File.ReadAllText(file));
+            var signingKey = new RsaSecurityKey(privateRSA);
+            var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.RsaSha256);
+            return signingCredentials;
+        }
+        public static SecurityKey GenerateValidationKey(string file)
+        {
+            var publicRSA = RSAUtils.FromPublicKey(File.ReadAllText(file));
+            var signingKey = new RsaSecurityKey(publicRSA);
+            return signingKey;
+        }
+    }
+}

+ 1 - 1
HiTeachCE/Startup.cs

@@ -47,7 +47,7 @@ namespace HiTeachCE
             services.AddCors();
             //設定跨域請求
             //引入Jwt配置
-            services.JwtAuth(Configuration.GetSection("JwtSetting"));
+            services.Auth(Configuration.GetSection("JwtSetting"));
             //JWT 黑名单///https://blog.csdn.net/sinat_14899485/article/details/88591848 jwt 黑名单
             //注入CSRedis
             var csredis = new CSRedis.CSRedisClient(Configuration.GetSection("Redis:ConnectionString").Get<string>());

+ 25 - 25
HiTeachCE/private.pem

@@ -1,27 +1,27 @@
 -----BEGIN RSA PRIVATE KEY-----
-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
+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
 -----END RSA PRIVATE KEY-----

+ 7 - 7
HiTeachCE/public.pem

@@ -1,9 +1,9 @@
 -----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq8pNyi9hNGbjAU16coA/
-W6hBQb92YbLFKuePbpmRg7nc/8beiGI6ngl0IDmbAcyQEN5IHN8B1xma33RSXdlR
-O3+fiUlk81gV1uSYTaiRAWL0TryMKf9avQsw+ezP5HVNQRj75lZZxeNg9YTbjVEb
-VAj/cF/0E8xBdplk1+AyrZTk/0Cl04Pr+dM5R+fisgIp3sEULORzhpow38l+S2Yj
-oCagsckPKhSuqLEkB3KoiFJKCoHKUJ+dOfYoa6m3N5ylQCm79HzWeS7PSaXVq6Qh
-+nxNvA2FkSgcSPc1a1vNKXCwHW2HShKsPGugaRCZPTHPDip05suatheVU4AhpVq9
-MwIDAQAB
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA478XskPJlQaL216X0N/X
+kG23OKmPDJkorDVb7V4kLF48+zoo8SAZMipNPiQo4nMXMjERk8YYrgZzzCESfESi
+EnWtMpqCFKShTWvqDDBqxoS/61xvRmAVSTfgNiGTJSAgnStZ5qJfLFYjf10wy2N2
+rL1PT8K0mg48U50teiUApdOlM9LxKvCscpwKSehvDcrM3gcp6QfCzZMPf/carA8l
+L8l0Ql/F+cjtBGIPKWMgHsm+70i0LZjYyJcFJUsbZW+0LTFVA6/JG7lsNDRpmn6m
+9REfJXgbQqNar8KNMNApM34BkzKzO88IWy/Kn3PRR2FPsxUJ3uIJt5yOCUYP2Btd
+gwIDAQAB
 -----END PUBLIC KEY-----