using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; 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 _logger; private readonly IMasterKeyService _masterKeyService; private readonly AkariPath _akariPath; public KeyManager(ILogger 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(string applicationName) { _logger.LogDebug($"Deleted keys for {applicationName}"); _logger.LogDebug($"Going to delete {GetKeyDirectoryPath(applicationName)}"); //Directory.Delete(GetKeyDirectoryPath(applicationName)); } 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 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}"; } }