GST Licensing Machine Binding Guide
This document describes the architecture of GST.Core.Licensing, the Machine Fingerprint mechanism, and the deployment workflow.
1. Architecture Overview
┌──────────────────────────────────────────────────┐
│ Metalama Aspect │
│ [LicenseCheck] / [LicenseRequired("feature")] │
├──────────────────────────────────────────────────┤
│ ILicenseService │
│ Validation, Feature checks, USB Dongle, │
│ background monitoring │
├──────────────────────────────────────────────────┤
│ IReactorLicenseProvider │
│ .NET Reactor License.Status wrapper │
├──────────────────────────────────────────────────┤
│ Hardware Layer │
│ MachineInfoService (WMI Fingerprint) │
│ UsbDeviceService (USB Dongle detection) │
└──────────────────────────────────────────────────┘
2. Core Interfaces
2.1 ILicenseService
public interface ILicenseService
{
ILicenseInfo? CurrentLicense { get; }
bool IsLicenseValid();
bool IsFeatureEnabled(string featureCode);
bool IsAnyFeatureEnabled(params string[] featureCodes);
IReadOnlyList<string> GetEnabledFeatures();
int? GetLimit(string limitCode);
bool IsUsbDonglePresent();
Task<LicenseActivationResult> ActivateAsync(string licenseKey);
Task<bool> DeactivateAsync();
}
2.2 ILicenseInfo
public interface ILicenseInfo
{
string LicenseId { get; }
string CustomerName { get; }
LicenseTier Tier { get; } // Trial/Basic/Standard/Professional/Enterprise
DateTime ExpiresAt { get; }
int DaysRemaining { get; }
bool IsExpired { get; }
bool IsTrial { get; }
LicenseBindingMode BindingMode { get; }
}
2.3 LicenseBindingMode
[Flags]
public enum LicenseBindingMode
{
None = 0,
Machine = 1, // Bound to hardware Fingerprint
UsbDongle = 2, // Bound to USB Dongle
MachineOrUsb = 3, // Either one (flexible)
MachineAndUsb = 4, // Both required (strict)
Online = 8 // Requires online verification
}
3. Machine Fingerprint
3.1 Components
| Component | WMI Query | Example |
|---|---|---|
| CPU ID | Win32_Processor.ProcessorId | BFEBFBFF000A0653 |
| Motherboard Serial | Win32_BaseBoard.SerialNumber | DBBGV1100120700A379100 |
| BIOS Serial | Win32_BIOS.SerialNumber | UDBGXTA00K221008170001 |
| HDD Serial | Win32_DiskDrive.SerialNumber (Index=0) | 0000_0000_... |
| MAC Address | NetworkInterface.GetPhysicalAddress() (highest speed non-Loopback) | 2C-0D-A7-B4-3B-FB |
3.2 Combined Hash
CombinedHash = SHA256(UTF8("{CpuId}|{MotherboardSerial}|{BiosSerial}|{HddSerial}"))
Only non-empty components participate in the Hash calculation.
3.3 Fault Tolerance
public bool Matches(MachineFingerprint other, int tolerance = 1)
By default, 1 mismatched component is tolerated, handling scenarios where hardware is replaced (e.g., replacing a network card or hard drive).
4. USB Dongle Support
4.1 Detection Method
Win32_DiskDrive (InterfaceType='USB')
→ Win32_DiskDriveToDiskPartition
→ Win32_LogicalDiskToPartition
→ Retrieve DriveLetter, SerialNumber, VID/PID
4.2 UsbDeviceInfo
public record UsbDeviceInfo
{
public required string DeviceId { get; init; }
public required string SerialNumber { get; init; }
public string VolumeLabel { get; init; }
public string DriveLetter { get; init; }
public string VendorId { get; init; } // USB VID
public string ProductId { get; init; } // USB PID
}
5. Metalama Aspect Integration
5.1 [LicenseCheck] — Verify License Exists
[LicenseCheck]
public void PerformCriticalOperation() { /* ... */ }
// 無有效 License → 拋出 LicenseException
[LicenseCheck(ThrowOnFailure = false)]
public void PerformOptionalOperation() { /* ... */ }
// 無有效 License → 回傳 default,不拋例外
5.2 [LicenseRequired] — Verify Specific Feature
[LicenseRequired("AdvancedReporting")]
public Report GenerateAdvancedReport() { /* ... */ }
// Feature 未啟用 → 拋出 LicenseException
6. DI Registration
services.AddLicensing(options =>
{
options.LicenseFilePath = Path.Combine(configDir, "license.lic");
options.ThrowOnLicenseFailure = false;
options.BackgroundCheckIntervalMinutes = 30;
options.FingerprintTolerance = 1; // 容許 1 個硬體元件不符
options.AllowOfflineMode = true; // 支援離線環境
});
7. Deployment Workflow
7.1 Fresh Deployment
1. Run the application on the target machine
└→ Application detects no License
└→ Exports hardware-id.txt (containing CombinedHash)
2. User sends hardware-id.txt to the development team
3. Development team uses .NET Reactor License Manager to generate license.lic
└→ Bound to the machine's Hardware ID
4. Place license.lic at %APPDATA%\{AppName}\license.lic
5. Restart the application → Verification passes
7.2 License File Location
%APPDATA%\ProcessVision\license.lic
7.3 App.xaml.cs Loading Sequence
// 1. 複製 License 檔(在 DI 之前,Reactor 保護 DLL 載入前)
CopyLicenseFiles();
// 2. DI 註冊 AddLicensing()
// 3. 初始化 AspectServiceLocator(啟用 Aspect 注入)
AspectServiceLocator.Initialize(Services);
// 4. 驗證 License 狀態
CheckLicenseStatus();
// 5. 若無 License,匯出 Hardware ID
ExportHardwareId();
8. Debug vs Release Behavior
| Scenario | Debug | Release |
|---|---|---|
| No License | Silently allows all features | Shows countdown window / restricts features |
| Validator | DevelopmentLicenseValidator (all pass) | LicenseValidator (Reactor verification) |
| Feature Check | All enabled | Based on License KeyValue |
9. .NET Reactor Hardware Lock Differences
| GST.Core.Licensing | .NET Reactor Hardware Lock | |
|---|---|---|
| Level | Application layer | DLL protection layer |
| Binding Method | SHA256 Fingerprint | Reactor built-in HID |
| Fault Tolerance | Supported (tolerance parameter) | Not supported |
| Feature Control | Supported (KeyValue) | Not supported |
| Purpose | Application license management | Prevent DLL copy execution |
The two are independent mechanisms and can be used simultaneously.
10. Frequently Asked Questions
10.1 License Verification Failure
- Confirm
license.licis at the correct path (%APPDATA%\{AppName}\) - Confirm the License has not expired
- Confirm hardware has not been significantly replaced (exceeding tolerance)
- Check whether
hardware-id.txtmatches the License
10.2 License Invalidated After Hardware Replacement
Replacing more than 1 hardware component simultaneously (e.g., replacing both the motherboard and hard drive) will cause a Fingerprint mismatch. A new Hardware ID must be exported and a new License generated.
10.3 Offline Environment (Air-Gapped)
ValidateOnlineAsync() currently returns true (no network required), specifically designed for factory offline environments.
Document Version: v1.0 | Created: 2026-03-16 | Linear Issue: GST-172