Sprint 8 #43
@@ -35,8 +35,9 @@
|
||||
"ExpiredAfterMinsOfInactivity": 15
|
||||
},
|
||||
"EncryptionSettings": {
|
||||
"Salt": "S7VIidfXQf1tOQYX",
|
||||
"Pepper": ""
|
||||
"Secret": "S7VIidfXQf1tOQYX",
|
||||
"Salt": "",
|
||||
"Iterations": 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@
|
||||
"ExpiredAfterMinsOfInactivity": 15
|
||||
},
|
||||
"EncryptionSettings": {
|
||||
"Salt": "AAAAA",
|
||||
"Pepper": ""
|
||||
"Secret": "AAAAA",
|
||||
"Salt": "",
|
||||
"Iterations": 10
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,9 @@ public static class ModelsInit
|
||||
FirstName = "FirstName",
|
||||
LastName = "LastName",
|
||||
Email = "test-new@email.it",
|
||||
PasswordHash = "PasswordHash",
|
||||
PasswordPepper = "PasswordPepper",
|
||||
PasswordSalt = "PasswordSalt",
|
||||
PasswordIterations = 0,
|
||||
Password = "Password",
|
||||
Role = CreateRole(),
|
||||
IsTestUser = true
|
||||
|
||||
@@ -102,11 +102,11 @@ public class CryptoUtils_Tests
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void GenerateSalt()
|
||||
public void GeneratePepper()
|
||||
{
|
||||
try
|
||||
{
|
||||
var salt = CryptUtils.GenerateSalt();
|
||||
var salt = CryptUtils.GeneratePepper();
|
||||
Assert.IsTrue(!String.IsNullOrEmpty(salt));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -122,14 +122,14 @@ public class CryptoUtils_Tests
|
||||
try
|
||||
{
|
||||
var password = "P4ssw0rd@1!";
|
||||
var salt = CryptUtils.GenerateSalt();
|
||||
Assert.IsTrue(!String.IsNullOrEmpty(salt));
|
||||
var pepper = CryptUtils.GeneratePepper();
|
||||
Assert.IsTrue(!String.IsNullOrEmpty(pepper));
|
||||
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
|
||||
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||
var encryptedPassword = cryptoUtils.GeneratePassword(password, salt, 0);
|
||||
Assert.IsTrue(password != encryptedPassword);
|
||||
var salt = appSettings?.EncryptionSettings?.Salt ?? String.Empty;
|
||||
var encryptedPassword = CryptUtils.GeneratePassword(password, salt, 0, pepper);
|
||||
Assert.AreNotEqual(encryptedPassword, password);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -147,10 +147,7 @@ public class CryptoUtils_Tests
|
||||
var salt = "Afi7PQYgEL2sPbNyVzduvg==";
|
||||
var hashedPassword = "2lMeySZ9ciH1KtSg1Z7oSJRmJEjHMeDvdaNRcJcGutM=";
|
||||
|
||||
WebApplicationBuilder builder = WebApplication.CreateBuilder(Array.Empty<string>());
|
||||
AppSettings appSettings = ProgramUtils.AddConfiguration(ref builder, System.AppDomain.CurrentDomain.BaseDirectory + "/JsonData");
|
||||
CryptUtils cryptoUtils = new CryptUtils(appSettings);
|
||||
var verified = cryptoUtils.VerifyPassword(password, salt, 0, hashedPassword);
|
||||
var verified = CryptUtils.VerifyPassword(hashedPassword, password, salt, 0);
|
||||
Assert.IsTrue(verified);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -13,7 +13,10 @@ public class User : Base
|
||||
[MaxLength(200)]
|
||||
public required string Email { get; set; }
|
||||
public required string PasswordSalt { get; set; }
|
||||
public required string PasswordHash { get; set; }
|
||||
#nullable enable
|
||||
public string? PasswordPepper { get; set; }
|
||||
#nullable disable
|
||||
public required int PasswordIterations { get; set; }
|
||||
public required Role Role { get; set; }
|
||||
public required bool IsTestUser { get; set; }
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ namespace BasicDotnetTemplate.MainProject.Models.Settings;
|
||||
public class EncryptionSettings
|
||||
{
|
||||
#nullable enable
|
||||
public string? Secret { get; set; }
|
||||
public string? Salt { get; set; }
|
||||
public string? Pepper { get; set; }
|
||||
public int? Iterations { get; set; }
|
||||
#nullable disable
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using BasicDotnetTemplate.MainProject.Core.Database;
|
||||
using BasicDotnetTemplate.MainProject.Models.Api.Common.Exceptions;
|
||||
using BasicDotnetTemplate.MainProject.Models.Api.Data.User;
|
||||
using BasicDotnetTemplate.MainProject.Models.Database.SqlServer;
|
||||
using BasicDotnetTemplate.MainProject.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BasicDotnetTemplate.MainProject.Services;
|
||||
@@ -21,12 +22,15 @@ public interface IUserService
|
||||
public class UserService : BaseService, IUserService
|
||||
{
|
||||
private readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
private readonly CryptUtils _cryptUtils;
|
||||
public UserService(
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IConfiguration configuration,
|
||||
SqlServerContext sqlServerContext
|
||||
) : base(httpContextAccessor, configuration, sqlServerContext)
|
||||
{ }
|
||||
{
|
||||
this._cryptUtils = new(_appSettings);
|
||||
}
|
||||
|
||||
private IQueryable<User> GetUsersQueryable()
|
||||
{
|
||||
@@ -51,8 +55,9 @@ public class UserService : BaseService, IUserService
|
||||
FirstName = data.FirstName,
|
||||
LastName = data.LastName,
|
||||
Email = data.Email,
|
||||
PasswordSalt = "",
|
||||
PasswordHash = "",
|
||||
PasswordSalt = _appSettings.EncryptionSettings?.Salt ?? String.Empty,
|
||||
PasswordPepper = CryptUtils.GeneratePepper(),
|
||||
PasswordIterations = _appSettings.EncryptionSettings?.Iterations ?? 10,
|
||||
Password = "",
|
||||
Role = role,
|
||||
IsTestUser = false
|
||||
@@ -77,7 +82,9 @@ public class UserService : BaseService, IUserService
|
||||
User? user = await this.GetUserByEmailQueryable(email).FirstOrDefaultAsync();
|
||||
if (user != null)
|
||||
{
|
||||
var encryptedPassword = user.PasswordHash;
|
||||
var valid = CryptUtils.VerifyPassword(user.Password, password, user.PasswordSalt, user.PasswordIterations, user.PasswordPepper);
|
||||
if (!valid)
|
||||
user = null;
|
||||
}
|
||||
|
||||
return user;
|
||||
|
||||
@@ -4,24 +4,17 @@ using System.Text;
|
||||
using BasicDotnetTemplate.MainProject.Models.Settings;
|
||||
|
||||
namespace BasicDotnetTemplate.MainProject.Utils;
|
||||
public class CryptUtils
|
||||
public class CryptUtils(AppSettings appSettings)
|
||||
{
|
||||
private readonly string _secretKey;
|
||||
private readonly string _pepper;
|
||||
private readonly string _secret = appSettings.EncryptionSettings?.Secret ?? String.Empty;
|
||||
private const int _M = 16;
|
||||
private const int _N = 32;
|
||||
|
||||
public CryptUtils(AppSettings appSettings)
|
||||
{
|
||||
_secretKey = appSettings.EncryptionSettings?.Salt ?? String.Empty;
|
||||
_pepper = appSettings.EncryptionSettings?.Pepper ?? String.Empty;
|
||||
}
|
||||
|
||||
public string Decrypt(string encryptedData)
|
||||
{
|
||||
var decrypted = String.Empty;
|
||||
|
||||
if (String.IsNullOrEmpty(this._secretKey) || this._secretKey.Length < _M)
|
||||
if (String.IsNullOrEmpty(this._secret) || this._secret.Length < _M)
|
||||
{
|
||||
throw new ArgumentException("Unable to proceed with decryption due to invalid settings");
|
||||
}
|
||||
@@ -35,7 +28,7 @@ public class CryptUtils
|
||||
|
||||
using (var aes = Aes.Create())
|
||||
{
|
||||
aes.Key = Encoding.UTF8.GetBytes(this._secretKey);
|
||||
aes.Key = Encoding.UTF8.GetBytes(this._secret);
|
||||
aes.IV = Encoding.UTF8.GetBytes(iv);
|
||||
|
||||
using (var decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
|
||||
@@ -57,21 +50,21 @@ public class CryptUtils
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
public static string GenerateSalt()
|
||||
public static string GeneratePepper()
|
||||
{
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
var byteSalt = new byte[16];
|
||||
rng.GetBytes(byteSalt);
|
||||
var salt = Convert.ToBase64String(byteSalt);
|
||||
return salt;
|
||||
var bytePepper = new byte[16];
|
||||
rng.GetBytes(bytePepper);
|
||||
var pepper = Convert.ToBase64String(bytePepper);
|
||||
return pepper;
|
||||
}
|
||||
|
||||
public string GeneratePassword(string password, string salt, int iteration)
|
||||
public static string GeneratePassword(string password, string salt, int iterations, string? pepper = "")
|
||||
{
|
||||
string hashedPassword = password;
|
||||
for(var i = 0; i <= iteration; i++)
|
||||
for (var i = 0; i <= iterations; i++)
|
||||
{
|
||||
var passwordSaltPepper = $"{hashedPassword}{salt}{this._pepper}";
|
||||
var passwordSaltPepper = $"{hashedPassword}{salt}{pepper}";
|
||||
var byteValue = Encoding.UTF8.GetBytes(passwordSaltPepper);
|
||||
var byteHash = SHA256.HashData(byteValue);
|
||||
hashedPassword = Convert.ToBase64String(byteHash);
|
||||
@@ -80,9 +73,9 @@ public class CryptUtils
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
public bool VerifyPassword(string password, string salt, int iteration, string userPassword)
|
||||
public static bool VerifyPassword(string userPassword, string password, string salt, int iterations, string? pepper = "")
|
||||
{
|
||||
string hashedPassword = this.GeneratePassword(password, salt, iteration);
|
||||
string hashedPassword = GeneratePassword(password, salt, iterations, pepper);
|
||||
return hashedPassword.Equals(userPassword, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
@@ -35,8 +35,9 @@
|
||||
"ExpiredAfterMinsOfInactivity": 15
|
||||
},
|
||||
"EncryptionSettings": {
|
||||
"Salt": "S7VIidfXQf1tOQYX",
|
||||
"Pepper": ""
|
||||
"Secret": "S7VIidfXQf1tOQYX",
|
||||
"Salt": "",
|
||||
"Iterations": 10
|
||||
},
|
||||
"PermissionsSettings": {
|
||||
"FilePath": "Config/permissions.json"
|
||||
|
||||
Reference in New Issue
Block a user