125 lines
4.1 KiB
C#
125 lines
4.1 KiB
C#
// <copyright file="CryptographyService.cs" company="alveus.dev">
|
|
// Copyright (c) alveus.dev. All rights reserved. Licensed under the MIT License.
|
|
// </copyright>
|
|
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using Astral.Services.Constants;
|
|
using Astral.Services.Interfaces;
|
|
using Astral.Services.Options;
|
|
using Injectio.Attributes;
|
|
using Konscious.Security.Cryptography;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Astral.Services.Services;
|
|
|
|
/// <inheritdoc />
|
|
[RegisterSingleton]
|
|
public class CryptographyService : ICryptographyService
|
|
{
|
|
private readonly PwdHashOptions _configuration;
|
|
private readonly ILogger<CryptographyService> _logger;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="CryptographyService" /> class.
|
|
/// </summary>
|
|
/// <param name="pwdHashSettings">Instance of <see cref="IOptions{PwdHashOptions}" />.</param>
|
|
/// <param name="logger">Instance of <see cref="ILogger{CryptographyService}" />.</param>
|
|
public CryptographyService(
|
|
IOptions<PwdHashOptions> pwdHashSettings,
|
|
ILogger<CryptographyService> logger)
|
|
{
|
|
_configuration = pwdHashSettings.Value;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public byte[] GenerateSalt(int? size = null)
|
|
{
|
|
var result = RandomNumberGenerator.GetBytes(size ?? _configuration.SaltSize);
|
|
return result;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public byte[] HashPassword(string password, byte[] salt)
|
|
{
|
|
var argon2Id = new Argon2id(Encoding.UTF8.GetBytes(password));
|
|
argon2Id.Salt = salt;
|
|
argon2Id.DegreeOfParallelism = _configuration.DegreeOfParallelism;
|
|
argon2Id.Iterations = _configuration.NumberOfIterations;
|
|
argon2Id.MemorySize = _configuration.MemoryToUseKb;
|
|
var bytes = argon2Id.GetBytes(_configuration.HashSize);
|
|
GC.Collect();
|
|
return bytes;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool VerifyPassword(string password, byte[] salt, byte[] passwordHash)
|
|
{
|
|
var checkHash = HashPassword(password, salt);
|
|
return passwordHash.SequenceEqual(checkHash);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public bool VerifyPassword(string password, string salt, string passwordHash)
|
|
{
|
|
var checkHash = HashPassword(password, Convert.FromBase64String(salt));
|
|
return Convert.FromBase64String(passwordHash).SequenceEqual(checkHash);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string GenerateRandomString(int length)
|
|
{
|
|
const string availableChars = "ABCDEFGHIJKLMONOPQRSTUVWXYZabcdefghijklmonopqrstuvwxyz0123456789";
|
|
return RandomNumberGenerator.GetString(availableChars, length);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string ConvertPublicKey(byte[] pkcs1Key, PublicKeyType type)
|
|
{
|
|
try
|
|
{
|
|
var rsa = RSA.Create();
|
|
var bytesRead = 0;
|
|
switch (type)
|
|
{
|
|
case PublicKeyType.SpkiX509PublicKey:
|
|
rsa.ImportSubjectPublicKeyInfo(pkcs1Key, out bytesRead);
|
|
break;
|
|
case PublicKeyType.Pkcs1PublicKey:
|
|
rsa.ImportRSAPublicKey(pkcs1Key, out bytesRead);
|
|
break;
|
|
}
|
|
var pem = "";
|
|
if (bytesRead == 0)
|
|
{
|
|
_logger.LogError(
|
|
"An error occured converting RSA public key from binary to SPKI (PEM). Bytes read: 0");
|
|
}
|
|
else
|
|
{
|
|
pem = rsa.ExportSubjectPublicKeyInfoPem();
|
|
}
|
|
|
|
rsa.Clear();
|
|
return pem;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
_logger.LogError("An error occured converting RSA public key from binary to SPKI (PEM). {exception}", e);
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string SimplifyPemKey(string pemKey)
|
|
{
|
|
pemKey = pemKey.Replace("-----BEGIN PUBLIC KEY-----", string.Empty);
|
|
pemKey = pemKey.Replace("-----END PUBLIC KEY-----", string.Empty);
|
|
pemKey = pemKey.Replace("\r", string.Empty);
|
|
pemKey = pemKey.Replace("\n", string.Empty);
|
|
return pemKey;
|
|
}
|
|
}
|