using System; using System.IO; using System.Security.Cryptography; using System.Text; using System.Windows; using System.Windows.Controls; using iNKORE.UI.WPF.Modern; using Microsoft.Win32; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.Security; namespace KeyGen { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow { public MainWindow() { InitializeComponent(); } private void LicenseClick(object sender, RoutedEventArgs e) { DateTime issueDate = DateTime.Now; //DateTime expiryDate = issueDate.AddYears(1); var expiryDate = ExpiryDatePicker.SelectedDate; if (expiryDate == null) { Message.Text = ("请选择授权到期时间"); return; } if (expiryDate < issueDate) { Message.Text = ("授权到期时间有误,应比发码日期晚"); return; } string mCode = MachineCodeTextBox.Text.Trim().ToUpper(); if (string.IsNullOrEmpty(mCode)) { Message.Text = ("请先获取机器码"); return; } string key = SignData(mCode, issueDate, expiryDate, PrivateKeyTextBox.Text); LicenseTextBox.Text = key; Clipboard.SetText(key); Message.Text = "激活码已复制!"; } private void GenerateKeyClick(object sender, RoutedEventArgs e) { //using (var rsa = new RSACryptoServiceProvider(2048)) //{ // PublicKeyTextBox.Text = rsa.ToXmlString(false); // 放在插件里 // PrivateKeyTextBox.Text = rsa.ToXmlString(true); // 自己妥善保存 //} // 1. 生成 RSA 密钥对 RsaKeyPairGenerator generator = new RsaKeyPairGenerator(); generator.Init(new KeyGenerationParameters(new SecureRandom(), 2048)); AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair(); // 2. 导出公钥为 PEM 格式 (SubjectPublicKeyInfo - OpenSSL 兼容) using (StringWriter sw = new StringWriter()) { PemWriter pemWriter = new PemWriter(sw); pemWriter.WriteObject(keyPair.Public); PublicKeyTextBox.Text = sw.ToString(); } // 3. 导出私钥为 PEM 格式 (PKCS#1 格式) using (StringWriter sw = new StringWriter()) { PemWriter pemWriter = new PemWriter(sw); pemWriter.WriteObject(keyPair.Private); PrivateKeyTextBox.Text = sw.ToString(); } } public string SignData(string machineCode, DateTime issue, DateTime? expiry, string privateKeyPem) { if (expiry == null) { throw new ArgumentNullException(nameof(expiry), "Expiry date cannot be null."); } // 1. 严格格式化日期 string issueStr = issue.ToString("yyyy-MM-dd"); string expiryStr = expiry?.ToString("yyyy-MM-dd"); // 2. 拼接原始数据 (必须带上 |) string dataToSign = $"{machineCode}{issueStr}{expiryStr}"; // 3. 使用 BouncyCastle 签名 ISigner signer = SignerUtilities.GetSigner("SHA-256withRSA"); using (StringReader sr = new StringReader(privateKeyPem)) { PemReader pr = new PemReader(sr); AsymmetricCipherKeyPair keyPair = (AsymmetricCipherKeyPair)pr.ReadObject(); signer.Init(true, keyPair.Private); } byte[] dataBytes = Encoding.UTF8.GetBytes(dataToSign); signer.BlockUpdate(dataBytes, 0, dataBytes.Length); byte[] sigBytes = signer.GenerateSignature(); string signature = Convert.ToBase64String(sigBytes); // 4. 返回最终激活码 return $"{signature}|{machineCode}|{issueStr}|{expiryStr}"; } private string CreateLicense(string machineCode, DateTime issue, DateTime expiry) { string issueStr = issue.ToString("yyyy-MM-dd"); string expiryStr = expiry.ToString("yyyy-MM-dd"); // 签名原始数据:机器码 + 发码日期 + 过期日期 string dataToSign = $"{machineCode}|{issueStr}|{expiryStr}"; using (var rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(PrivateKeyTextBox.Text); 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 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); Message.Text = ($"公私密钥已保存到:{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); Message.Text = ($"激活码已保存到:{path}"); } } private void ImportPrivateKeyClick(object sender, RoutedEventArgs e) { OpenFileDialog dialog = new OpenFileDialog() { Filter = "文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*" }; if (dialog.ShowDialog() == true) { string privateKey = File.ReadAllText(dialog.FileName); PrivateKeyTextBox.Text = privateKey; Message.Text = ($"导入文件私钥完成"); } } private void ToggleTheme_Click(object sender, RoutedEventArgs e) { if (ThemeManager.Current.ApplicationTheme == ApplicationTheme.Dark) { ThemeManager.Current.ApplicationTheme = ApplicationTheme.Light; } else { ThemeManager.Current.ApplicationTheme = ApplicationTheme.Dark; } } /// /// 生成通用的 6 位应急码 /// /// "1D", "7D", 或 "30D" public static string GenerateGlobalOTP(string durationTag, string salt) { string secret = salt + durationTag; // 计算天数步长 long unixTime = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; long step = unixTime / 86400; byte[] secretBytes = Encoding.UTF8.GetBytes(secret); // 字符表必须严格一致 string charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 步长转 8 字节大端 byte[] stepBytes = BitConverter.GetBytes(step); if (BitConverter.IsLittleEndian) Array.Reverse(stepBytes); byte[] msg = new byte[8]; Array.Copy(stepBytes, 0, msg, 8 - stepBytes.Length, stepBytes.Length); using (var hmac = new HMACSHA1(Encoding.UTF8.GetBytes(secret))) { byte[] hash = hmac.ComputeHash(msg); int offset = hash[hash.Length - 1] & 0xf; uint binary = (uint)(((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16) | ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff)); string code = ""; for (int i = 0; i < 6; i++) { code += charset[(int)(binary % (uint)charset.Length)]; binary /= (uint)charset.Length; } return code; } } private void DynamicLicenseClick(object sender, RoutedEventArgs e) { if (DynamicDurationComboBox.SelectedItem is not ComboBoxItem selectedItem) { Message.Text = ("请选择授权时长"); return; } if (string.IsNullOrEmpty(SaltTextBox.Text)) { Message.Text = ("请设置Salt值"); return; } string durationTag = selectedItem.Tag.ToString(); string salt = SaltTextBox.Text.Trim(); string globalOTP = GenerateGlobalOTP(durationTag, salt); DynamicLicenseTextBox.Text = globalOTP; Clipboard.SetText(globalOTP); Message.Text = "动态应急码已复制!"; } } }