You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为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. 完整激活流程

  1. 应用启动时读取本地激活状态。
  2. 若已激活且未过期,直接进入主界面;否则显示激活窗口。
  3. 用户输入密钥后,先执行本地签名验证。
  4. 本地验证通过后,可选发送请求到服务器做二次验证。
  5. 验证通过后,保存加密的激活状态,进入主界面。

内容的提问来源于stack exchange,提问作者Poula Adel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 05:27:47