Encrypt NRIC

This commit is contained in:
2025-02-08 01:07:45 +08:00
parent fdfea2b1f0
commit 3c0f021714
8 changed files with 238 additions and 10 deletions

View File

@@ -1,7 +1,6 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using System.Text.RegularExpressions;
using AceJobAgency.Data;
using AceJobAgency.Entities;
using AceJobAgency.Utilities;
@@ -17,11 +16,13 @@ namespace AceJobAgency.Controllers
{
private readonly DataContext _context;
private readonly IConfiguration _configuration;
private readonly IEncryptionService _encryptionService;
public UserController(DataContext context, IConfiguration configuration)
public UserController(DataContext context, IConfiguration configuration, IEncryptionService encryptionService)
{
_context = context;
_configuration = configuration;
_encryptionService = encryptionService;
}
[HttpPost("register")]
@@ -32,9 +33,10 @@ namespace AceJobAgency.Controllers
return BadRequest("Password must be at least 12 characters long and include uppercase, lowercase, number, and special character.");
}
var encryptedNric = _encryptionService.Encrypt(user.NationalRegistrationIdentityCardNumber);
var emailExists = _context.Users.Any(u => u.Email == user.Email);
var nricExists = _context.Users.Any(u =>
u.NationalRegistrationIdentityCardNumber == user.NationalRegistrationIdentityCardNumber);
var nricExists = _context.Users.Any(u => u.NationalRegistrationIdentityCardNumber == encryptedNric);
if (emailExists || nricExists)
{
return BadRequest("User with the same email or NRIC already exists.");
@@ -43,10 +45,23 @@ namespace AceJobAgency.Controllers
user.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
user.Id = Guid.NewGuid().ToString();
user.IsActive = 1;
user.NationalRegistrationIdentityCardNumber = encryptedNric;
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
return Ok(user);
var response = new
{
user.Id,
user.Email,
NationalRegistrationIdentityCardNumber = _encryptionService.Decrypt(user.NationalRegistrationIdentityCardNumber),
user.FirstName,
user.LastName,
user.DateOfBirth,
user.WhoAmI,
user.ResumeName,
};
return Ok(response);
}
[HttpPost("login")]
@@ -72,7 +87,20 @@ namespace AceJobAgency.Controllers
{
return NotFound();
}
return Ok(user);
var decryptedNric = _encryptionService.Decrypt(user.NationalRegistrationIdentityCardNumber);
var response = new
{
user.Id,
user.Email,
NationalRegistrationIdentityCardNumber = decryptedNric,
user.FirstName,
user.LastName,
user.DateOfBirth,
user.WhoAmI,
user.ResumeName,
};
return Ok(response);
}
[Authorize]

View File

@@ -20,7 +20,7 @@ namespace AceJobAgency.Entities
public required int Gender { get; set; }
[Required]
[StringLength(9, MinimumLength = 9)]
[StringLength(255)]
public required string NationalRegistrationIdentityCardNumber { get; set; }
[Required]

View File

@@ -0,0 +1,91 @@
// <auto-generated />
using System;
using AceJobAgency.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace AceJobAgency.Migrations
{
[DbContext(typeof(DataContext))]
[Migration("20250207154242_UpdatedNRIC")]
partial class UpdatedNRIC
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.2")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
modelBuilder.Entity("AceJobAgency.Entities.User", b =>
{
b.Property<string>("Id")
.HasMaxLength(36)
.HasColumnType("varchar(36)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("DateOfBirth")
.HasColumnType("datetime(6)");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<int>("Gender")
.HasColumnType("int");
b.Property<int>("IsActive")
.HasColumnType("int");
b.Property<string>("LastName")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)");
b.Property<string>("NationalRegistrationIdentityCardNumber")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("Password")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<string>("ResumeName")
.IsRequired()
.HasMaxLength(128)
.HasColumnType("varchar(128)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("WhoAmI")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.HasKey("Id");
b.ToTable("Users");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,42 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace AceJobAgency.Migrations
{
/// <inheritdoc />
public partial class UpdatedNRIC : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "NationalRegistrationIdentityCardNumber",
table: "Users",
type: "varchar(255)",
maxLength: 255,
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(9)",
oldMaxLength: 9)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.AlterColumn<string>(
name: "NationalRegistrationIdentityCardNumber",
table: "Users",
type: "varchar(9)",
maxLength: 9,
nullable: false,
oldClrType: typeof(string),
oldType: "varchar(255)",
oldMaxLength: 255)
.Annotation("MySql:CharSet", "utf8mb4")
.OldAnnotation("MySql:CharSet", "utf8mb4");
}
}
}

View File

@@ -57,8 +57,8 @@ namespace AceJobAgency.Migrations
b.Property<string>("NationalRegistrationIdentityCardNumber")
.IsRequired()
.HasMaxLength(9)
.HasColumnType("varchar(9)");
.HasMaxLength(255)
.HasColumnType("varchar(255)");
b.Property<string>("Password")
.IsRequired()

View File

@@ -1,5 +1,6 @@
using System.Text;
using AceJobAgency.Data;
using AceJobAgency.Utilities;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
@@ -60,6 +61,8 @@ builder.Services.AddSwaggerGen(options =>
});
});
builder.Services.AddScoped<IEncryptionService, AesEncryptionService>();
var app = builder.Build();
// Configure the HTTP request pipeline.

View File

@@ -0,0 +1,61 @@
using System.Security.Cryptography;
namespace AceJobAgency.Utilities;
public interface IEncryptionService
{
string Encrypt(string plainText);
string Decrypt(string cipherText);
}
public class AesEncryptionService : IEncryptionService
{
private readonly byte[] _key;
public AesEncryptionService(IConfiguration configuration)
{
var encryptionKey = configuration["Encryption:Key"];
if (string.IsNullOrEmpty(encryptionKey))
throw new ArgumentException("Encryption key is not configured.");
_key = Convert.FromBase64String(encryptionKey);
}
public string Encrypt(string plainText)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.GenerateIV();
using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs))
sw.Write(plainText);
var encryptedBytes = ms.ToArray();
var iv = aes.IV;
var combined = new byte[iv.Length + encryptedBytes.Length];
Buffer.BlockCopy(iv, 0, combined, 0, iv.Length);
Buffer.BlockCopy(encryptedBytes, 0, combined, iv.Length, encryptedBytes.Length);
return Convert.ToBase64String(combined);
}
public string Decrypt(string cipherText)
{
var combined = Convert.FromBase64String(cipherText);
var iv = new byte[16];
var encryptedBytes = new byte[combined.Length - iv.Length];
Buffer.BlockCopy(combined, 0, iv, 0, iv.Length);
Buffer.BlockCopy(combined, iv.Length, encryptedBytes, 0, encryptedBytes.Length);
using var aes = Aes.Create();
aes.Key = _key;
aes.IV = iv;
using var decryptor = aes.CreateDecryptor(aes.Key, aes.IV);
using var ms = new MemoryStream(encryptedBytes);
using var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read);
using var sr = new StreamReader(cs);
return sr.ReadToEnd();
}
}

View File

@@ -5,6 +5,9 @@
"Authentication": {
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
},
"Encryption": {
"Key": "laVspGq/dNFECeUP93fUF05T422cLjieKPotTI8Wgfc="
},
"Logging": {
"LogLevel": {
"Default": "Information",