feat: Implementieren der Verschlüsselungs- und Entschlüsselungsdienste mit AES und Integration in die API
- Hinzufügen der `Encryptor`-Klasse für AES-Verschlüsselung und -Entschlüsselung. - Implementierung des `EncryptionController` zur Bereitstellung von Endpunkten für Verschlüsselung, Entschlüsselung und Generierung von Verschlüsselungsparametern. - Erweiterung der DI-Konfiguration mit `AddEncryptor`-Erweiterungsmethode und Integration in `Program.cs`. - Bedingte Registrierung des `EncryptionController` basierend auf der Konfiguration `UseEncryptor`, um sicherzustellen, dass der Controller nur bei Bedarf verfügbar ist. - Implementierung von Lazy Loading für die Verbindungszeichenfolge in `UserManagerDbContext` zur sicheren Handhabung von verschlüsselten Verbindungszeichenfolgen.
This commit is contained in:
parent
c8bcb5a6ac
commit
6e973a494e
@ -0,0 +1,46 @@
|
||||
using DigitalData.UserManager.Application.Services;
|
||||
using DigitalData.UserManager.Application.Services.Options;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace DigitalData.UserManager.API.Controllers
|
||||
{
|
||||
[Route("api/[controller]")]
|
||||
[ApiController]
|
||||
public class EncryptionController : ControllerBase
|
||||
{
|
||||
private readonly Encryptor _encryptor;
|
||||
|
||||
public EncryptionController(Encryptor encryptor)
|
||||
{
|
||||
_encryptor = encryptor;
|
||||
}
|
||||
|
||||
[HttpPost("encrypt")]
|
||||
public IActionResult Encrypt([FromQuery] string plainText, [FromBody] EncryptionParameters? options = null)
|
||||
{
|
||||
string cipherText = options is null
|
||||
? _encryptor.Encrypt(plainText)
|
||||
: Encryptor.Encrypt(plainText, options.Key, options.IV);
|
||||
|
||||
return Ok(cipherText);
|
||||
}
|
||||
|
||||
[HttpPost("decrypt")]
|
||||
public IActionResult Decrypt([FromQuery] string cipherText, [FromBody] EncryptionParameters? options = null)
|
||||
{
|
||||
var plainText = options is null
|
||||
? _encryptor.Decrypt(cipherText)
|
||||
: Encryptor.Decrypt(cipherText, options.Key, options.IV);
|
||||
|
||||
return Ok(plainText);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult Generate()
|
||||
{
|
||||
var param = Encryptor.GenerateParameters();
|
||||
return Ok(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,9 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using NLog.Web;
|
||||
using NLog;
|
||||
using DigitalData.Core.API;
|
||||
using DigitalData.UserManager.API;
|
||||
using DigitalData.UserManager.API.Controllers;
|
||||
using DigitalData.UserManager.Application.Services;
|
||||
|
||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||
logger.Debug("init main");
|
||||
@ -13,6 +16,10 @@ logger.Debug("init main");
|
||||
try {
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
var config = builder.Configuration;
|
||||
|
||||
builder.Services.AddEncryptor(builder.Configuration.GetSection("EncryptionParameters"));
|
||||
|
||||
if (builder.Configuration.GetValue<bool>("RunAsWindowsService"))
|
||||
builder.Host.UseWindowsService();
|
||||
|
||||
@ -27,7 +34,12 @@ try {
|
||||
builder.Services.AddSwaggerGen();
|
||||
}
|
||||
|
||||
builder.Services.AddControllers();
|
||||
builder.Services.AddControllers(opt =>
|
||||
{
|
||||
opt.Conventions.Add(new RemoveIfControllerConvention()
|
||||
.AndIf(c => c.ControllerName == nameof(EncryptionController).Replace("Controller", ""))
|
||||
.AndIf(c => !config.GetValue<bool>("UseEncryptor")));
|
||||
});
|
||||
|
||||
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||
.AddCookie(options =>
|
||||
@ -39,9 +51,10 @@ try {
|
||||
options.LogoutPath = "/api/auth/logout";
|
||||
});
|
||||
|
||||
builder.Services.AddDbContext<UserManagerDbContext>(options =>
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DD_ECM_Connection"))
|
||||
.EnableDetailedErrors());
|
||||
// Once the app is built, the password will be decrypted with Encryptor. lazy loading also acts as a call back method.
|
||||
Lazy<string>? cnn_str = null;
|
||||
|
||||
builder.Services.AddDbContext<UserManagerDbContext>(options => options.UseSqlServer(cnn_str!.Value).EnableDetailedErrors());
|
||||
|
||||
var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>() ?? throw new InvalidOperationException("In appsettings there is no allowed origin.");
|
||||
|
||||
@ -67,6 +80,14 @@ try {
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
cnn_str = new(() =>
|
||||
{
|
||||
var encryptor = app.Services.GetRequiredService<Encryptor>();
|
||||
var eCnnStr = config.GetConnectionString("DD_ECM_Connection") ?? throw new InvalidOperationException("Connection string 'DD_ECM_Connection' is missing from the configuration.");
|
||||
var cnnStr = encryptor.Decrypt(eCnnStr);
|
||||
return cnnStr;
|
||||
});
|
||||
|
||||
app.UseCors("DefaultCorsPolicy");
|
||||
|
||||
if (builder.Configuration.GetValue<bool>("UseSwagger"))
|
||||
|
||||
@ -5,9 +5,6 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DD_ECM_Connection": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
||||
},
|
||||
"AllowedOrigins": [ "http://localhost:4200" ],
|
||||
"Jwt": {
|
||||
"Key": "pJBcBWZSjsWlhi1OlCcw6ERTMRNb7qsdvsfvdfbagdfbdfsSDGSDMhsjkfdhsdfbgkHKSDF",
|
||||
|
||||
@ -6,10 +6,9 @@
|
||||
}
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DD_ECM_Connection": "Server=SDD-VMP04-SQL17\\DD_DEVELOP01;Database=DD_ECM;User Id=sa;Password=dd;Encrypt=false;TrustServerCertificate=True;"
|
||||
"DD_ECM_Connection": "cIFSoeMqHel7SDkAj4MWjy1UHrNJgoHrLkBJ/I/1Y95MsV9vFQjJLn6Shm9qtAyymwSNrX9s+78mW2PX4KulSA/KAaRwNQteP6SHrX0nNOJptot8TcohuiT0m9K2M/GsJEnLyJ+3yb0nJHR5yzRaVvjl8ERhgntW47dFMni98YA="
|
||||
},
|
||||
"AllowedOrigins": [ "http://172.24.12.39:85", "http://localhost:85", "http://localhost:4200", "http://localhost:5500", "https://localhost:7202" ],
|
||||
"UseSwagger": true,
|
||||
"AllowedOrigins": [ "https://localhost:7103", "http://172.24.12.39:85", "http://localhost:85", "http://localhost:4200", "http://localhost:5500", "https://localhost:7202" ],
|
||||
"RunAsWindowsService": false,
|
||||
"DirectorySearchOptions": {
|
||||
"ServerName": "DD-VMP01-DC01",
|
||||
@ -67,5 +66,12 @@
|
||||
"writeTo": "criticalLogs"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"EncryptionParameters": {
|
||||
"Key": "JGPwHVD0BQmC7upi5OV11PzzIk47ugTJoqBV/et5w40=",
|
||||
"IV": "gMuetIjlPvJnSzu+i7I3xg=="
|
||||
},
|
||||
// Delete below in production
|
||||
"UseEncryptor": true,
|
||||
"UseSwagger": true
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
using DigitalData.UserManager.Application.Contracts;
|
||||
using DigitalData.UserManager.Application.MappingProfiles;
|
||||
using DigitalData.UserManager.Application.Services;
|
||||
using DigitalData.UserManager.Application.Services.Options;
|
||||
using DigitalData.UserManager.Infrastructure.Contracts;
|
||||
using DigitalData.UserManager.Infrastructure.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace DigitalData.UserManager.Application
|
||||
@ -40,5 +42,12 @@ namespace DigitalData.UserManager.Application
|
||||
.AddScoped<IModuleService, ModuleService>()
|
||||
.AddScoped<IModuleOfUserService, ModuleOfUserService>()
|
||||
.AddScoped<IUserRepService, UserRepService>();
|
||||
|
||||
public static IServiceCollection AddEncryptor(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddSingleton<Encryptor>();
|
||||
services.Configure<EncryptionParameters>(configuration);
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
<PackageReference Include="DigitalData.Core.Application" Version="1.0.0" />
|
||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0" />
|
||||
<PackageReference Include="System.DirectoryServices" Version="7.0.1" />
|
||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
|
||||
<PackageReference Include="System.DirectoryServices.Protocols" Version="7.0.1" />
|
||||
|
||||
78
DigitalData.UserManager.Application/Services/Encryptor.cs
Normal file
78
DigitalData.UserManager.Application/Services/Encryptor.cs
Normal file
@ -0,0 +1,78 @@
|
||||
using DigitalData.UserManager.Application.Services.Options;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace DigitalData.UserManager.Application.Services
|
||||
{
|
||||
public class Encryptor
|
||||
{
|
||||
public const int KeyByteLength = 32;
|
||||
|
||||
private readonly EncryptionParameters _params;
|
||||
|
||||
public Encryptor(IOptions<EncryptionParameters> options)
|
||||
{
|
||||
_params = options.Value;
|
||||
}
|
||||
|
||||
public string Encrypt(string plainText) => Encrypt(plainText, _params.Key, _params.IV);
|
||||
|
||||
public string Decrypt(string cipherText) => Decrypt(cipherText, _params.Key, _params.IV);
|
||||
|
||||
public static string Encrypt(string plainText, string key, string iv)
|
||||
{
|
||||
using Aes aes = Aes.Create();
|
||||
aes.KeySize = KeyByteLength * 8;
|
||||
aes.Key = AdjustKeySize(Encoding.UTF8.GetBytes(key), aes.KeySize / 8);
|
||||
aes.IV = AdjustKeySize(Encoding.UTF8.GetBytes(iv), aes.BlockSize / 8);
|
||||
|
||||
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
|
||||
|
||||
using MemoryStream ms = new();
|
||||
using (CryptoStream cs = new(ms, encryptor, CryptoStreamMode.Write))
|
||||
{
|
||||
using StreamWriter sw = new(cs);
|
||||
sw.Write(plainText);
|
||||
}
|
||||
return Convert.ToBase64String(ms.ToArray());
|
||||
}
|
||||
|
||||
public static string Decrypt(string cipherText, string key, string iv)
|
||||
{
|
||||
using Aes aes = Aes.Create();
|
||||
aes.KeySize = KeyByteLength * 8;
|
||||
aes.Key = AdjustKeySize(Encoding.UTF8.GetBytes(key), aes.KeySize / 8);
|
||||
aes.IV = AdjustKeySize(Encoding.UTF8.GetBytes(iv), aes.BlockSize / 8);
|
||||
|
||||
ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
|
||||
|
||||
using MemoryStream ms = new(Convert.FromBase64String(cipherText));
|
||||
using CryptoStream cs = new(ms, decryptor, CryptoStreamMode.Read);
|
||||
using StreamReader sr = new(cs);
|
||||
return sr.ReadToEnd();
|
||||
}
|
||||
|
||||
public static EncryptionParameters GenerateParameters()
|
||||
{
|
||||
using Aes aes = Aes.Create();
|
||||
aes.KeySize = KeyByteLength * 8;
|
||||
aes.GenerateKey();
|
||||
aes.GenerateIV();
|
||||
return new EncryptionParameters
|
||||
{
|
||||
Key = Convert.ToBase64String(aes.Key),
|
||||
IV = Convert.ToBase64String(aes.IV)
|
||||
};
|
||||
}
|
||||
|
||||
private static byte[] AdjustKeySize(byte[] key, int size)
|
||||
{
|
||||
if (key.Length < size)
|
||||
Array.Resize(ref key, size);
|
||||
else if (key.Length > size)
|
||||
Array.Resize(ref key, size);
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
namespace DigitalData.UserManager.Application.Services.Options
|
||||
{
|
||||
public class EncryptionParameters
|
||||
{
|
||||
public required string Key { get; init; }
|
||||
|
||||
public required string IV { get; init; }
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user