commit 2ef68dd4778de13034578e484b304702aa3eed38 Author: dtrentin Date: Sun May 17 23:33:30 2026 +0200 feat: initial LicenseGenerator -- keygen, fingerprint, sign commands Standalone .NET 4.7.2 console tool for NcProgramManager offline licensing. - keygen: RSA 2048-bit key pair generation - fingerprint: WMI machine fingerprint (Windows only) - sign: RSA-SHA256 sign fingerprint, write .lic file Co-Authored-By: Claude Sonnet 4.6 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..718d27f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +bin/ +obj/ +.vs/ +*.user +private.key.xml +*.lic +packages/ diff --git a/Commands/FingerprintCommand.cs b/Commands/FingerprintCommand.cs new file mode 100644 index 0000000..0d4ef14 --- /dev/null +++ b/Commands/FingerprintCommand.cs @@ -0,0 +1,44 @@ +using System; +using System.Management; + +namespace LicenseGenerator.Commands +{ + internal static class FingerprintCommand + { + // Windows + WMI only. Matches MachineFingerprint.GetFingerprint() logic. + internal static void Execute() + { + try + { + string pcInfo = string.Empty; + + using (var mc = new ManagementClass("win32_processor")) + using (ManagementObjectCollection moc = mc.GetInstances()) + { + foreach (ManagementObject mo in moc) + { + pcInfo = mo.Properties["processorID"].Value.ToString(); + break; + } + } + + using (var searcher = new ManagementObjectSearcher( + "root\\CIMV2", "SELECT * FROM Win32_BaseBoard")) + { + foreach (ManagementObject obj in searcher.Get()) + { + pcInfo += obj["SerialNumber"].ToString(); + break; + } + } + + Console.WriteLine(pcInfo); + } + catch (Exception ex) + { + Console.Error.WriteLine("Error reading fingerprint (Windows + WMI required): " + ex.Message); + Environment.Exit(1); + } + } + } +} diff --git a/Commands/KeygenCommand.cs b/Commands/KeygenCommand.cs new file mode 100644 index 0000000..843f892 --- /dev/null +++ b/Commands/KeygenCommand.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; +using System.Security.Cryptography; + +namespace LicenseGenerator.Commands +{ + internal static class KeygenCommand + { + internal static void Execute() + { + using (var rsa = new RSACryptoServiceProvider(2048)) + { + string privateXml = rsa.ToXmlString(true); + string publicXml = rsa.ToXmlString(false); + File.WriteAllText("private.key.xml", privateXml); + Console.WriteLine("=== PUBLIC KEY XML -- paste into LicenseValidator.cs ==="); + Console.WriteLine(publicXml); + Console.WriteLine("========================================================"); + Console.WriteLine("Private key saved: private.key.xml"); + Console.WriteLine("KEEP private.key.xml SECRET -- never distribute it."); + } + } + } +} diff --git a/Commands/SignCommand.cs b/Commands/SignCommand.cs new file mode 100644 index 0000000..7764af4 --- /dev/null +++ b/Commands/SignCommand.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace LicenseGenerator.Commands +{ + internal static class SignCommand + { + internal static void Execute(InputArgs args) + { + if (string.IsNullOrEmpty(args.KeyPath)) + { + Console.Error.WriteLine("Missing: -key="); + Environment.Exit(1); + } + if (string.IsNullOrEmpty(args.Fingerprint)) + { + Console.Error.WriteLine("Missing: -fingerprint="); + Environment.Exit(1); + } + + string privateXml = File.ReadAllText(args.KeyPath); + byte[] data = Encoding.UTF8.GetBytes(args.Fingerprint); + + using (var rsa = new RSACryptoServiceProvider()) + { + rsa.FromXmlString(privateXml); + byte[] signature = rsa.SignData(data, new SHA256CryptoServiceProvider()); + string base64 = Convert.ToBase64String(signature); + File.WriteAllText(args.OutPath, base64); + Console.WriteLine("License written: " + args.OutPath); + } + } + } +} diff --git a/Dockerfile.build b/Dockerfile.build new file mode 100644 index 0000000..b92d907 --- /dev/null +++ b/Dockerfile.build @@ -0,0 +1,8 @@ +FROM mono:6.12 +WORKDIR /build +RUN cert-sync /etc/ssl/certs/ca-certificates.crt +COPY LicenseGenerator.csproj ./ +RUN nuget restore LicenseGenerator.csproj -NonInteractive 2>/dev/null || true +COPY . . +RUN msbuild LicenseGenerator.csproj /p:Configuration=Release /p:Platform=AnyCPU /v:minimal /nologo +CMD mono bin/Release/LicenseGenerator.exe keygen diff --git a/InputArgs.cs b/InputArgs.cs new file mode 100644 index 0000000..377cecb --- /dev/null +++ b/InputArgs.cs @@ -0,0 +1,38 @@ +namespace LicenseGenerator +{ + internal class InputArgs + { + internal string Command { get; private set; } + internal string KeyPath { get; private set; } + internal string Fingerprint { get; private set; } + internal string OutPath { get; private set; } + + private InputArgs() { } + + internal static InputArgs Parse(string[] args) + { + var result = new InputArgs + { + Command = args.Length > 0 ? args[0].ToLowerInvariant() : string.Empty, + OutPath = "license.lic" + }; + + for (int i = 1; i < args.Length; i++) + { + string arg = args[i]; + int eq = arg.IndexOf('='); + if (eq < 0) continue; + string key = arg.Substring(0, eq).TrimStart('-').ToLowerInvariant(); + string val = arg.Substring(eq + 1); + switch (key) + { + case "key": result.KeyPath = val; break; + case "fingerprint": result.Fingerprint = val; break; + case "out": result.OutPath = val; break; + } + } + + return result; + } + } +} diff --git a/LicenseGenerator.csproj b/LicenseGenerator.csproj new file mode 100644 index 0000000..c6fa228 --- /dev/null +++ b/LicenseGenerator.csproj @@ -0,0 +1,49 @@ + + + + + Debug + AnyCPU + {A7B3C4D5-E6F7-4801-9ABC-DEF012345678} + Exe + LicenseGenerator + LicenseGenerator + v4.7.2 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + diff --git a/LicenseGenerator.sln b/LicenseGenerator.sln new file mode 100644 index 0000000..0676150 --- /dev/null +++ b/LicenseGenerator.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.28701.123 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LicenseGenerator", "LicenseGenerator.csproj", "{A7B3C4D5-E6F7-4801-9ABC-DEF012345678}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A7B3C4D5-E6F7-4801-9ABC-DEF012345678}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7B3C4D5-E6F7-4801-9ABC-DEF012345678}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7B3C4D5-E6F7-4801-9ABC-DEF012345678}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7B3C4D5-E6F7-4801-9ABC-DEF012345678}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..91e38c0 --- /dev/null +++ b/Program.cs @@ -0,0 +1,46 @@ +using System; +using LicenseGenerator.Commands; + +namespace LicenseGenerator +{ + internal class Program + { + private static void Main(string[] args) + { + var input = InputArgs.Parse(args); + switch (input.Command) + { + case "keygen": + KeygenCommand.Execute(); + break; + case "fingerprint": + FingerprintCommand.Execute(); + break; + case "sign": + SignCommand.Execute(input); + break; + default: + PrintUsage(); + Environment.Exit(1); + break; + } + } + + private static void PrintUsage() + { + Console.WriteLine("LicenseGenerator — NcProgramManager license tool"); + Console.WriteLine(); + Console.WriteLine("Commands:"); + Console.WriteLine(" keygen"); + Console.WriteLine(" Generate RSA 2048-bit key pair."); + Console.WriteLine(" Writes private.key.xml to current dir."); + Console.WriteLine(" Prints public key XML to stdout."); + Console.WriteLine(); + Console.WriteLine(" fingerprint"); + Console.WriteLine(" Print machine fingerprint (Windows + WMI required)."); + Console.WriteLine(); + Console.WriteLine(" sign -key= -fingerprint= [-out=]"); + Console.WriteLine(" Sign machine fingerprint, write .lic file."); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..33db869 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("LicenseGenerator")] +[assembly: AssemblyDescription("NcProgramManager license generator tool")] +[assembly: AssemblyProduct("LicenseGenerator")] +[assembly: AssemblyCopyright("")] +[assembly: ComVisible(false)] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]