diff --git a/KeyGen/App.xaml b/KeyGen/App.xaml index 5be5bd3..e928a93 100644 --- a/KeyGen/App.xaml +++ b/KeyGen/App.xaml @@ -8,11 +8,12 @@ - + - + + diff --git a/KeyGen/KeyGen.csproj b/KeyGen/KeyGen.csproj index 00ff778..b9331c4 100644 --- a/KeyGen/KeyGen.csproj +++ b/KeyGen/KeyGen.csproj @@ -8,6 +8,7 @@ true true authentication.ico + false diff --git a/KeyGen/MainWindow.xaml b/KeyGen/MainWindow.xaml index 50ae2d1..3394cba 100644 --- a/KeyGen/MainWindow.xaml +++ b/KeyGen/MainWindow.xaml @@ -28,131 +28,198 @@ - - - - - - - - - - - - - - + + + + + + + - + + + + + - - - - + VerticalAlignment="Top" + Fill="Gray" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/KeyGen/MainWindow.xaml.cs b/KeyGen/MainWindow.xaml.cs index 5ce1e74..feee365 100644 --- a/KeyGen/MainWindow.xaml.cs +++ b/KeyGen/MainWindow.xaml.cs @@ -3,6 +3,7 @@ using System.IO; using System.Security.Cryptography; using System.Text; using System.Windows; +using System.Windows.Controls; using iNKORE.UI.WPF.Modern; @@ -174,7 +175,7 @@ namespace KeyGen Message.Text = ($"导入文件私钥完成"); } } - private void Button_ToggleTheme_Click(object sender, RoutedEventArgs e) + private void ToggleTheme_Click(object sender, RoutedEventArgs e) { if (ThemeManager.Current.ApplicationTheme == ApplicationTheme.Dark) { @@ -185,5 +186,61 @@ namespace KeyGen 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 = "动态应急码已复制!"; + } } } diff --git a/Szmedi.CADkits/ReviewAndMatchWindow.xaml b/Szmedi.CADkits/ReviewAndMatchWindow.xaml index 50407dd..5a4a07c 100644 --- a/Szmedi.CADkits/ReviewAndMatchWindow.xaml +++ b/Szmedi.CADkits/ReviewAndMatchWindow.xaml @@ -68,6 +68,12 @@ Margin="5" Click="CleanLinesButton_Click" Content="清理连线" /> +