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,12 +6,19 @@ using Microsoft.AspNetCore.Authentication.Cookies;
|
|||||||
using NLog.Web;
|
using NLog.Web;
|
||||||
using NLog;
|
using NLog;
|
||||||
using DigitalData.Core.API;
|
using DigitalData.Core.API;
|
||||||
|
using DigitalData.UserManager.API;
|
||||||
|
using DigitalData.UserManager.API.Controllers;
|
||||||
|
using DigitalData.UserManager.Application.Services;
|
||||||
|
|
||||||
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
var logger = LogManager.Setup().LoadConfigurationFromAppSettings().GetCurrentClassLogger();
|
||||||
logger.Debug("init main");
|
logger.Debug("init main");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
var config = builder.Configuration;
|
||||||
|
|
||||||
|
builder.Services.AddEncryptor(builder.Configuration.GetSection("EncryptionParameters"));
|
||||||
|
|
||||||
if (builder.Configuration.GetValue<bool>("RunAsWindowsService"))
|
if (builder.Configuration.GetValue<bool>("RunAsWindowsService"))
|
||||||
builder.Host.UseWindowsService();
|
builder.Host.UseWindowsService();
|
||||||
@ -27,7 +34,12 @@ try {
|
|||||||
builder.Services.AddSwaggerGen();
|
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)
|
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
.AddCookie(options =>
|
.AddCookie(options =>
|
||||||
@ -39,9 +51,10 @@ try {
|
|||||||
options.LogoutPath = "/api/auth/logout";
|
options.LogoutPath = "/api/auth/logout";
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddDbContext<UserManagerDbContext>(options =>
|
// Once the app is built, the password will be decrypted with Encryptor. lazy loading also acts as a call back method.
|
||||||
options.UseSqlServer(builder.Configuration.GetConnectionString("DD_ECM_Connection"))
|
Lazy<string>? cnn_str = null;
|
||||||
.EnableDetailedErrors());
|
|
||||||
|
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.");
|
var allowedOrigins = builder.Configuration.GetSection("AllowedOrigins").Get<string[]>() ?? throw new InvalidOperationException("In appsettings there is no allowed origin.");
|
||||||
|
|
||||||
@ -64,9 +77,17 @@ try {
|
|||||||
builder.Services.AddDirectorySearchService();
|
builder.Services.AddDirectorySearchService();
|
||||||
|
|
||||||
builder.Services.AddCookieBasedLocalizer();
|
builder.Services.AddCookieBasedLocalizer();
|
||||||
|
|
||||||
var app = builder.Build();
|
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");
|
app.UseCors("DefaultCorsPolicy");
|
||||||
|
|
||||||
if (builder.Configuration.GetValue<bool>("UseSwagger"))
|
if (builder.Configuration.GetValue<bool>("UseSwagger"))
|
||||||
|
|||||||
@ -5,9 +5,6 @@
|
|||||||
"Microsoft.AspNetCore": "Warning"
|
"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" ],
|
"AllowedOrigins": [ "http://localhost:4200" ],
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"Key": "pJBcBWZSjsWlhi1OlCcw6ERTMRNb7qsdvsfvdfbagdfbdfsSDGSDMhsjkfdhsdfbgkHKSDF",
|
"Key": "pJBcBWZSjsWlhi1OlCcw6ERTMRNb7qsdvsfvdfbagdfbdfsSDGSDMhsjkfdhsdfbgkHKSDF",
|
||||||
|
|||||||
@ -6,10 +6,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"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" ],
|
"AllowedOrigins": [ "https://localhost:7103", "http://172.24.12.39:85", "http://localhost:85", "http://localhost:4200", "http://localhost:5500", "https://localhost:7202" ],
|
||||||
"UseSwagger": true,
|
|
||||||
"RunAsWindowsService": false,
|
"RunAsWindowsService": false,
|
||||||
"DirectorySearchOptions": {
|
"DirectorySearchOptions": {
|
||||||
"ServerName": "DD-VMP01-DC01",
|
"ServerName": "DD-VMP01-DC01",
|
||||||
@ -67,5 +66,12 @@
|
|||||||
"writeTo": "criticalLogs"
|
"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.Contracts;
|
||||||
using DigitalData.UserManager.Application.MappingProfiles;
|
using DigitalData.UserManager.Application.MappingProfiles;
|
||||||
using DigitalData.UserManager.Application.Services;
|
using DigitalData.UserManager.Application.Services;
|
||||||
|
using DigitalData.UserManager.Application.Services.Options;
|
||||||
using DigitalData.UserManager.Infrastructure.Contracts;
|
using DigitalData.UserManager.Infrastructure.Contracts;
|
||||||
using DigitalData.UserManager.Infrastructure.Repositories;
|
using DigitalData.UserManager.Infrastructure.Repositories;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace DigitalData.UserManager.Application
|
namespace DigitalData.UserManager.Application
|
||||||
@ -40,5 +42,12 @@ namespace DigitalData.UserManager.Application
|
|||||||
.AddScoped<IModuleService, ModuleService>()
|
.AddScoped<IModuleService, ModuleService>()
|
||||||
.AddScoped<IModuleOfUserService, ModuleOfUserService>()
|
.AddScoped<IModuleOfUserService, ModuleOfUserService>()
|
||||||
.AddScoped<IUserRepService, UserRepService>();
|
.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.Core.Application" Version="1.0.0" />
|
||||||
<PackageReference Include="DigitalData.EmailProfilerDispatcher.Abstraction" 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.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" Version="7.0.1" />
|
||||||
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
|
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
|
||||||
<PackageReference Include="System.DirectoryServices.Protocols" 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