Files
ShrlAlgoToolkit/NeoUI/MelskinTest/ControlTestWindow.xaml.cs
2026-01-02 17:30:41 +08:00

330 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Management;
using System.Net;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Windows;
using Microsoft.Win32;
using Melskin.Controls;
namespace MelskinTest;
/// <summary>
/// ControlTestWindow.xaml 的交互逻辑
/// </summary>
public partial class ControlTestWindow
{
private static string PrivateKey =
@"<RSAKeyValue><Modulus>rd5+EjU6QxaTY/AalU9g6ugAquN0ahJSgeahnf2CrfvUAWFNJ+SH7Qr0RcvOTyAbWfvLWoBDACKaPsg+8nRQcO3EZdFyjJ9oycLNrw38+gSB2+/79Axys/8MHtSXVUw9WN2e9LxeHOQGtcaoSsp+bPGaXswthovQ2CkvBTmCokcAOX6UaR7Av4npaXyEoGCUsZLEiSydMsNbQ+wLsumVg1H2o/cpkO0s4DiJHoF66zUuxA+pYSjSCn5KTUsOemf+gBsob6Sw+7WOToyiOkMdO9Op6esf8yL7DJ1Yd40XaKd5IeLQmEX/b+n1EV2JEkGO0p9q9MQRp6NrEc9LvfMJWQ==</Modulus><Exponent>AQAB</Exponent><P>yR+R5ofKJenqgMvXxT/Tqxka4jeuPz+Uta9WXYVyzIeyjESOEe1B9uM+DkhM27zw8XsG143KpzUp8jy5Gh6z14ivfUbv09GN5ICFWeqEOQJ8JSPfqcq5YKpFNY+zJGNgUfKdLwMUvu55O6Y2BdB2yLFwAUztUAW0qT+ZmINI3xM=</P><Q>3U84e83rhoi5o4kR9I4JllM1ys/43uvJSVDwkqAo+p7R1uFUsl1STNHVo/d0mJKXLOf8J4fI0yVz1H73XBHjEzJqJPWlycOArA+c8eKtLvvIlauSh1ulAJbSKlINxjr6aVjXH9ztGKRVIRU2EuUPKQKwnxScKrqNbeGqRKHPx2M=</Q><DP>ktj9I3AkFfisIywyuC+5MeUbru5uyHl48As8qP4l6tZtdGMdxFMSZdxX0//QNmTHG9nzDfHWdK2pmdfiYwLl1spTL8pak7Mewidgtl03+5Qn5spBpWyCW+UWsVvhtgDlNBBL2iaKwDnIxNe//BDQmgqMODbd0x7HYQgx8pxw2Yc=</DP><DQ>dblkhIcfmKs2hQWvJXZBJ1QQM1i6PGsU4glKT9Uet2KwoSuwUElQNEkS6mwB+/9Op7an7adPbvJnUUxQ7QsezeFtkUeS72cuFVkg1ZMNKQcKxoNEKPjZJx0ToyuO5DoPZua5WNu+y/LuzfBomVh73gbuSVE/WYNvJFp8ppGk450=</DQ><InverseQ>gSHzvMS0Fu4j/yuzjOjLXYxR8qP3AweEbLlM8/SBjpT5qq+R+j/m3q7vTvYZwwMqAMamGWmTHAlQvOjnSeV2Qnb2C/bqs5jTTiDGyY/xqGV8pFlkegieBl+zJYrOyisUP/xNk4ckKhGfGUIan91N8R5M9Dk2x0hOTCY7m4Tv2SI=</InverseQ><D>polHLj+Hh7z2/jF79OnsRfRkt0pYNkVIfB4hTYgGBIoCfjPpyD0wKM9mO/hDqyxwplb0Z8IU6us53MrG6EqaxiAoDvJ4CtOhdie+BV+boQpyK+0I4rCNqXSw8lLkhRIabKUSXQ6UAo4zEyeuCL0+LTGZkBV3wbjoGDQSUqnMQ4uzvuGsWUU76Y/nnpYf6VVajCsLAseQbdAbeLuL0WPmNqO+E3SLsL72z5lgqkk5yyEQYDTWZMEuFUsyGmP+XC4EBp44IW3eQmPPBfUqxRvCAq2Gvk95Q+9H9FZlTW4ctMSezOZaH9pAaJvG5QL2ggw+KXh+pljnM8bYtL2I+KWtNQ==</D></RSAKeyValue>";
public ControlTestWindow()
{
InitializeComponent();
}
private void LicenseClick(object sender, RoutedEventArgs e)
{
PrivateKey = PrivateKeyTextBox.Text;
LicenseManager.PublicKey = PublicKeyTextBox.Text;
DateTime issueDate = DateTime.Now;
//DateTime expiryDate = issueDate.AddYears(1);
DateTime expiryDate = ExpiryDatePicker.DisplayDate;
if (expiryDate < issueDate)
{
MessageBox.Show("授权到期时间有误,应比发码日期晚");
return;
}
string mCode = MachineCodeTextBox.Text.Trim().ToUpper();
if (string.IsNullOrEmpty(mCode))
{
MessageBox.Show("请先获取机器码");
return;
}
string key = CreateLicense(mCode, issueDate, expiryDate);
LicenseTextBox.Text = key;
Clipboard.SetText(key);
MessageBox.Show("激活码已复制!");
}
private string CreateLicense(string machineCode, DateTime issue, DateTime expiry)
{
string issueStr = issue.ToString("yyyyMMdd");
string expiryStr = expiry.ToString("yyyyMMdd");
// 签名原始数据:机器码 + 发码日期 + 过期日期
string dataToSign = $"{machineCode}|{issueStr}|{expiryStr}";
using (var rsa = new RSACryptoServiceProvider())
{
rsa.FromXmlString(PrivateKey);
var formatter = new RSAPKCS1SignatureFormatter(rsa);
formatter.SetHashAlgorithm("SHA256");
byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSign);
using (var sha = SHA256.Create())
{
byte[] hash = sha.ComputeHash(dataBytes);
string signature = Convert.ToBase64String(formatter.CreateSignature(hash));
// 最终格式:签名|机器码|发码日期|过期日期
return $"{signature}|{machineCode}|{issueStr}|{expiryStr}";
}
}
}
private void GenerateKeyClick(object sender, RoutedEventArgs e)
{
using var rsa = new RSACryptoServiceProvider(2048);
PublicKeyTextBox.Text = rsa.ToXmlString(false); // 放在插件里
PrivateKeyTextBox.Text = rsa.ToXmlString(true); // 自己妥善保存
}
private void ValidateClick(object sender, RoutedEventArgs e)
{
var result = LicenseManager.Validate();
MessageBox.Show(result.msg);
}
private void RemoveLicenseClick(object sender, RoutedEventArgs e)
{
LicenseManager.RemoveLicense();
MessageBox.Show("授权已注销");
}
private void ActivateClick(object sender, RoutedEventArgs e)
{
LicenseManager.Activate(LicenseTextBox.Text);
}
private void MachineCodeClick(object sender, RoutedEventArgs e)
{
MachineCodeTextBox.Text = HardwareInfo.GetMachineCode();
}
private void SaveKeysClick(object sender, RoutedEventArgs e)
{
VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog();
if (dialog.ShowDialog())
{
string publicKeyPath = System.IO.Path.Combine(dialog.SelectedPath, "PublicKey.txt");
File.WriteAllText(publicKeyPath, PublicKeyTextBox.Text);
string privateKeyPath = System.IO.Path.Combine(dialog.SelectedPath, "PrivateKey.txt");
File.WriteAllText(privateKeyPath, PrivateKeyTextBox.Text);
MessageBox.Show($"公私密钥已保存到:{dialog.SelectedPath}");
}
}
private void SaveLicenseClick(object sender, RoutedEventArgs e)
{
VistaFolderBrowserDialog dialog = new VistaFolderBrowserDialog();
if (dialog.ShowDialog())
{
string path = System.IO.Path.Combine(dialog.SelectedPath, "license.txt");
File.WriteAllText(path, LicenseTextBox.Text);
MessageBox.Show($"激活码已保存到:{path}");
}
}
}
public static class HardwareInfo
{
public static string GetMachineCode()
{
try
{
string cpu = GetWmiInfo("Win32_Processor", "ProcessorId");
string board = GetWmiInfo("Win32_BaseBoard", "SerialNumber");
string disk = GetWmiInfo("Win32_PhysicalMedia", "SerialNumber"); // 物理硬盘序列号
// 拼接并哈希只取前16位方便复制
string raw = $"{cpu}@{board}@{disk}";
using (var sha = SHA256.Create())
{
byte[] bytes = sha.ComputeHash(Encoding.UTF8.GetBytes(raw));
return BitConverter.ToString(bytes).Replace("-", "").Substring(0, 16).ToUpper();
}
}
catch
{
return "ERROR-HARDWARE-ID";
}
}
private static string GetWmiInfo(string table, string prop)
{
try
{
using (var mc = new ManagementClass(table))
foreach (var mo in mc.GetInstances())
{
string val = mo[prop]?.ToString();
if (!string.IsNullOrWhiteSpace(val)) return val.Trim();
}
}
catch { }
return "UNKNOWN";
}
}
public 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
}
public static class TimeHelper
{
public static DateTime GetTrustedTime()
{
// 1. 尝试网络时间 (3秒超时)
try
{
var request = WebRequest.Create("http://www.baidu.com"); // 或 microsoft.com
request.Method = "HEAD";
request.Timeout = 3000;
using (var response = request.GetResponse())
{
string dateStr = response.Headers["Date"];
if (DateTime.TryParseExact(dateStr, "ddd, dd MMM yyyy HH:mm:ss 'GMT'",
CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime netTime))
return netTime.ToLocalTime();
}
}
catch { }
// 2. 尝试文件系统痕迹校验 (防断网改时间)
DateTime localNow = DateTime.Now;
try
{
// 获取系统目录和临时目录的最后修改时间
DateTime t1 = new DirectoryInfo(Environment.SystemDirectory).LastWriteTime;
DateTime t2 = new DirectoryInfo(Path.GetTempPath()).LastWriteTime;
DateTime latestFileTime = t1 > t2 ? t1 : t2;
// 如果本地时间比系统文件时间还要早超过24小时判定为作弊
if (localNow < latestFileTime.AddHours(-24))
{
return latestFileTime; // 强制使用文件时间
}
}
catch { }
// 3. 兜底
return localNow;
}
}