Encrypt NRIC
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using AceJobAgency.Data;
|
using AceJobAgency.Data;
|
||||||
using AceJobAgency.Entities;
|
using AceJobAgency.Entities;
|
||||||
using AceJobAgency.Utilities;
|
using AceJobAgency.Utilities;
|
||||||
@@ -17,11 +16,13 @@ namespace AceJobAgency.Controllers
|
|||||||
{
|
{
|
||||||
private readonly DataContext _context;
|
private readonly DataContext _context;
|
||||||
private readonly IConfiguration _configuration;
|
private readonly IConfiguration _configuration;
|
||||||
|
private readonly IEncryptionService _encryptionService;
|
||||||
|
|
||||||
public UserController(DataContext context, IConfiguration configuration)
|
public UserController(DataContext context, IConfiguration configuration, IEncryptionService encryptionService)
|
||||||
{
|
{
|
||||||
_context = context;
|
_context = context;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
|
_encryptionService = encryptionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
[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.");
|
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 emailExists = _context.Users.Any(u => u.Email == user.Email);
|
||||||
var nricExists = _context.Users.Any(u =>
|
var nricExists = _context.Users.Any(u => u.NationalRegistrationIdentityCardNumber == encryptedNric);
|
||||||
u.NationalRegistrationIdentityCardNumber == user.NationalRegistrationIdentityCardNumber);
|
|
||||||
if (emailExists || nricExists)
|
if (emailExists || nricExists)
|
||||||
{
|
{
|
||||||
return BadRequest("User with the same email or NRIC already exists.");
|
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.Password = BCrypt.Net.BCrypt.HashPassword(user.Password);
|
||||||
user.Id = Guid.NewGuid().ToString();
|
user.Id = Guid.NewGuid().ToString();
|
||||||
user.IsActive = 1;
|
user.IsActive = 1;
|
||||||
|
user.NationalRegistrationIdentityCardNumber = encryptedNric;
|
||||||
|
|
||||||
await _context.Users.AddAsync(user);
|
await _context.Users.AddAsync(user);
|
||||||
await _context.SaveChangesAsync();
|
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")]
|
[HttpPost("login")]
|
||||||
@@ -72,7 +87,20 @@ namespace AceJobAgency.Controllers
|
|||||||
{
|
{
|
||||||
return NotFound();
|
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]
|
[Authorize]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ namespace AceJobAgency.Entities
|
|||||||
public required int Gender { get; set; }
|
public required int Gender { get; set; }
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(9, MinimumLength = 9)]
|
[StringLength(255)]
|
||||||
public required string NationalRegistrationIdentityCardNumber { get; set; }
|
public required string NationalRegistrationIdentityCardNumber { get; set; }
|
||||||
|
|
||||||
[Required]
|
[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")
|
b.Property<string>("NationalRegistrationIdentityCardNumber")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(9)
|
.HasMaxLength(255)
|
||||||
.HasColumnType("varchar(9)");
|
.HasColumnType("varchar(255)");
|
||||||
|
|
||||||
b.Property<string>("Password")
|
b.Property<string>("Password")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using AceJobAgency.Data;
|
using AceJobAgency.Data;
|
||||||
|
using AceJobAgency.Utilities;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
@@ -60,6 +61,8 @@ builder.Services.AddSwaggerGen(options =>
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Services.AddScoped<IEncryptionService, AesEncryptionService>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// 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": {
|
"Authentication": {
|
||||||
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
|
"Secret": "b8bc713a-d8d1-4d37-911e-1bb931d70ba5"
|
||||||
},
|
},
|
||||||
|
"Encryption": {
|
||||||
|
"Key": "laVspGq/dNFECeUP93fUF05T422cLjieKPotTI8Wgfc="
|
||||||
|
},
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
|
|||||||
Reference in New Issue
Block a user