diff --git a/IRaCIS.Core.API/Controllers/ExtraController.cs b/IRaCIS.Core.API/Controllers/ExtraController.cs index 73cc21a20..e20a5051e 100644 --- a/IRaCIS.Core.API/Controllers/ExtraController.cs +++ b/IRaCIS.Core.API/Controllers/ExtraController.cs @@ -110,11 +110,11 @@ namespace IRaCIS.Api.Controllers [FromServices] ITokenService _tokenService, [FromServices] IReadingImageTaskService readingImageTaskService, [FromServices] IOptionsMonitor _verifyConfig, - [FromServices] IOptionsMonitor _emailConfig, - + [FromServices] IOptionsMonitor _emailConfig, + [FromServices] IMailVerificationService _mailVerificationService) { - var emailConfig= _emailConfig.CurrentValue; + var emailConfig = _emailConfig.CurrentValue; var companyInfo = new SystemEmailSendConfigView() { CompanyName = emailConfig.CompanyName, CompanyNameCN = emailConfig.CompanyNameCN, CompanyShortName = emailConfig.CompanyShortName, CompanyShortNameCN = emailConfig.CompanyShortNameCN }; //MFA 邮箱验证 前端传递用户Id 和MFACode @@ -291,8 +291,14 @@ namespace IRaCIS.Api.Controllers } + [AllowAnonymous] + [HttpGet, Route("user/getPublicKey")] + public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor _IRCEncreptOption) + { + var pemPublicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey)); - + return ResponseOutput.Ok(pemPublicKey); + } [HttpGet, Route("imageShare/ShareImage")] diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index 62e075ff6..5d258c077 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -111,7 +111,7 @@ builder.Services.AddControllers(options => options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); - options.Filters.Add(10); + //options.Filters.Add(10); options.Filters.Add(); @@ -122,7 +122,7 @@ builder.Services.AddOptions().Configure(_configuration.Ge builder.Services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); -builder.Services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig")); +builder.Services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("SystemPacsConfig")); builder.Services.Configure(_configuration.GetSection("IRaCISBasicConfig")); @@ -218,6 +218,7 @@ var env = app.Environment; #region 配置中间件 +app.UseMiddleware(); // Configure the HTTP request pipeline. diff --git a/IRaCIS.Core.API/appsettings.json b/IRaCIS.Core.API/appsettings.json index dec14286d..529451e7f 100644 --- a/IRaCIS.Core.API/appsettings.json +++ b/IRaCIS.Core.API/appsettings.json @@ -60,7 +60,9 @@ "ImageShareExpireDays": 10 }, "EncrypteResponseConfig": { - "IsEnable": true, + "Base64RSAPublicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0NCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBaHp3T1hYTWYyaEFkS1ZoWHczYUYNCmNaT3QycE1lcmdEaFVrOUdQK2s4VDBrUjFTRVlGVGtzNlkzaEVvL0dRTExqMHZFYVV3bTNhSFNuTTl5NmdLRWoNCmY5cTN6dkoyZzRSQjE4Z0UrTnNWWi9DMkVRZ3k5OWFiWGc5TitGREVlT0NmSjlSRTJPV3JBQ2s0V0RPbFFUdXYNCnhvR2JmcnkwVElSaFBrOGtuYkFmVkZ1and1VXJGblpJZ0ExYXhKZVZ6aDhwcmV1SEgreW1jdHp6NVo4V1pSV3kNCi9ISURHUy90dkg2NUMra2l6cUxRYUpKNHYwODMrRGpaVTBmNzNCdkk5eWt1dW1saXFzY1pvU2preDFOUFJwSkUNCkFtMVFNQ0hMRCtFdzlHT2Vsc2Mwa1ZxdjdaeEF1TkFrMkZuUURNRk1BUmZuUFd0aGVhOGZYVTJsMW9ROWs3WDcNCmN3SURBUUFCDQotLS0tLUVORCBQVUJMSUMgS0VZLS0tLS0NCg==", + "Base64RSAPrivateKey": "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQ0KTUlJRW9nSUJBQUtDQVFFQWh6d09YWE1mMmhBZEtWaFh3M2FGY1pPdDJwTWVyZ0RoVWs5R1ArazhUMGtSMVNFWQ0KRlRrczZZM2hFby9HUUxMajB2RWFVd20zYUhTbk05eTZnS0VqZjlxM3p2SjJnNFJCMThnRStOc1ZaL0MyRVFneQ0KOTlhYlhnOU4rRkRFZU9DZko5UkUyT1dyQUNrNFdET2xRVHV2eG9HYmZyeTBUSVJoUGs4a25iQWZWRnVqd3VVcg0KRm5aSWdBMWF4SmVWemg4cHJldUhIK3ltY3R6ejVaOFdaUld5L0hJREdTL3R2SDY1QytraXpxTFFhSko0djA4Mw0KK0RqWlUwZjczQnZJOXlrdXVtbGlxc2Nab1Nqa3gxTlBScEpFQW0xUU1DSExEK0V3OUdPZWxzYzBrVnF2N1p4QQ0KdU5BazJGblFETUZNQVJmblBXdGhlYThmWFUybDFvUTlrN1g3Y3dJREFRQUJBb0lCQUNDRFoxNi9XWWFYZmpOZA0KQ29pemU2VFJZU2llVzI5eFJic0ExVDV6YUcwVmY4U1NsdFF2Y1dWYmw2UGJUa3BxMkF4MHJDUVB2M2xOSm8vNA0KL3h3QzRlS1E1c1ZLRlFWTXJIbmhISlRxTTJ6UWVpMkJINlBuaEdZcVh0QVhOdzFxejhrSEoyQlFZM3IvN2d5Qw0KcWpZVFVCRDFRem5HeThCanlXOXVIcnNNeDVPRHRRZWxBM3B1TFd1bXZNb3Z1L2JhaDZvTGtOSHY4b0VTdzhGSQ0KTllyTUtscHhFTjZaWUdwSTl2VTZnYUhuTmhEa2ExTHlvUnZ5NnA2dTRLR0FsRTc1VXk4T0dsdncydU5uay9sdg0KSEMyYnY5TnlCRGJpSFNDY0MyK1JXUXMrN0tNWFlwYnBvTVFCR0hqV01GRHVBODFaUCs1TWYxUm9yQUpRNGxrRw0KQnRDQThva0NnWUVBeWY4alBjcFIvQ3dJNU5MYnlwb3ZUWEFHVkFKMmtWRlJVUC9HT2ZOMkFyYWMvL20wdDJ5NA0KemNYVkJZc0pJeHkvVWYxRTFJNFg3VFg3V3NBNGxVNGlPTkwzTnN4dDBEbk1PV2tKUlBPZlF1bW1JaWw5QVRiYQ0KTnRVWFNlTmRoUFFGMGlCb21acFFJYWpSN1RmUnVBbzR6dmpTLzkzeXZZY0lIWU1zM0tjR00ya0NnWUVBcTJPbg0KZlp0RmhLTElGanVlRzRrNklGTWdjbytEUUh5TTFWSUp5Tk05K1o2TEgybzI5eDJCaSs2Qm82M2NKUjQ2STZncQ0KNWUrSTBvdzZRYmNQeTZUNHFSQ0o3Ujd6MllkdEw4eXdJTkNYS3U0cC9qaFNqNWJ1TzFJWlI3ZStSV29CakNtUQ0KWFd0ZVBCbldqWVlLdVRCazROc2FXV09GTXg1QndKdUp2MjBnQ0hzQ2dZQlV4QnFYM1lWV0cyeUlDZXh1TXhIUw0KbjBZb2p2Z090MTgyYkg5VTVsUUpnM1NTL3NqVmlHeHMvYTROSzNGa0tMWW93KzNVZk9TUmlPdTRBNTQ3R1pURw0KMzlFYVQrTnRWRFBkaTdSMkdQNG1hRUp0WjVlcm9NY2w1M3BrYVdOZlhiL3JrK29STzI2UkVYVTI1UXUrL1pzbA0KVDhuTDBlb0JtdDdPODdNcHpYV09zUUtCZ0ZxVGFQSGx2RUNUY3FEbFV2S0VmRmFXOTkvelhrOFhRNnA5RjdTdA0KaHVSRDJJeDZxcC9BVlRWcGo5Tzd6MHRDaFVGUTM1THpHMkVDUU10My9uNEdLbS9XMEwyakRRWWFIeWNTeXNZYw0KMXJjV2ROVG9XU0dQaDBtTVl0WFhFbFJHNkpoMVl0a3NJL29wUVkwN21MRTBGU3dNUHdtY29jbFpKVEN3UW9VTA0KRzlHL0FvR0FWM25kcWcydnUyR0x4TlRUbm1UTWRJNFB3UzBmN0V4VnVNUnRicGZyOWhKNzlDakVNTGFXQ1FsNg0KeE43TElMTnArNVQwQW1DZVBqcitkbnJkRUNidFZPTDFybDc3Z0krRkwzaVVqYmZmMVZqa0N3M0x6K3cyb1FFdA0KbGE4aTZrL1NRK01iYkRPaWRJOVczdlN6MmlJRlZobWJiK1Q2SlZwakxqNjlkblM3eUxZPQ0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0NCg==", + "IsResponseEncreptEnable": true, "ApiPathList": [ "/test/get" ] diff --git a/IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs index bae8e48ad..0ff58ed56 100644 --- a/IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/Encryption/EncreptApiResultFilter.cs @@ -16,9 +16,9 @@ namespace IRaCIS.Core.Application.BusinessFilter; /// public class EncreptApiResultFilter : IAsyncResultFilter { - private readonly IOptionsMonitor _encreptResponseMonitor; + private readonly IOptionsMonitor _encreptResponseMonitor; - public EncreptApiResultFilter(IOptionsMonitor encreptResponseMonitor) + public EncreptApiResultFilter(IOptionsMonitor encreptResponseMonitor) { _encreptResponseMonitor = encreptResponseMonitor; } @@ -26,7 +26,7 @@ public class EncreptApiResultFilter : IAsyncResultFilter public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { - if (_encreptResponseMonitor.CurrentValue.IsEnable) + if (_encreptResponseMonitor.CurrentValue.IsResponseEncreptEnable) { if (context.Result is ObjectResult objectResult) diff --git a/IRaCIS.Core.Application/BusinessFilter/Encryption/EncryptionRequestMiddleware.cs b/IRaCIS.Core.Application/BusinessFilter/Encryption/EncryptionRequestMiddleware.cs index 055e306f9..b23917a09 100644 --- a/IRaCIS.Core.Application/BusinessFilter/Encryption/EncryptionRequestMiddleware.cs +++ b/IRaCIS.Core.Application/BusinessFilter/Encryption/EncryptionRequestMiddleware.cs @@ -1,5 +1,7 @@ using DocumentFormat.OpenXml.InkML; +using IRaCIS.Core.Domain.Share; using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; @@ -13,55 +15,120 @@ public class EncryptionRequestMiddleware { private readonly RequestDelegate _next; - public EncryptionRequestMiddleware(RequestDelegate next) + private readonly IRCEncreptOption _IRCEncreptOption; + + public EncryptionRequestMiddleware(RequestDelegate next, IOptionsMonitor encreptResponseMonitor) { _next = next; + + _IRCEncreptOption = encreptResponseMonitor.CurrentValue; } public async Task InvokeAsync(HttpContext context) { + //模拟前端已经设置该请求头 + //context.Request.Headers["X-Encrypted-Key"] = "hW8JuJocBzIPWSHURUnYJMZ0l68g6uP9rEdMho8ioFO8amD6GIH+R6RIX5jVFzOwO+lmgBi+4gaVJGGkWJMTcoDTfzzzT1EmfPV+UhFLE9HWvCkEXmDOLE9xCHrbG8TrR1I9+Qd3cvo9HLmrQ58n13cJsM82FBw+reUgiWeklPCkEWM9br2qc9VkCp6gKzimTldNTWBezV86S6j3qHbZpVZm0atdDtGb+zSC9LLA3+oHQwRLJAAGSOAi8CUiRmIQsygRd824wBndkaH2ImEeRjBMs7PqCeK3KsoDp13yHdj2AE751gsfNzf4SF547UPf72m0F2/rBFgrSNy+Jz0T9w=="; + // 检查请求头中是否包含加密的对称密钥 if (context.Request.Headers.ContainsKey("X-Encrypted-Key")) { - var encryptedSymmetricKey = Convert.FromBase64String(context.Request.Headers["X-Encrypted-Key"]); - //// 使用私钥解密对称密钥 - //var decryptedSymmetricKey = RsaEncryptionHelper.DecryptRsa(encryptedSymmetricKey, _rsaPrivateKey); - //var aesKey = decryptedSymmetricKey[..32]; // 前32字节作为AES密钥 - //var aesIv = decryptedSymmetricKey[32..]; // 后面16字节作为IV + var encryptedSymmetricKeyStr = context.Request.Headers["X-Encrypted-Key"].ToString(); - //// 读取并解密请求体中的JSON数据 - //context.Request.EnableBuffering(); - //using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true)) - //{ - // var encryptedBody = await reader.ReadToEndAsync(); - // context.Request.Body.Position = 0; + var pemSecretKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.Base64RSAPrivateKey)); + + // 使用私钥解密对称密钥 + var decryptedSymmetricKey = RSAEncryption.Decrypt(pemSecretKey, encryptedSymmetricKeyStr); + + + #region 处理路径传递参数 + + // 处理路径参数解密 + var path = context.Request.Path.Value; + + // 假设加密的参数在路径中,按照顺序解密路径中的每个部分 + var pathSegments = path.Split('/', StringSplitOptions.RemoveEmptyEntries); + for (int i = 2; i < pathSegments.Length; i++) + { + if (!string.IsNullOrEmpty(pathSegments[i])) + { + try + { + var decryptedSegment = AesEncryption.Decrypt(pathSegments[i], decryptedSymmetricKey); + pathSegments[i] = decryptedSegment; + } + catch + { + // 如果不能解密该部分,保持原值 + } + } + } + + // 将解密后的路径重新拼接并设置到请求的Path + context.Request.Path = "/" + string.Join("/", pathSegments); + + #endregion + + #region 处理url 传递参数 + + // 处理 URL 查询参数的解密 + var queryParams = context.Request.Query.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.ToString()); + var decryptedQueryParams = new Dictionary(); + + foreach (var param in queryParams) + { + var encryptedValue = param.Value; + var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey); + decryptedQueryParams[param.Key] = decryptedValue; + } + + // 构造解密后的查询字符串 + var decryptedQueryString = new QueryString("?" + string.Join("&", decryptedQueryParams.Select(kvp => $"{kvp.Key}={kvp.Value}"))); + context.Request.QueryString = decryptedQueryString; + + #endregion + + #region 处理body传参 + + // 读取并解密请求体中的JSON数据 + context.Request.EnableBuffering(); + using (var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true)) + { + var encryptedBody = await reader.ReadToEndAsync(); + context.Request.Body.Position = 0; + + + if (!string.IsNullOrEmpty(encryptedBody)) + { + // 尝试解析为JObject + var encryptedJson = JObject.Parse(encryptedBody); + var decryptedJson = new JObject(); + + // 解密每个字段的值 + foreach (var property in encryptedJson.Properties()) + { + var encryptedValue = property.Value.ToString(); + var decryptedValue = AesEncryption.Decrypt(encryptedValue, decryptedSymmetricKey); + decryptedJson[property.Name] = decryptedValue; + } + + // 将解密后的JSON对象转换回字符串,并替换原始请求体 + var decryptedBody = decryptedJson.ToString(); + var bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(decryptedBody)); + context.Request.Body = bodyStream; + context.Request.ContentLength = bodyStream.Length; + bodyStream.Seek(0, SeekOrigin.Begin); + } + } + + #endregion - // // 尝试解析为JObject - // var encryptedJson = JObject.Parse(encryptedBody); - // var decryptedJson = new JObject(); - // // 解密每个字段的值 - // foreach (var property in encryptedJson.Properties()) - // { - // var encryptedValue = property.Value.ToString(); - // var decryptedValue = AesEncryptionHelper.DecryptString(encryptedValue, aesKey, aesIv); - // decryptedJson[property.Name] = decryptedValue; - // } - // // 将解密后的JSON对象转换回字符串,并替换原始请求体 - // var decryptedBody = decryptedJson.ToString(); - // var bodyStream = new MemoryStream(Encoding.UTF8.GetBytes(decryptedBody)); - // context.Request.Body = bodyStream; - // context.Request.ContentLength = bodyStream.Length; - // bodyStream.Seek(0, SeekOrigin.Begin); } - - // 调用下一个中间件 await _next(context); } - } diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index 2c12fa561..f65699bcd 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -126,7 +126,7 @@ namespace IRaCIS.Application.Services - var model4= _mapper.Map(model1, model2); + var model4 = _mapper.Map(model1, model2); await _trialBodyPartRepository.FirstOrDefaultAsync(); @@ -245,9 +245,37 @@ namespace IRaCIS.Application.Services return ResponseOutput.Ok(); } - [UnitOfWork] - public async Task Get() + + public class TestEncrept { + public string Name { get; set; } + + public string Code { get; set; } + } + + public IResponseOutput TestEncreptTest(TestEncrept testModel) + { + return ResponseOutput.Ok(testModel); + } + + //etx5EzcF9XWA6ICzQB5ywEEextuhmUwaHM2TmyyCC8Q= + [HttpPut("{name}/{code}")] + public IResponseOutput TestEncreptTest2(string name, string code) + { + return ResponseOutput.Ok($"name:{name} Code: {code}"); + } + + public IResponseOutput TestEncreptTest3(string name, string Code) + { + return ResponseOutput.Ok($"name:{name} Code: {Code}"); + } + + + [UnitOfWork] + public async Task Get([FromServices] IOptionsMonitor _encreptResponseMonitor) + { + var _IRCEncreptOption = _encreptResponseMonitor.CurrentValue; + string plainText = "Hello, BouncyCastle!"; string key = "12345678901234567890123456789012"; // AES 密钥长度应为 16 字节(128 位) string iv = "your-iv-12345678"; // IV 长度为 16 字节 @@ -276,17 +304,32 @@ namespace IRaCIS.Application.Services Console.WriteLine($"解密后的数据: {decrypte}"); - // Generate RSA keys - var keyPair = RSAEncryption.GenerateRSAKeyPair(2048); + //// Generate RSA keys + //var keyPair = RSAEncryption.GenerateRSAKeyPair(2048); - // Export the public and private keys to PEM format - string publicKey = RSAEncryption.ExportPublicKey(keyPair.Public); - string privateKey = RSAEncryption.ExportPrivateKey(keyPair.Private); + //// Export the public and private keys to PEM format + //string publicKey = RSAEncryption.ExportPublicKey(keyPair.Public); + //string privateKey = RSAEncryption.ExportPrivateKey(keyPair.Private); + + //Console.WriteLine("Public Key:"); + //Console.WriteLine(publicKey); + //Console.WriteLine($"{Convert.ToBase64String(Encoding.UTF8.GetBytes(publicKey))}"); + //Console.WriteLine("\nPrivate Key:"); + //Console.WriteLine(privateKey); + //Console.WriteLine($"{Convert.ToBase64String(Encoding.UTF8.GetBytes(privateKey))}"); + + + + var publicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.Base64RSAPublicKey)); + var privateKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.Base64RSAPrivateKey)); Console.WriteLine("Public Key:"); - Console.WriteLine(publicKey); - Console.WriteLine("\nPrivate Key:"); Console.WriteLine(privateKey); + Console.WriteLine("\nPrivate Key:"); + Console.WriteLine(publicKey); + + Console.WriteLine("encrept sys Key:"); + Console.WriteLine($"\n{RSAEncryption.Encrypt(publicKey, key)}"); // Data to encrypt string dataToEncrypt = "Hello, RSA!"; diff --git a/IRaCIS.Core.Domain/_Config/_AppSettings.cs b/IRaCIS.Core.Domain/_Config/_AppSettings.cs index 8afe0386d..be182ee8c 100644 --- a/IRaCIS.Core.Domain/_Config/_AppSettings.cs +++ b/IRaCIS.Core.Domain/_Config/_AppSettings.cs @@ -98,9 +98,15 @@ namespace IRaCIS.Core.Domain.Share - public class EncreptResponseOption + public class IRCEncreptOption { - public bool IsEnable { get; set; } + public string Base64RSAPublicKey { get; set;} + + public string Base64RSAPrivateKey { get; set; } + + + + public bool IsResponseEncreptEnable { get; set; } public List ApiPathList { get; set; } }