用C#和BSV构建加密日记本:从密钥管理到链上存储的全栈实现
在数字时代,隐私保护成为越来越多人关注的核心问题。传统日记应用虽然方便,但存在云端数据泄露、服务商监控等风险。本文将带你用C#和BSV区块链技术构建一个真正私密的加密日记本,实现本地加密、链上存储的完整解决方案。不同于普通数据库存储,区块链的不可篡改特性确保了日记的永久保存,而加密技术则保障了只有你自己能查看内容。
1. 环境准备与项目架构
1.1 开发环境配置
首先确保已安装Visual Studio 2022(社区版即可)和.NET 6+环境。新建一个Windows窗体应用项目,命名为EncryptedDiary。通过NuGet添加以下关键依赖库:
Install-Package NBitcoin -Version 6.0.10 Install-Package BsvSimpleLibrary -Version 1.2.3 Install-Package BitcoinSVCryptor -Version 2.1.0这些库将分别提供:
- NBitcoin:比特币密钥管理和交易构建
- BsvSimpleLibrary:BSV链交互的简化API
- BitcoinSVCryptor:AES加密功能
1.2 项目结构设计
建议采用分层架构组织代码:
EncryptedDiary/ ├── Models/ │ ├── DiaryEntry.cs # 日记数据模型 │ └── BlockchainTx.cs # 交易元数据 ├── Services/ │ ├── CryptoService.cs # 加密服务 │ └── ChainService.cs # 区块链交互 ├── Views/ │ └── MainForm.cs # 主界面 └── Utils/ └── Extensions.cs # 扩展方法提示:实际开发中可先实现核心功能,再逐步完善架构。初期可先将所有代码放在主窗体,后期再重构分离。
2. 密钥管理与安全存储
2.1 私钥的生成与导入
BSV钱包的核心是私钥管理。我们支持两种方式获取私钥:
- 新建密钥对:使用NBitcoin生成新私钥
- 导入现有密钥:支持WIF格式的私钥导入
关键代码示例:
// 生成新密钥对 private BitcoinSecret GenerateNewKey(Network network) { var key = new Key(); return key.GetBitcoinSecret(network); } // 验证私钥有效性 private bool ValidatePrivateKey(string wifKey) { try { var secret = new BitcoinSecret(wifKey); return secret != null; } catch { return false; } }2.2 安全存储方案
私钥的安全存储至关重要,推荐以下几种方案:
| 存储方式 | 安全性 | 便利性 | 适用场景 |
|---|---|---|---|
| 内存加密 | ★★★★☆ | ★★★☆☆ | 临时使用 |
| Windows DPAPI | ★★★★☆ | ★★★★☆ | 本地持久化 |
| 硬件钱包 | ★★★★★ | ★★☆☆☆ | 高安全需求 |
实现DPAPI加密存储的示例:
public static class SecureStorage { public static void SaveKey(string key, string password) { byte[] entropy = Encoding.UTF8.GetBytes(password); byte[] encrypted = ProtectedData.Protect( Encoding.UTF8.GetBytes(key), entropy, DataProtectionScope.CurrentUser); File.WriteAllBytes("key.dat", encrypted); } public static string LoadKey(string password) { byte[] encrypted = File.ReadAllBytes("key.dat"); byte[] entropy = Encoding.UTF8.GetBytes(password); byte[] decrypted = ProtectedData.Unprotect( encrypted, entropy, DataProtectionScope.CurrentUser); return Encoding.UTF8.GetString(decrypted); } }3. 日记加密与链上存储
3.1 内容加密实现
采用AES-256-CBC模式加密日记内容,密钥派生自用户私钥:
public static class AesHelper { public static byte[] Encrypt(string plainText, string privateKey) { using var aes = Aes.Create(); aes.Key = DeriveKey(privateKey); aes.IV = new byte[16]; // 固定IV简化示例 using var ms = new MemoryStream(); using var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write); using var sw = new StreamWriter(cs); sw.Write(plainText); sw.Close(); return ms.ToArray(); } private static byte[] DeriveKey(string privateKey) { using var sha256 = SHA256.Create(); return sha256.ComputeHash(Encoding.UTF8.GetBytes(privateKey)); } }3.2 OP_RETURN交易构建
BSV支持通过OP_RETURN脚本存储任意数据,单笔交易可承载约100KB内容。关键实现步骤:
- 计算交易费用(建议0.5 BSV/kB)
- 构建未签名交易
- 添加OP_RETURN输出
- 签名并广播
public async Task<string> PublishToChain(string encryptedData, BitcoinSecret privateKey) { var network = privateKey.Network; var feeRate = new FeeRate(Money.Satoshis(500), 1000); // 0.5 BSV/kB var builder = network.CreateTransactionBuilder(); var tx = builder .AddCoins(GetUnspentCoins(privateKey)) .AddKeys(privateKey) .SendFees(feeRate.GetFee(200)) // 预估200字节 .Send(privateKey.GetAddress(), Money.Zero) // 找零地址 .SetOpReturn(Encoding.UTF8.GetBytes(encryptedData)) .BuildTransaction(true); return await BroadcastTransaction(tx); }注意:实际项目中应添加UTXO管理和找零计算逻辑,此处为简化示例。
4. 数据检索与解密
4.1 链上数据查询
通过BSV节点的API查询指定地址的所有OP_RETURN交易:
public async Task<List<DiaryEntry>> FetchDiaryEntries(string address) { var apiUrl = "https://api.whatsonchain.com/v1/bsv/main/address/{address}/history"; var client = new HttpClient(); var response = await client.GetAsync(apiUrl.Replace("{address}", address)); var history = JsonConvert.DeserializeObject<List<TxHistory>>( await response.Content.ReadAsStringAsync()); var entries = new List<DiaryEntry>(); foreach (var tx in history.Where(t => t.HasOpReturn)) { var txData = await GetTransactionData(tx.TxHash); entries.Add(new DiaryEntry { TxId = tx.TxHash, Timestamp = tx.Time, EncryptedData = txData.OpReturnHex }); } return entries.OrderByDescending(e => e.Timestamp).ToList(); }4.2 内容解密流程
解密过程是加密的逆操作,需要特别注意错误处理:
- Base58解码加密数据
- 使用私钥派生AES密钥
- 执行解密操作
- 验证结果完整性
public string DecryptEntry(string encryptedBase58, string privateKey) { try { var decoder = new Base58Encoder(); byte[] cipherBytes = decoder.DecodeData(encryptedBase58); using var aes = Aes.Create(); aes.Key = DeriveKey(privateKey); aes.IV = new byte[16]; using var ms = new MemoryStream(cipherBytes); using var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Read); using var sr = new StreamReader(cs); return sr.ReadToEnd(); } catch (CryptographicException ex) { throw new Exception("解密失败,请检查私钥是否正确", ex); } }5. 用户体验优化与实践建议
5.1 界面设计要点
一个友好的加密日记本应包含以下核心界面元素:
- 私钥管理区:输入/生成私钥的控件
- 日记编辑区:支持Markdown的富文本编辑器
- 历史记录面板:按时间排序的日记列表
- 状态显示栏:网络状态、余额等信息
建议使用DevExpress或MaterialSkin等UI库提升视觉效果。
5.2 性能优化技巧
处理区块链数据时需注意:
- 缓存策略:本地缓存已获取的交易数据
- 分批加载:当历史记录过多时实现分页
- 后台线程:将链上操作放在后台线程避免界面卡顿
示例异步加载实现:
private async void LoadDiaryEntriesAsync() { loadingIndicator.Visible = true; try { var entries = await _diaryService.GetEntriesAsync(_currentAddress); diaryList.BeginUpdate(); diaryList.Items.Clear(); foreach (var entry in entries) { var item = new ListViewItem(entry.Date.ToShortDateString()); item.SubItems.Add(entry.Title); item.Tag = entry; diaryList.Items.Add(item); } } catch (Exception ex) { ShowError("加载失败: " + ex.Message); } finally { diaryList.EndUpdate(); loadingIndicator.Visible = false; } }5.3 错误处理与日志
完善的错误处理应包括:
- 网络连接失败时的重试机制
- 交易广播超时处理
- 解密失败的用户引导
- 详细的本地日志记录
推荐使用Serilog等日志库实现结构化日志:
public class DiaryLogger { private readonly ILogger _logger; public DiaryLogger() { _logger = new LoggerConfiguration() .WriteTo.File("logs/diary-.log", rollingInterval: RollingInterval.Day) .CreateLogger(); } public void LogTransaction(string txId, decimal fee) { _logger.Information("Transaction {TxId} broadcast with fee {Fee} BSV", txId, fee); } }在实际项目中,我发现最常遇到的挑战是交易费用的合理估算。BSV网络虽然费用低廉,但在高峰期仍可能出现拥堵。经过多次测试,建议采用动态费率计算:
public async Task<decimal> GetRecommendedFeeRate() { var client = new HttpClient(); var response = await client.GetAsync("https://api.whatsonchain.com/v1/bsv/main/fee/recommended"); var data = JsonConvert.DeserializeObject<dynamic>(await response.Content.ReadAsStringAsync()); return data.fastestFee / 100000; // 转换为BSV单位 }