Encrypt NRIC
This commit is contained in:
@@ -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]
|
||||
@@ -174,4 +202,4 @@ namespace AceJobAgency.Controllers
|
||||
public string CurrentPassword { get; set; } = string.Empty;
|
||||
public string NewPassword { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]
|
||||
|
||||
91
AceJobAgency/Migrations/20250207154242_UpdatedNRIC.Designer.cs
generated
Normal file
91
AceJobAgency/Migrations/20250207154242_UpdatedNRIC.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
42
AceJobAgency/Migrations/20250207154242_UpdatedNRIC.cs
Normal file
42
AceJobAgency/Migrations/20250207154242_UpdatedNRIC.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
61
AceJobAgency/Utilities/Cryptography.cs
Normal file
61
AceJobAgency/Utilities/Cryptography.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@
|
||||
"Authentication": {
|
||||
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
|
||||
},
|
||||
"Encryption": {
|
||||
"Key": "laVspGq/dNFECeUP93fUF05T422cLjieKPotTI8Wgfc="
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
||||
Reference in New Issue
Block a user