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 <noreply@anthropic.com>
This commit is contained in:
dtrentin 2026-05-17 23:33:30 +02:00
commit 2ef68dd477
10 changed files with 284 additions and 0 deletions

7
.gitignore vendored Normal file
View file

@ -0,0 +1,7 @@
bin/
obj/
.vs/
*.user
private.key.xml
*.lic
packages/

View file

@ -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);
}
}
}
}

24
Commands/KeygenCommand.cs Normal file
View file

@ -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.");
}
}
}
}

36
Commands/SignCommand.cs Normal file
View file

@ -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=<path-to-private.key.xml>");
Environment.Exit(1);
}
if (string.IsNullOrEmpty(args.Fingerprint))
{
Console.Error.WriteLine("Missing: -fingerprint=<machine-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);
}
}
}
}

8
Dockerfile.build Normal file
View file

@ -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

38
InputArgs.cs Normal file
View file

@ -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;
}
}
}

49
LicenseGenerator.csproj Normal file
View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A7B3C4D5-E6F7-4801-9ABC-DEF012345678}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>LicenseGenerator</RootNamespace>
<AssemblyName>LicenseGenerator</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Management" />
</ItemGroup>
<ItemGroup>
<Compile Include="InputArgs.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Commands\KeygenCommand.cs" />
<Compile Include="Commands\FingerprintCommand.cs" />
<Compile Include="Commands\SignCommand.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

22
LicenseGenerator.sln Normal file
View file

@ -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

46
Program.cs Normal file
View file

@ -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=<private.key.xml> -fingerprint=<fp> [-out=<license.lic>]");
Console.WriteLine(" Sign machine fingerprint, write .lic file.");
}
}
}

View file

@ -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")]