فهرست منبع

提交RSA256的Jwt实现方式

黄贺彬 6 سال پیش
والد
کامیت
2ff9f2dc70

+ 6 - 3
TEAMModelOS.SDK/Extension/JwtAuth/JwtAuthExtension.cs

@@ -8,6 +8,8 @@ using Microsoft.IdentityModel.Tokens;
 using System;
 using System.Text;
 using System.Threading.Tasks;
+using TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper;
+using TEAMModelOS.SDK.Context.Configuration;
 
 namespace TEAMModelOS.SDK.Extension.JwtAuth
 {
@@ -17,9 +19,10 @@ namespace TEAMModelOS.SDK.Extension.JwtAuth
         {
             services.Configure<JwtSetting>(configuration);
             // var creds = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"]), SecurityAlgorithms.RsaSha256Signature);
-           
-
-             var creds = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"]));
+            //var creds = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"]));
+            string path = BaseConfigModel.ContentRootPath;
+            RsaSecurityKey creds = new RsaSecurityKey(RsaHelper.LoadCertificateFile(path + "/private.pem"));
+            //RsaSecurityKey creds = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["SecurityKey"])), SecurityAlgorithms.RsaSha256Signature);
             // 令牌验证参数
             var tokenValidationParameters = new TokenValidationParameters
             {

+ 9 - 5
TEAMModelOS.SDK/Extension/JwtAuth/JwtHelper/JwtHelper.cs

@@ -8,6 +8,8 @@ using System.Linq;
 using System.Security.Claims;
 using System.Text;
 using TEAMModelOS.SDK.Helper.Common.DateTimeHelper;
+using TEAMModelOS.SDK.Context.Configuration;
+using System.Security.Cryptography;
 
 namespace TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper
 {
@@ -40,17 +42,16 @@ namespace TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper
             claims.Add(new Claim(JwtClaimTypes.Issuer, setting.Issuer));
             claims.Add(new Claim(JwtClaimTypes.Scope, claimModel.Scope));
             claims.AddRange(claimModel.Roles.ToArray().Select(s=>new Claim(JwtClaimTypes.Role,s)));
-            var creds = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(setting.SecurityKey)), SecurityAlgorithms.RsaSha256Signature);
+            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 = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(setting.SecurityKey)), SecurityAlgorithms.HmacSha512);
             var jwt = new JwtSecurityToken(
                 claims:claims,
                 signingCredentials:creds
                 );
             var jwtHandler = new JwtSecurityTokenHandler();
-              jwtHandler.WriteToken(jwt);
-
-
             return new JwtResponse {
                 access_token = jwtHandler.WriteToken(jwt),
                 scope = claimModel.Scope
@@ -63,6 +64,9 @@ namespace TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper
         /// <returns></returns>
         public static ClaimModel SerializeJWT(string jwtStr)
         {
+
+            ///https://www.cnblogs.com/JacZhu/p/6837676.html#Update2.0  刷新     用户的 Token 在过期时间之内根本无法手动设置失效,随之而来的还有重放攻击等等问题
+
             var jwtHandler = new JwtSecurityTokenHandler();
             JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);
             ClaimModel claimModel = new ClaimModel();

+ 155 - 48
TEAMModelOS.SDK/Extension/JwtAuth/JwtHelper/RsaHelper.cs

@@ -10,63 +10,170 @@ namespace TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper
 {
     public static class RsaHelper
     {
-        /// <summary>
-        /// 从本地文件中读取用来签发 Token 的 RSA Key
-        /// </summary>
-        /// <param name="filePath">存放密钥的文件夹路径</param>
-        /// <param name="withPrivate"></param>
-        /// <param name="keyParameters"></param>
-        /// <returns></returns>
-        public static bool TryGetKeyParameters(string filePath, bool withPrivate, out RSAParameters keyParameters)
+        public static string RSASign(string data, string privateKeyPem)
         {
-            string filename = withPrivate ? "private.pem" : "public.pem";
-            filePath = Path.Combine(filePath, filename);
-            keyParameters = default(RSAParameters);
-            if (File.Exists(filePath) == false) return false;
-          //  keyParameters = JsonConvert.DeserializeObject<RsaParameterStorage>(File.ReadAllText(filePath)).Map().To<RSAParameters>();
-            return true;
+            RSACryptoServiceProvider rsaCsp = LoadCertificateFile(privateKeyPem);
+            byte[] dataBytes = Encoding.UTF8.GetBytes(data);
+            byte[] signatureBytes = rsaCsp.SignData(dataBytes, "SHA1");
+            return Convert.ToBase64String(signatureBytes);
+        }
+       
+        private static byte[] GetPem(string type, byte[] data)
+        {
+            string pem = Encoding.UTF8.GetString(data);
+            string header = String.Format("-----BEGIN {0}-----\\n", type);
+            string footer = String.Format("-----END {0}-----", type);
+            int start = pem.IndexOf(header) + header.Length;
+            int end = pem.IndexOf(footer, start);
+            string base64 = pem.Substring(start, (end - start));
+            return Convert.FromBase64String(base64);
+        }
+        public static string  LoadCertificateFileToSting (string filename) {
+            FileStream fs = System.IO.File.OpenRead(filename);
+            byte[] data = new byte[fs.Length];
+            byte[] res = null;
+            fs.Read(data, 0, data.Length);
+            if (data[0] != 0x30)
+            {
+                res = GetPem("RSA PRIVATE KEY", data);
+            }
+            return res.ToJson();
+        }
+        public static RSACryptoServiceProvider LoadCertificateFile(string filename)
+        {
+            FileStream fs = System.IO.File.OpenRead(filename);
+            byte[] data = new byte[fs.Length];
+            byte[] res = null;
+            fs.Read(data, 0, data.Length);
+            if (data[0] != 0x30)
+            {
+                res = GetPem("RSA PRIVATE KEY", data);
+            }
+            try
+            {
+              string  ss= res.ToJson();
+                RSACryptoServiceProvider rsa = DecodeRSAPrivateKey(res);
+                string s = rsa.ToString();
+                return rsa;
+            }
+            catch (Exception ex)
+            {
+            }
+            return null;
         }
 
-        /// <summary>
-        /// 生成并保存 RSA 公钥与私钥
-        /// </summary>
-        /// <param name="filePath">存放密钥的文件夹路径</param>
-        /// <returns></returns>
-        public static RSAParameters GenerateAndSaveKey(string filePath)
+        private static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey)
         {
-            RSAParameters publicKeys, privateKeys;
-            using (var rsa = new RSACryptoServiceProvider(2048))
+            byte[] MODULUS, E, D, P, Q, DP, DQ, IQ;
+
+            // --------- Set up stream to decode the asn.1 encoded RSA private key ------
+            MemoryStream mem = new MemoryStream(privkey);
+            BinaryReader binr = new BinaryReader(mem);  //wrap Memory Stream with BinaryReader for easy reading
+            byte bt = 0;
+            ushort twobytes = 0;
+            int elems = 0;
+            try
             {
-                try
-                {
-                    privateKeys = rsa.ExportParameters(true);
-                    publicKeys = rsa.ExportParameters(false);
-                }
-                finally
-                {
-                    rsa.PersistKeyInCsp = false;
-                }
+                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;
+
+
+                //------ all private key components are Integer sequences ----
+                elems = GetIntegerSize(binr);
+                MODULUS = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                E = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                D = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                P = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                Q = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                DP = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                DQ = binr.ReadBytes(elems);
+
+                elems = GetIntegerSize(binr);
+                IQ = binr.ReadBytes(elems);
+
+
+                // ------- create RSACryptoServiceProvider instance and initialize with public key -----
+                CspParameters CspParameters = new CspParameters();
+                CspParameters.Flags = CspProviderFlags.UseMachineKeyStore;
+                RSACryptoServiceProvider RSA = new RSACryptoServiceProvider(1024, CspParameters);
+                RSAParameters RSAparams = new RSAParameters();
+                RSAparams.Modulus = MODULUS;
+                RSAparams.Exponent = E;
+                RSAparams.D = D;
+                RSAparams.P = P;
+                RSAparams.Q = Q;
+                RSAparams.DP = DP;
+                RSAparams.DQ = DQ;
+                RSAparams.InverseQ = IQ;
+                RSA.ImportParameters(RSAparams);
+                return RSA;
+            }
+            catch (Exception ex)
+            {
+                return null;
+            }
+            finally
+            {
+                binr.Close();
             }
-            File.WriteAllText(Path.Combine(filePath, "private.pem"), privateKeys.ToJsonString());
-            File.WriteAllText(Path.Combine(filePath, "public.pem"), publicKeys.ToJsonString());
-            return privateKeys;
         }
 
-        static string ToJsonString(this RSAParameters parameters)
+        private static int GetIntegerSize(BinaryReader binr)
         {
-           // var content = MessagePackHelper.ByteToObject<RsaParameterStorage>(MessagePackHelper.ObjectToByte(parameters));
-            return JsonConvert.SerializeObject(parameters);
+            byte bt = 0;
+            byte lowbyte = 0x00;
+            byte highbyte = 0x00;
+            int count = 0;
+            bt = binr.ReadByte();
+            if (bt != 0x02)        //expect integer
+                return 0;
+            bt = binr.ReadByte();
+
+            if (bt == 0x81)
+                count = binr.ReadByte();    // data size in next byte
+            else
+                if (bt == 0x82)
+            {
+                highbyte = binr.ReadByte();    // data size in next 2 bytes
+                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;
         }
-        //class RsaParameterStorage
-        //{
-        //    public byte[] D { get; set; }
-        //    public byte[] DP { get; set; }
-        //    public byte[] DQ { get; set; }
-        //    public byte[] Exponent { get; set; }
-        //    public byte[] InverseQ { get; set; }
-        //    public byte[] Modulus { get; set; }
-        //    public byte[] P { get; set; }
-        //    public byte[] Q { get; set; }
-        //}
     }
 }

+ 94 - 2
TEAMModelOS/Controllers/Common/LoginController.cs

@@ -1,13 +1,21 @@
+using IdentityModel;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using Microsoft.IdentityModel.Tokens;
 using System;
 using System.Collections.Generic;
+using System.IdentityModel.Tokens.Jwt;
 using System.Linq;
+using System.Security.Claims;
+using System.Text;
 using System.Threading.Tasks;
 using TEAMModelOS.Model.Common.Dtos;
 using TEAMModelOS.SDK.Context.Constant.Common;
 using TEAMModelOS.SDK.Extension.DataResult.JsonRpcRequest;
 using TEAMModelOS.SDK.Extension.DataResult.JsonRpcResponse;
+using TEAMModelOS.SDK.Extension.JwtAuth.JwtHelper;
+using TEAMModelOS.SDK.Extension.JwtAuth.Models;
 using TEAMModelOS.SDK.Helper.Common.ValidateHelper;
 using TEAMModelOS.Service.Common.Interfaces;
 
@@ -18,9 +26,10 @@ namespace TEAMModelOS.Controllers.Common
     public class LoginController : Controller
     {
         private ILoginInfoService _loginInfoService;
-
-        public LoginController(ILoginInfoService loginInfoService) {
+        public readonly IOptions<JwtSetting> _options;
+        public LoginController(ILoginInfoService loginInfoService , IOptions<JwtSetting> options) {
             _loginInfoService = loginInfoService;
+            _options = options;
         }
 
         /// <summary>
@@ -51,5 +60,88 @@ namespace TEAMModelOS.Controllers.Common
             }
             return responseBuilder.build();
         }
+
+        /// <summary>
+        /// 授权
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("authorization")]
+        [AllowAnonymous]
+        public BaseJosnRPCResponse Authorization()
+        {
+            string jwtToken = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiaHVhbmdoYiIsImVtYWlsIjoiaHVhbmdoYkBjZGhhYm9vay5jb20iLCJpYXQiOjE1NDg3MzA2MjMsIm5iZiI6MTU0ODczMDYyMywiZXhwIjoxNTQ4NzMwNjgzLCJhdWQiOiJIYWJvb2suVGVhbU1vZGVsLkNvbnRlc3QiLCJpc3MiOiJIYUJvb2siLCJyb2xlIjoiYWRtaW4scm9vdCJ9.acye5fBHttovBjr0JdDNvm8zVjuLukqyQZ_GPSN7t8aDBiKO8K80dMfefaYC7bF1VodzVFDKKEkieuq4NRnTjw";
+            //ClaimModel claimModel =  JwtHelper.SerializeJWT(jwtToken);
+            JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
+            ClaimModel claimModel = new ClaimModel
+            {
+                Scope = "WebApp"
+            };
+            claimModel.Claims.Add(new Claim(JwtClaimTypes.Name, "huanghb"));
+            claimModel.Claims.Add(new Claim(JwtClaimTypes.Email, "huanghb@cdhabook.com"));
+            claimModel.Roles.Add("admin,root");
+            Dictionary<string, object> ext = new Dictionary<string, object>();
+            ext.Add("claimModel", claimModel);
+
+            builder.Data(claimModel).CurrPage(1);
+            return builder.Data(JwtHelper.IssueJWT(claimModel, _options.Value)).Extend(ext).build();
+        }
+        /// <summary>
+        /// 认证
+        /// </summary>
+        /// <returns></returns>
+        [HttpPost("authentication")]
+        [Authorize]
+        public Object Authentication()
+        {
+            return "";
+        }
+        [AllowAnonymous]
+       
+        [HttpPost("Authenticate")]
+        public IActionResult Authenticate()
+        {
+            var user = new 
+            {
+                id = 111,
+                name = "123",
+                email = "3538@qq.com",
+                phone = "15283771540"
+            };
+            if (user == null)
+                return Unauthorized();
+            var tokenHandler = new JwtSecurityTokenHandler();
+            var key = Encoding.ASCII.GetBytes("JwtBearerSample_11231~#$%#%^2235");
+            var authTime = DateTime.UtcNow;
+            var expiresAt = authTime.AddDays(7);
+            var tokenDescriptor = new SecurityTokenDescriptor
+            {
+                Subject = new ClaimsIdentity(new Claim[]
+                {
+                new Claim(JwtClaimTypes.Audience,"api"),
+                new Claim(JwtClaimTypes.Issuer,"http://localhost:5200"),
+                new Claim(JwtClaimTypes.Id, user.id.ToString()),
+                new Claim(JwtClaimTypes.Name, user.name),
+                new Claim(JwtClaimTypes.Email, user.email),
+                new Claim(JwtClaimTypes.PhoneNumber, user.phone)
+                }),
+                Expires = expiresAt,
+                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
+            };
+            var token = tokenHandler.CreateToken(tokenDescriptor);
+            var tokenString = tokenHandler.WriteToken(token);
+            return Ok(new
+            {
+                access_token = tokenString,
+                token_type = "Bearer",
+                profile = new
+                {
+                    sid = user.id,
+                    user.name,
+                    auth_time = new DateTimeOffset(authTime).ToUnixTimeSeconds(),
+                    expires_at = new DateTimeOffset(expiresAt).ToUnixTimeSeconds()
+                }
+            });
+        }
+
     }
 }

+ 2 - 2
TEAMModelOS/Controllers/Syllabus/SyllabusController.cs

@@ -72,8 +72,8 @@ namespace TEAMModelOS.Controllers.Syllabus
         public  BaseJosnRPCResponse RSA()
         {
             JsonRPCResponseBuilder builder = JsonRPCResponseBuilder.custom();
-            var data = RsaHelper.GenerateAndSaveKey("E://");
-            return builder.Data(data).build();
+          //  var data = RsaHelper.GenerateAndSaveKey("E://");
+            return builder.Data(null).build();
         }
     }
 }

+ 1 - 1
TEAMModelOS/Startup.cs

@@ -36,7 +36,7 @@ namespace TEAMModelOS
         public IConfiguration Configuration { get; }
 
         // This method gets called by the runtime. Use this method to add services to the container.
-        public void ConfigureServices(IServiceCollection services)
+        public void ConfigureServices(IServiceCollection services )
         {
             //services.Configure<CookiePolicyOptions>(options =>
             //{