using Microsoft.Data.Sqlite; using System.Security.Cryptography; using System.Text; namespace SimPas2_Windows.Managers { internal class CryptoManager { private readonly string mConnectionString; private readonly byte[] mEncryptionKey; private readonly string mCulture; private string mJpLang; public CryptoManager(string databasePath, byte[] encryptionKey, string culture) { mConnectionString = $"Data Source={databasePath}"; mEncryptionKey = encryptionKey ?? throw new ArgumentNullException(nameof(encryptionKey)); mCulture = culture; mJpLang = "ja-JP"; } private bool AlreadyExists(string currency, string name, int? excludeId = null) { using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" SELECT COUNT(*) FROM Crypto WHERE UPPER(Currency) = UPPER(@currency) AND UPPER(Name) = UPPER(@name)"; com.Parameters.AddWithValue("@currency", currency); com.Parameters.AddWithValue("@name", name); if (excludeId.HasValue) { com.CommandText += " AND Id != @excludeId"; com.Parameters.AddWithValue("@excludeId", excludeId.Value); } return Convert.ToInt32(com.ExecuteScalar()) > 0; } } public void AddCrypto(string currency, string name, string address, string seed, string viewkey, string spendkey, string height, string password, string note) { if (string.IsNullOrWhiteSpace(currency) || string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(address)) { string err = mCulture == mJpLang ? "通貨、ウォレット名及び、住所を御入力下さい。" : "Please fill in the currency, wallet name, and address."; throw new ArgumentException(err); } string encryptedPassword = !string.IsNullOrWhiteSpace(password) ? EncryptInfo(password) : ""; string encryptedSeed = !string.IsNullOrWhiteSpace(seed) ? EncryptInfo(seed) : ""; string encryptedViewkey = !string.IsNullOrWhiteSpace(viewkey) ? EncryptInfo(viewkey) : ""; string encryptedSpendkey = !string.IsNullOrWhiteSpace(spendkey) ? EncryptInfo(spendkey) : ""; if (AlreadyExists(currency, name)) { string err = mCulture == mJpLang ? $"通貨及びウォレット名「{currency}/{name}」エントリは既に存在します。" : $"An entry with the currency and wallet name for '{currency}/{name}' already exists."; throw new ArgumentException(err); } using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" INSERT INTO Crypto (Currency, Name, Address, Seed, Viewkey, Spendkey, Height, Password, Note) VALUES ($currency, $name, $address, $seed, $viewkey, $spendkey, $height, $password, $note)"; com.Parameters.AddWithValue("$currency", currency); com.Parameters.AddWithValue("$name", name); com.Parameters.AddWithValue("$address", address); com.Parameters.AddWithValue("$seed", string.IsNullOrEmpty(seed) ? DBNull.Value : encryptedSeed); com.Parameters.AddWithValue("$viewkey", string.IsNullOrEmpty(viewkey) ? DBNull.Value : encryptedViewkey); com.Parameters.AddWithValue("$spendkey", string.IsNullOrEmpty(spendkey) ? DBNull.Value : encryptedSpendkey); com.Parameters.AddWithValue("$height", string.IsNullOrEmpty(height) ? DBNull.Value : height); com.Parameters.AddWithValue("$password", string.IsNullOrEmpty(password) ? DBNull.Value : encryptedPassword); com.Parameters.AddWithValue("$note", string.IsNullOrEmpty(note) ? DBNull.Value : note); com.ExecuteNonQuery(); } } public bool EditCrypto(int id, string currency, string name, string address, string seed, string viewkey, string spendkey, string height, string password, string note) { if (string.IsNullOrWhiteSpace(currency) || string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(address)) { string err = mCulture == mJpLang ? "通貨、ウォレット名及び、住所を御入力下さい。" : "Please fill in the currency, wallet name, and address."; throw new ArgumentException(err); } string encryptedPassword = string.IsNullOrEmpty(password) ? string.Empty : EncryptInfo(password); string encryptedSeed = string.IsNullOrEmpty(seed) ? string.Empty : EncryptInfo(seed); string encryptedViewkey = string.IsNullOrEmpty(viewkey) ? string.Empty : EncryptInfo(viewkey); string encryptedSpendkey = string.IsNullOrEmpty(spendkey) ? string.Empty : EncryptInfo(spendkey); using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" UPDATE Crypto SET Currency = $currency, Name = $name, Address = $address, Seed = $seed, Viewkey = $viewkey, Spendkey = $spendkey, Height = $height, Password = $password, Note = $note WHERE Id = $id"; com.Parameters.AddWithValue("$id", id); com.Parameters.AddWithValue("$currency", currency); com.Parameters.AddWithValue("$name", name); com.Parameters.AddWithValue("$address", address); com.Parameters.AddWithValue("$seed", string.IsNullOrEmpty(seed) ? DBNull.Value : encryptedSeed); com.Parameters.AddWithValue("$viewkey", string.IsNullOrEmpty(viewkey) ? DBNull.Value : encryptedViewkey); com.Parameters.AddWithValue("$spendkey", string.IsNullOrEmpty(spendkey) ? DBNull.Value : encryptedSpendkey); com.Parameters.AddWithValue("$height", string.IsNullOrEmpty(height) ? DBNull.Value : height); com.Parameters.AddWithValue("$password", string.IsNullOrEmpty(password) ? DBNull.Value : encryptedPassword); com.Parameters.AddWithValue("$note", string.IsNullOrEmpty(note) ? DBNull.Value : note); return com.ExecuteNonQuery() > 0; } } public bool DeleteCrypto(int id) { using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = "DELETE FROM Crypto WHERE Id = $id"; com.Parameters.AddWithValue("$id", id); return com.ExecuteNonQuery() > 0; } } public List<(int Id, string Currency, string Name, string Address, string Seed, string Viewkey, string Spendkey, string Height, string Password, string Note)> GetAll(string keyword = "") { var cryptos = new List<(int, string, string, string, string, string, string, string, string, string)>(); using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); if (string.IsNullOrWhiteSpace(keyword)) { com.CommandText = @" SELECT Id, Currency, Name, Address, Seed, Viewkey, Spendkey, Height, Password, Note FROM Crypto ORDER BY Currency DESC"; } else { com.CommandText = @" SELECT Id, Currency, Name, Address, Seed, Viewkey, Spendkey, Height, Password, Note FROM Crypto WHERE Currency LIKE @keyword OR Name LIKE @keyword ORDER BY Currency DESC"; com.Parameters.AddWithValue("@keyword", $"%{keyword}%"); } using (SqliteDataReader reader = com.ExecuteReader()) { while (reader.Read()) { string decryptedSeed = DecryptInfo(reader.GetString(4)); string decryptedViewkey = reader.IsDBNull(5) ? string.Empty : DecryptInfo(reader.GetString(5)); string decryptedSpendkey = reader.IsDBNull(6) ? string.Empty : DecryptInfo(reader.GetString(6)); string decryptedPassword = reader.IsDBNull(8) ? string.Empty : DecryptInfo(reader.GetString(8)); cryptos.Add(( reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), decryptedSeed, reader.IsDBNull(5) ? string.Empty : decryptedViewkey, reader.IsDBNull(6) ? string.Empty : decryptedSpendkey, reader.IsDBNull(7) ? string.Empty : reader.GetString(7), reader.IsDBNull(8) ? string.Empty : decryptedPassword, reader.IsDBNull(9) ? string.Empty : reader.GetString(9) )); } } } return cryptos; } private string EncryptInfo(string answer) { using (Aes aes = Aes.Create()) { aes.Key = mEncryptionKey; aes.GenerateIV(); byte[] iv = aes.IV; using (ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, iv)) { byte[] plainBytes = Encoding.UTF8.GetBytes(answer); byte[] encryptedBytes = encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length); byte[] result = new byte[iv.Length + encryptedBytes.Length]; Buffer.BlockCopy(iv, 0, result, 0, iv.Length); Buffer.BlockCopy(encryptedBytes, 0, result, iv.Length, encryptedBytes.Length); return Convert.ToBase64String(result); } } } private string DecryptInfo(string encryptedAnswer) { byte[] combined = Convert.FromBase64String(encryptedAnswer); byte[] iv = new byte[16]; byte[] encryptedBytes = new byte[combined.Length - iv.Length]; Buffer.BlockCopy(combined, 0, iv, 0, iv.Length); Buffer.BlockCopy(combined, iv.Length, encryptedBytes, 0, encryptedBytes.Length); using (Aes aes = Aes.Create()) { aes.Key = mEncryptionKey; aes.IV = iv; using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV)) { byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedBytes, 0, encryptedBytes.Length); return Encoding.UTF8.GetString(decryptedBytes); } } } } }