Files
Shrlalgo.RvKits/ShrlAlgoStudio/LicenseManager.cs
2026-02-17 22:17:23 +08:00

123 lines
5.2 KiB
C#

using System;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
namespace ShrlAlgoStudio
{
internal class LicenseManager
{
// 【重要】替换为你生成的公钥
public static string PublicKey = @"<RSAKeyValue><Modulus>rd5+EjU6QxaTY/AalU9g6ugAquN0ahJSgeahnf2CrfvUAWFNJ+SH7Qr0RcvOTyAbWfvLWoBDACKaPsg+8nRQcO3EZdFyjJ9oycLNrw38+gSB2+/79Axys/8MHtSXVUw9WN2e9LxeHOQGtcaoSsp+bPGaXswthovQ2CkvBTmCokcAOX6UaR7Av4npaXyEoGCUsZLEiSydMsNbQ+wLsumVg1H2o/cpkO0s4DiJHoF66zUuxA+pYSjSCn5KTUsOemf+gBsob6Sw+7WOToyiOkMdO9Op6esf8yL7DJ1Yd40XaKd5IeLQmEX/b+n1EV2JEkGO0p9q9MQRp6NrEc9LvfMJWQ==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
private static string RegPath = @$"Software\{Assembly.GetExecutingAssembly().GetName()}\License";
/// <summary>
/// 校验结果枚举
/// </summary>
public enum AuthStatus { Valid, Expired, HardwareMismatch, TimeTampered, InvalidSignature, NoLicense }
/// <summary>
/// 核心验证方法
/// </summary>
public static (AuthStatus status, string msg) Validate()
{
string licenseKey = LoadReg("Key");
if (string.IsNullOrEmpty(licenseKey)) return (AuthStatus.NoLicense, "未激活");
try
{
// 格式:签名|机器码|发码日期|过期日期
var parts = licenseKey.Split('|');
if (parts.Length != 4) return (AuthStatus.InvalidSignature, "授权格式错误");
string signature = parts[0];
string mCode = parts[1];
string issueDateStr = parts[2];
string expiryDateStr = parts[3];
// 1. 硬件校验
if (mCode != HardwareInfo.GetMachineCode())
return (AuthStatus.HardwareMismatch, "机器码不匹配,请联系管理员");
// 2. RSA 签名校验 (防止篡改日期)
string dataVerify = $"{mCode}|{issueDateStr}|{expiryDateStr}";
if (!VerifyRSA(dataVerify, signature))
return (AuthStatus.InvalidSignature, "非法授权 (签名无效)");
// 3. 时间逻辑校验
DateTime issueDate = DateTime.ParseExact(issueDateStr, "yyyyMMdd", null);
DateTime expiryDate = DateTime.ParseExact(expiryDateStr, "yyyyMMdd", null);
DateTime now = TimeHelper.GetTrustedTime(); // 获取可信时间
// 3.1 检查是否早于发码日期 (防极度回滚)
if (now < issueDate)
return (AuthStatus.TimeTampered, "系统时间异常:早于授权发码时间");
// 3.2 检查上次运行记录 T (防短期回滚)
long lastRunTicks = 0;
long.TryParse(LoadReg("T"), out lastRunTicks);
if (lastRunTicks > 0)
{
DateTime lastRun = new DateTime(lastRunTicks);
// 允许1小时的误差
if (now < lastRun.AddHours(-1))
return (AuthStatus.TimeTampered, "系统时间异常:检测到时间倒流");
}
// 3.3 检查过期
if (now.Date > expiryDate.Date)
return (AuthStatus.Expired, $"授权已于 {expiryDate:yyyy-MM-dd} 到期");
// --- 验证通过 ---
SaveReg("T", now.Ticks.ToString()); // 更新痕迹
return (AuthStatus.Valid, $"授权有效,到期时间为{expiryDate:yyyy-MM-dd}");
}
catch
{
return (AuthStatus.InvalidSignature, "授权解析异常");
}
}
// 保存激活码
public static void Activate(string keyInput)
{
SaveReg("Key", keyInput);
}
// RSA 验签辅助
private static bool VerifyRSA(string data, string signature)
{
try
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(PublicKey);
var formatter = new RSAPKCS1SignatureDeformatter(rsa);
formatter.SetHashAlgorithm("SHA256");
byte[] dataBytes = Encoding.UTF8.GetBytes(data);
byte[] signBytes = Convert.FromBase64String(signature);
using (var sha = SHA256.Create())
{
return formatter.VerifySignature(sha.ComputeHash(dataBytes), signBytes);
}
}
}
catch { return false; }
}
#region
private static void SaveReg(string key, string val)
{
using (var r = Registry.CurrentUser.CreateSubKey(RegPath)) r.SetValue(key, val);
}
private static string LoadReg(string key)
{
using (var r = Registry.CurrentUser.OpenSubKey(RegPath)) return r?.GetValue(key)?.ToString();
}
public static void RemoveLicense()
{
using (var r = Registry.CurrentUser.OpenSubKey(RegPath, true)) r?.DeleteValue("Key", false);
}
#endregion
}
}