为Flutter Windows应用实现许可证激活保护
为Flutter Windows应用实现许可证激活功能的方案
1. 设计安全的许可证密钥结构
优先采用**非对称加密(RSA)**生成密钥,规避对称加密的密钥泄露风险:
- 用私钥生成带签名的许可证密钥,密钥可包含用户ID、有效期、设备绑定标识等信息,格式推荐为
[原始数据].[签名](Base64编码后拼接)。 - 将公钥嵌入Flutter应用中,用于验证密钥的合法性。
2. 本地验证逻辑实现
借助pointycastle包处理RSA签名验证,核心代码示例:
import 'package:pointycastle/pointycastle.dart'; import 'dart:convert'; import 'dart:typed_data'; // 验证许可证密钥合法性 bool verifyLicense(String licenseKey, String publicKey) { final parts = licenseKey.split('.'); if (parts.length != 2) return false; final rawData = base64Decode(parts[0]); final signature = base64Decode(parts[1]); // 初始化RSA验证器 final signer = Signer('SHA-256/RSA') ..init(false, PublicKeyParameter<RsaPublicKey>(_parsePublicKey(publicKey))); return signer.verifySignature(rawData, signature); } // 解析PEM格式公钥 RsaPublicKey _parsePublicKey(String publicKey) { final pemContent = publicKey.replaceAll(RegExp(r'-----BEGIN PUBLIC KEY-----\n?'), '') .replaceAll(RegExp(r'\n?-----END PUBLIC KEY-----'), '') .replaceAll('\n', ''); final bytes = base64Decode(pemContent); final asn1Parser = ASN1Parser(bytes); final seq = asn1Parser.nextObject() as ASN1Sequence; final modulus = seq.elements[0] as ASN1Integer; final exponent = seq.elements[1] as ASN1Integer; return RsaPublicKey(modulus.valueAsBigInteger, exponent.valueAsBigInteger); }
3. 激活状态的安全存储
Windows平台用path_provider获取应用专属存储目录,保存激活状态时需加密,防止用户篡改本地文件,示例用encrypt包:
import 'package:path_provider/path_provider.dart'; import 'package:encrypt/encrypt.dart'; import 'dart:io'; import 'dart:convert'; // 保存激活状态到本地 Future<void> saveActivationStatus(bool activated, String licenseKey) async { final appDir = await getApplicationSupportDirectory(); final statusFile = File('${appDir.path}/license_status.dat'); final statusData = jsonEncode({ 'activated': activated, 'license': licenseKey, 'expireDate': '2025-12-31' // 示例有效期 }); // 建议结合设备唯一标识生成加密密钥,提升安全性 final encryptionKey = Key.fromUtf8('your_secure_encryption_key'); final iv = IV.fromLength(16); final encrypter = Encrypter(AES(encryptionKey)); final encryptedData = encrypter.encrypt(statusData, iv: iv); await statusFile.writeAsString(encryptedData.base64); } // 读取本地激活状态 Future<Map<String, dynamic>> getActivationStatus() async { final appDir = await getApplicationSupportDirectory(); final statusFile = File('${appDir.path}/license_status.dat'); if (!await statusFile.exists()) return {'activated': false}; final encryptedContent = await statusFile.readAsString(); final encryptionKey = Key.fromUtf8('your_secure_encryption_key'); final iv = IV.fromLength(16); final encrypter = Encrypter(AES(encryptionKey)); final decryptedData = encrypter.decrypt(Encrypted.fromBase64(encryptedContent), iv: iv); return jsonDecode(decryptedData); }
4. 可选:服务器端增强验证
本地验证易被逆向破解,建议补充服务器验证环节:
- 用
device_info_plus获取Windows设备唯一标识(如设备ID),用户输入密钥后,将密钥+设备ID发送至后端服务器。 - 服务器验证密钥合法性、是否已绑定其他设备、是否过期,返回激活结果。
- 后端可选用任意语言实现,核心逻辑是用私钥重验签名,同时维护密钥使用记录。
5. 反篡改与防护措施
- 编译Flutter应用时开启代码混淆:
flutter build windows --obfuscate --split-debug-info=./debug_info,提升逆向难度。 - 核心验证逻辑尽量放在服务器端,避免前端暴露敏感判断。
- 可选:应用启动时重新验证密钥签名,定期校验本地激活状态合法性。
6. 完整激活流程
- 应用启动时读取本地激活状态。
- 若已激活且未过期,直接进入主界面;否则显示激活窗口。
- 用户输入密钥后,先执行本地签名验证。
- 本地验证通过后,可选发送请求到服务器做二次验证。
- 验证通过后,保存加密的激活状态,进入主界面。
内容的提问来源于stack exchange,提问作者Poula Adel
相关产品推荐
相关产品推荐

