using Microsoft.Data.Sqlite; using System.Security.Cryptography; using System.Text; namespace SimPas2_Windows.Managers { internal class CreditcardManager { private readonly string mConnectionString; private readonly byte[] mEncryptionKey; private readonly string mCulture; private string mJpLang; public CreditcardManager(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 cardnumber, int? excludeId = null) { using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" SELECT COUNT(*) FROM Cc WHERE UPPER(Cardnumber) = UPPER(@cardnumber)"; com.Parameters.AddWithValue("@cardnumber", cardnumber); if (excludeId.HasValue) { com.CommandText += " AND Id != @excludeId"; com.Parameters.AddWithValue("@excludeId", excludeId.Value); } return Convert.ToInt32(com.ExecuteScalar()) > 0; } } public void AddCc(string brand, string cardnumber, string expiration, string cvc, string holdername, string note) { if (string.IsNullOrWhiteSpace(brand) || string.IsNullOrWhiteSpace(cardnumber) || string.IsNullOrWhiteSpace(expiration) || string.IsNullOrWhiteSpace(cvc) || string.IsNullOrWhiteSpace(holdername)) { string err = mCulture == mJpLang ? "ブランド、カード番号、氏名、有効期限及び、CVCを御入力下さい。" : "Please fill in the brand, card number, full name, expiration, and CVC."; throw new ArgumentException(err); } string encryptedCvc = EncryptCvc(cvc); if (AlreadyExists(cardnumber)) { string err = mCulture == mJpLang ? $"カード「{brand}/{holdername} ({expiration})」は既に存在します。" : $"The card '{brand}/{holdername} ({expiration})' already exists."; throw new ArgumentException(err); } using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" INSERT INTO Cc (Brand, Cardnumber, Expiration, Cvc, Holdername, Note) VALUES ($brand, $cardnumber, $expiration, $cvc, $holdername, $note)"; com.Parameters.AddWithValue("$brand", brand); com.Parameters.AddWithValue("$cardnumber", cardnumber); com.Parameters.AddWithValue("$expiration", expiration); com.Parameters.AddWithValue("$cvc", encryptedCvc); com.Parameters.AddWithValue("$holdername", holdername); com.Parameters.AddWithValue("$note", string.IsNullOrEmpty(note) ? DBNull.Value : note); com.ExecuteNonQuery(); } } public bool EditCc(int id, string brand, string cardnumber, string expiration, string cvc, string holdername, string note) { if (string.IsNullOrWhiteSpace(brand) || string.IsNullOrWhiteSpace(cardnumber) || string.IsNullOrWhiteSpace(expiration) || string.IsNullOrWhiteSpace(cvc) || string.IsNullOrWhiteSpace(holdername)) { string err = mCulture == mJpLang ? "ブランド、カード番号、氏名、有効期限及び、CVCを御入力下さい。" : "Please fill in the brand, card number, full name, expiration, and CVC."; throw new ArgumentException(err); } string encryptedCvc = EncryptCvc(cvc); using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = @" UPDATE Cc SET Brand = $brand, Cardnumber = $cardnumber, Expiration = $expiration, Cvc = $cvc, Holdername = $holdername, Note = $note WHERE Id = $id"; com.Parameters.AddWithValue("$id", id); com.Parameters.AddWithValue("$brand", brand); com.Parameters.AddWithValue("$cardnumber", cardnumber); com.Parameters.AddWithValue("$expiration", expiration); com.Parameters.AddWithValue("$cvc", encryptedCvc); com.Parameters.AddWithValue("$holdername", holdername); com.Parameters.AddWithValue("$note", string.IsNullOrEmpty(note) ? DBNull.Value : note); return com.ExecuteNonQuery() > 0; } } public bool DeleteCc(int id) { using (SqliteConnection conn = new SqliteConnection(mConnectionString)) { conn.Open(); SqliteCommand com = conn.CreateCommand(); com.CommandText = "DELETE FROM Cc WHERE Id = $id"; com.Parameters.AddWithValue("$id", id); return com.ExecuteNonQuery() > 0; } } public List<(int Id, string Brand, string Cardnumber, string Expiration, string Cvc, string Holdername, string Note)> GetAll(string keyword = "") { var ccs = new List<(int, 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, Brand, Cardnumber, Expiration, Cvc, Holdername, Note FROM Cc ORDER BY Brand ASC"; } else { com.CommandText = @" SELECT Id, Brand, Cardnumber, Expiration, Cvc, Holdername, Note FROM Cc WHERE Brand LIKE @keyword OR Holdername LIKE @keyword OR Cardnumber LIKE @keyword ORDER BY Brand ASC"; com.Parameters.AddWithValue("@keyword", $"%{keyword}%"); } using (SqliteDataReader reader = com.ExecuteReader()) { while (reader.Read()) { string encryptedCvc = reader.GetString(4); string decryptedCvc = DecryptCvc(encryptedCvc); ccs.Add(( reader.GetInt32(0), reader.GetString(1), reader.GetString(2), reader.GetString(3), decryptedCvc, reader.GetString(5), reader.IsDBNull(6) ? string.Empty : reader.GetString(6) )); } } } return ccs; } private string EncryptCvc(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 DecryptCvc(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); } } } } }