Private
Public Access
1
0
Files
Purrse/src/Purrse.Api/Services/EncryptionService.cs
T
Catherine Renelle 3e0446c9be Implement Phase 6: automated bank sync (Plaid + SimpleFIN) & settings
Add automated transaction syncing via Plaid and SimpleFIN providers,
a Settings page for managing connections and account mappings,
background scheduled syncing, and sync history logging.

New backend: BankSync plugin project with PlaidSyncProvider and
SimpleFinSyncProvider, SyncConnection/LinkedAccount/SyncLog models,
AES-256 encryption service, BankSyncService orchestration,
BankSyncBackgroundService (15-min tick), and BankSyncController
(13 endpoints). EF migration creates sync_connections,
linked_accounts, and sync_logs tables.

New frontend: Settings view with 3 tabs (Profile, Bank Connections,
Sync History), bankSync API service, BankSyncComplete SignalR
notifications, and Settings nav item in sidebar + avatar dropdown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 15:08:23 -05:00

55 lines
1.8 KiB
C#

using System.Security.Cryptography;
using System.Text;
using Purrse.Core.Interfaces.Services;
namespace Purrse.Api.Services;
public class EncryptionService : IEncryptionService
{
private readonly byte[] _key;
public EncryptionService(IConfiguration configuration)
{
var keyString = configuration["BankSync:EncryptionKey"]
?? throw new InvalidOperationException("BankSync:EncryptionKey is not configured");
_key = SHA256.HashData(Encoding.UTF8.GetBytes(keyString));
}
public string Encrypt(string plainText)
{
using var aes = Aes.Create();
aes.Key = _key;
aes.GenerateIV();
using var encryptor = aes.CreateEncryptor();
var plainBytes = Encoding.UTF8.GetBytes(plainText);
var cipherBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);
// Prepend IV to ciphertext
var result = new byte[aes.IV.Length + cipherBytes.Length];
Buffer.BlockCopy(aes.IV, 0, result, 0, aes.IV.Length);
Buffer.BlockCopy(cipherBytes, 0, result, aes.IV.Length, cipherBytes.Length);
return Convert.ToBase64String(result);
}
public string Decrypt(string cipherText)
{
var fullCipher = Convert.FromBase64String(cipherText);
using var aes = Aes.Create();
aes.Key = _key;
var iv = new byte[aes.BlockSize / 8];
var cipher = new byte[fullCipher.Length - iv.Length];
Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, cipher.Length);
aes.IV = iv;
using var decryptor = aes.CreateDecryptor();
var plainBytes = decryptor.TransformFinalBlock(cipher, 0, cipher.Length);
return Encoding.UTF8.GetString(plainBytes);
}
}