126 lines
4.3 KiB
C#
126 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Security.Cryptography;
|
|
using System.Threading.Tasks;
|
|
using Akari.Prototype.Server.Models;
|
|
using Akari.Prototype.Server.Utils;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Akari.Prototype.Server.Services
|
|
{
|
|
public sealed class KeyManager : IKeyManager
|
|
{
|
|
public const string KeysPath = "Keys";
|
|
public const int KeyLength = 256 / 8;
|
|
|
|
private readonly ILogger<KeyManager> _logger;
|
|
private readonly IMasterKeyService _masterKeyService;
|
|
private readonly AkariPath _akariPath;
|
|
|
|
public KeyManager(ILogger<KeyManager> logger, IMasterKeyService masterKeyService, AkariPath akariPath)
|
|
{
|
|
_logger = logger;
|
|
_masterKeyService = masterKeyService;
|
|
_akariPath = akariPath;
|
|
|
|
CheckConfig();
|
|
}
|
|
|
|
private void CheckConfig()
|
|
{
|
|
string path = _akariPath.GetPath(KeysPath);
|
|
|
|
if (!Directory.Exists(path))
|
|
{
|
|
Directory.CreateDirectory(path);
|
|
}
|
|
}
|
|
|
|
public bool AddFingerprint(string applicationName, string fingerprintName, AesGcm fingerprintKey)
|
|
{
|
|
if (!Directory.Exists(GetKeyDirectoryPath(applicationName)) || !_masterKeyService.TryGetKey(out var masterKey))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Read key
|
|
var encryptedKeyBytes = File.ReadAllBytes(GetMasterKeyPath(applicationName));
|
|
|
|
var decryptedKeyBytes = Security.AesGcmDecrypt(masterKey, encryptedKeyBytes);
|
|
|
|
var newEncryptedKeyBytes = Security.AesGcmEncrypt(fingerprintKey, decryptedKeyBytes);
|
|
|
|
File.WriteAllBytes(GetKeyPath(applicationName, fingerprintName), newEncryptedKeyBytes);
|
|
|
|
_logger.LogDebug($"Fingerprint '{fingerprintName}' added for {applicationName}");
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Clear(Application application)
|
|
{
|
|
// Do it safely to avoid deleting unwanted files
|
|
File.Delete(GetMasterKeyPath(application.Name));
|
|
|
|
foreach (var fingerprint in application.Fingerprints)
|
|
{
|
|
File.Delete(GetKeyPath(application.Name, fingerprint));
|
|
}
|
|
|
|
Directory.Delete(GetKeyDirectoryPath(application.Name), recursive: false);
|
|
|
|
_logger.LogDebug($"Deleted keys for {application.Name}");
|
|
}
|
|
|
|
public bool Create(string applicationName)
|
|
{
|
|
if (!_masterKeyService.TryGetKey(out var masterKey))
|
|
{
|
|
_logger.LogDebug("Can't create key if master not logged in");
|
|
|
|
return false;
|
|
}
|
|
|
|
Directory.CreateDirectory(GetKeyDirectoryPath(applicationName));
|
|
|
|
Span<byte> keyBytes = KeyLength <= 1024 ? stackalloc byte[KeyLength]
|
|
: new byte[KeyLength];
|
|
|
|
RandomNumberGenerator.Fill(keyBytes);
|
|
|
|
var encryptedKeyBytes = Security.AesGcmEncrypt(masterKey, keyBytes);
|
|
|
|
File.WriteAllBytes(GetMasterKeyPath(applicationName), encryptedKeyBytes);
|
|
|
|
_logger.LogDebug($"Key created for {applicationName}");
|
|
|
|
return true;
|
|
}
|
|
|
|
public AesGcm RetrieveKey(string applicationName, string fingerprintName, AesGcm fingerprintKey)
|
|
{
|
|
if (!Directory.Exists(GetKeyDirectoryPath(applicationName)))
|
|
{
|
|
throw new IOException($"Can't find key files for {applicationName}");
|
|
}
|
|
|
|
// Read key
|
|
var encryptedKeyBytes = File.ReadAllBytes(GetKeyPath(applicationName, fingerprintName));
|
|
|
|
var decryptedKeyBytes = Security.AesGcmDecrypt(fingerprintKey, encryptedKeyBytes);
|
|
|
|
_logger.LogDebug($"Key retrieved for {applicationName} using {fingerprintName}");
|
|
|
|
return new AesGcm(decryptedKeyBytes);
|
|
}
|
|
|
|
private string GetKeyDirectoryPath(string applicationName) => Path.Combine(_akariPath.GetPath(KeysPath), applicationName);
|
|
|
|
private string GetMasterKeyPath(string applicationName) => Path.Combine(GetKeyDirectoryPath(applicationName), "key");
|
|
|
|
private string GetKeyPath(string applicationName, string fingerprintName) => $"{GetMasterKeyPath(applicationName)}-{fingerprintName}";
|
|
}
|
|
}
|