Compare commits

...

1 Commits

Author SHA1 Message Date
638e680a97 Upgrade dependencies, add encrypted connection strings
- Updated NuGet packages across projects for compatibility and bug fixes
- Switched to Microsoft.OpenApi and refactored Swagger setup
- Added AES encryption/decryption for connection strings
- Stored encrypted DB/cache connection strings in config
- Improved encryption reliability in Program.cs
- Added AutoMapper to multiple projects for mapping support
- Enhanced security and code maintainability
2026-03-14 22:57:00 +05:30
10 changed files with 72 additions and 66 deletions

View File

@ -94,10 +94,10 @@ static string EncryptDataWithAes(string plainText, string keyBase64, out string
{ {
using (StreamWriter sw = new StreamWriter(cs)) using (StreamWriter sw = new StreamWriter(cs))
{ {
sw.Write(plainText); sw.Write(plainText); // Missing explicit flush before dispose
} }
encryptedData = ms.ToArray();
} }
encryptedData = ms.ToArray();
} }
return Convert.ToBase64String(encryptedData); return Convert.ToBase64String(encryptedData);

View File

@ -1,6 +1,6 @@
using Asp.Versioning.ApiExplorer; using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using Swashbuckle.AspNetCore.SwaggerGen; using Swashbuckle.AspNetCore.SwaggerGen;
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions> public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>

View File

@ -11,22 +11,23 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.1" /> <PackageReference Include="Asp.Versioning.Mvc" Version="8.1.1" />
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" /> <PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
<PackageReference Include="AutoMapper" Version="16.0.0" /> <PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" /> <PackageReference Include="BCrypt.Net-Next" Version="4.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="9.0.9" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.9" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.9"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.5">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Otp.NET" Version="1.4.0" /> <PackageReference Include="Microsoft.OpenApi" Version="2.4.1" />
<PackageReference Include="Otp.NET" Version="1.4.1" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0" />
<PackageReference Include="Razor.Templating.Core" Version="2.1.0" /> <PackageReference Include="Razor.Templating.Core" Version="3.1.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="serilog.sinks.console" Version="6.0.0" /> <PackageReference Include="serilog.sinks.console" Version="6.1.1" />
<PackageReference Include="serilog.sinks.file" Version="7.0.0" /> <PackageReference Include="serilog.sinks.file" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.FileProviders;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi;
using PortBlog.API.DbContexts; using PortBlog.API.DbContexts;
using PortBlog.API.Extensions; using PortBlog.API.Extensions;
using PortBlog.API.Middleware; using PortBlog.API.Middleware;
@ -13,6 +13,7 @@ using Serilog;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using KBR.Shared.Extensions; using KBR.Shared.Extensions;
using Swashbuckle.AspNetCore.SwaggerGen;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -143,15 +144,6 @@ builder.Services.AddSwaggerGen(c =>
Scheme = "ApiKeyScheme" Scheme = "ApiKeyScheme"
}); });
var key = new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey"
}
};
// JWT Bearer Security Definition // JWT Bearer Security Definition
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{ {
@ -162,23 +154,6 @@ builder.Services.AddSwaggerGen(c =>
Scheme = "Bearer", Scheme = "Bearer",
BearerFormat = "JWT" BearerFormat = "JWT"
}); });
var bearerScheme = new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
};
var requirement = new OpenApiSecurityRequirement
{
{ key, new List<string>() },
{ bearerScheme, new List<string>() }
};
c.AddSecurityRequirement(requirement);
}); });
builder.Services.ConfigureOptions<ConfigureSwaggerOptions>(); builder.Services.ConfigureOptions<ConfigureSwaggerOptions>();

View File

@ -1,12 +1,12 @@
{ {
"ConnectionStrings": { "ConnectionStrings": {
"PortBlogDBConnectionString": "SERVER=192.168.0.197; DATABASE=cv_blog; UID=PortBlogDevUser; PWD=p@$$w0rd1234", "PortBlogDBConnectionString": "XHIrOTmGNBQ8xE5DPwiEvtoNkU5YT6HsgwPYYawv7sRsMCnPGrAxbaiUZZ4mveUBP5yIAzzU5KSXK5XeoA9xxXugBF3nji2icvCQvoFxRqc=:CbDuLYbh/dAGyNU38qCHBQ==",
"Encryption": "false", "Encryption": "true",
"Key": "rgdBsYjrgQV9YaE+6QFK5oyTOWwbl2bSWkuc2JXcIyw=" "Key": "rgdBsYjrgQV9YaE+6QFK5oyTOWwbl2bSWkuc2JXcIyw="
}, },
"Cache": { "Cache": {
"ConnectionString": "SERVER=192.168.0.197; DATABASE=cv_blog; UID=PortBlogDevUser; PWD=p@$$w0rd1234;Allow User Variables=true;", "ConnectionString": "pxv9z73rYXioZIToJ4A4CfpmR3m6OeQ5CD5bdjXAKuH1UBu/RvdOPuUnxokDytq/vlDYfDdrsffNAs5t5r6VSVD/H/bvgGQV+I3X2mzo1343XbJfncPMORbJPP/tSdl0Yu5R5pMhrSXnG7vOHPu+Bw==:gHnavGn1ASf0Sovu3zquGg==",
"Encryption": "false", "Encryption": "true",
"Provider": "SqlServer" "Provider": "SqlServer"
}, },
"Jwt": { "Jwt": {

View File

@ -8,11 +8,12 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="coverlet.collector" Version="8.0.0"> <PackageReference Include="coverlet.collector" Version="8.0.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageReference Include="xunit" Version="2.9.3" /> <PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5"> <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View File

@ -7,14 +7,15 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.3" /> <PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.5" />
<PackageReference Include="Pomelo.Extensions.Caching.MySql" Version="2.2.1" /> <PackageReference Include="Pomelo.Extensions.Caching.MySql" Version="2.2.1" />
</ItemGroup> </ItemGroup>

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup> </ItemGroup>

View File

@ -1,13 +1,45 @@
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text;
namespace KBR.Shared.Extensions namespace KBR.Shared.Extensions
{ {
public static class ConfigurationExtensions public static class ConfigurationExtensions
{ {
public static string EncryptConnectionString(this IConfiguration configuration, string plainConnectionString)
{
string keyBase64 = configuration.GetSection("ConnectionStrings:Key").Value;
using (Aes aesAlgorithm = Aes.Create())
{
aesAlgorithm.Key = Convert.FromBase64String(keyBase64);
aesAlgorithm.GenerateIV();
aesAlgorithm.Mode = CipherMode.CBC;
aesAlgorithm.Padding = PaddingMode.PKCS7;
ICryptoTransform encryptor = aesAlgorithm.CreateEncryptor();
byte[] encryptedData;
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
byte[] plainBytes = Encoding.UTF8.GetBytes(plainConnectionString);
cs.Write(plainBytes, 0, plainBytes.Length);
cs.FlushFinalBlock(); // Explicitly apply padding
}
encryptedData = ms.ToArray();
}
string cipherBase64 = Convert.ToBase64String(encryptedData);
string ivBase64 = Convert.ToBase64String(aesAlgorithm.IV);
return $"{cipherBase64}:{ivBase64}";
}
}
public static string DecryptConnectionString(this IConfiguration configuration, string encryptedConnectionString) public static string DecryptConnectionString(this IConfiguration configuration, string encryptedConnectionString)
{ {
// Fix: Use GetSection and Value instead of GetValue (since GetValue is not available on IConfiguration)
string keyBase64 = configuration.GetSection("ConnectionStrings:Key").Value; string keyBase64 = configuration.GetSection("ConnectionStrings:Key").Value;
string vectorBase64 = encryptedConnectionString.Split(":")[1]; string vectorBase64 = encryptedConnectionString.Split(":")[1];
@ -17,24 +49,18 @@ namespace KBR.Shared.Extensions
{ {
aesAlgorithm.Key = Convert.FromBase64String(keyBase64); aesAlgorithm.Key = Convert.FromBase64String(keyBase64);
aesAlgorithm.IV = Convert.FromBase64String(vectorBase64); aesAlgorithm.IV = Convert.FromBase64String(vectorBase64);
aesAlgorithm.Mode = CipherMode.CBC;
aesAlgorithm.Padding = PaddingMode.PKCS7;
Console.WriteLine($"Aes Cipher Mode : {aesAlgorithm.Mode}");
Console.WriteLine($"Aes Padding Mode: {aesAlgorithm.Padding}");
Console.WriteLine($"Aes Key Size : {aesAlgorithm.KeySize}");
Console.WriteLine($"Aes Block Size : {aesAlgorithm.BlockSize}");
// Create decryptor object
ICryptoTransform decryptor = aesAlgorithm.CreateDecryptor(); ICryptoTransform decryptor = aesAlgorithm.CreateDecryptor();
byte[] cipher = Convert.FromBase64String(cipherText); byte[] cipher = Convert.FromBase64String(cipherText);
//Decryption will be done in a memory stream through a CryptoStream object
using (MemoryStream ms = new MemoryStream(cipher)) using (MemoryStream ms = new MemoryStream(cipher))
{ {
using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) using (CryptoStream cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{ {
using (StreamReader sr = new StreamReader(cs)) using (StreamReader sr = new StreamReader(cs, Encoding.UTF8))
{ {
return sr.ReadToEnd(); return sr.ReadToEnd();
} }

View File

@ -7,8 +7,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.3" /> <PackageReference Include="AutoMapper" Version="16.1.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>