C# SDK
Signox.Sdk — the official SDK for C# (.NET, Unity). It targets netstandard2.0 only, so it runs as-is on .NET Framework 4.6.1+, .NET 5/6/7/8+, and Unity 2019+ (including IL2CPP).
- Every server response is trusted only after its Ed25519 signature is verified with the product public key (tampering/replay defense).
- The hardware ID (hwid) is generated internally by the SDK — callers never deal with it.
- Verdicts are delivered as
LicenseResultvalues, not exceptions. Exceptions occur only on invalid arguments (empty key, etc.). - The only runtime dependency is
BouncyCastle.Cryptography(for Ed25519 verification, MIT).
Installation (NuGet)
Section titled “Installation (NuGet)”dotnet add package Signox.SdkOr in .csproj:
<PackageReference Include="Signox.Sdk" Version="0.1.0" />Unity setup
Section titled “Unity setup”- Drop the DLLs in directly (recommended, simplest) — copy
Signox.Sdk.dllandBouncyCastle.Cryptography.dllintoAssets/Plugins/. Both are pure managed code, so no extra setup is needed even on the IL2CPP backend. GetBouncyCastle.Cryptography.dllfrom your NuGet cache or nuget.org. - NuGetForUnity — installing
Signox.Sdkvia NuGetForUnity pulls in the dependency automatically.
In Unity, hwid collection attempts external process execution or system file reads; in restricted environments it safely falls back to a low-confidence identifier (weak_hwid).
Quickstart
Section titled “Quickstart”using Signox.Sdk;
var client = new SignoxClient(new SignoxOptions{ // Embed the product public key (SPKI PEM) in your app. See "Embedding the public key" below. ProductPublicKey = @"-----BEGIN PUBLIC KEY-----MCowBQYDK2VwAyEA... (per-product public key)-----END PUBLIC KEY-----", // BaseUrl defaults to https://api.signox.kr // With CacheDir set, the offline-fallback cache is stored on disk CacheDir = System.IO.Path.Combine( System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData), "MyApp", "signox-cache"),});
// 1) Activate (register this device) — once, idempotentLicenseResult act = client.Activate("S4K2MB-7IWQ3F-...", new ActivateOptions{ Name = "John's laptop", // shown in the dashboard (optional)});if (!act.Valid){ // act.Code: DEVICE_LIMIT_REACHED / VM_NOT_ALLOWED / NOT_FOUND ... return;}
// 2) Validate — on every app run. The cache (cache.ttl) skips redundant re-validationLicenseResult v = client.Validate("S4K2MB-7IWQ3F-...");if (v.Valid){ bool proExport = v.Features.TryGetValue("pro_export", out var pe) && pe is bool b && b; // v.Code == VALID | IN_GRACE_PERIOD}else if (v.Code == ValidationCode.NETWORK_ERROR){ // Network failure + no valid cache — handle distinctly from "invalid" in your UX}Public API
Section titled “Public API”| Method | Purpose |
|---|---|
Validate(licenseKey) |
Validate license + device (includes cache/offline fallback) |
Activate(licenseKey[, opts]) |
Activate this device (hwid/anomalies attached automatically) |
Deactivate(licenseKey) |
Deactivate (returns the slot) |
Heartbeat(licenseKey) |
Liveness signal + lightweight re-validation |
ReportUsage(licenseKey, feature[, value]) |
Report metered usage |
RequestOfflineFile(licenseKey) |
Issue a .lic file (content via File.Content) |
ValidateOffline(licContent) |
Fully local .lic verification (no network) |
GetHwid() |
Diagnostic — this device’s hwid |
Verdict codes (ValidationCode)
Section titled “Verdict codes (ValidationCode)”| Group | Codes |
|---|---|
| Valid | VALID, IN_GRACE_PERIOD |
| Invalid (state) | SUSPENDED, REVOKED, EXPIRED |
| Invalid (request/device) | NOT_FOUND, PRODUCT_INACTIVE, HWID_REQUIRED, DEVICE_NOT_ACTIVATED, DEVICE_LIMIT_REACHED, VM_NOT_ALLOWED, FEATURE_NOT_FOUND, USAGE_LIMIT_REACHED |
| SDK-local | SIGNATURE_INVALID, NONCE_MISMATCH, OFFLINE_FILE_INVALID, HWID_MISMATCH, NETWORK_ERROR |
NETWORK_ERROR means no verdict is possible (network failure + no valid cache) — it does not mean the license is invalid. On network failure, a cache within SignoxOptions.OfflineGraceDays (default 7 days counted from when the cache was stored, 0 = no fallback) is used as the verdict with Stale=true. See the online activation guide for the recommended UX per code.
Offline verification (.lic)
Section titled “Offline verification (.lic)”Machines without internet access use a .lic file issued from the portal/dashboard or an online device. See offline activation for the full flow.
// (Online) issue a .lic — on success the file body is in result.File.ContentLicenseResult issued = client.RequestOfflineFile("S4K2MB-7IWQ3F-...");if (issued.Code == ValidationCode.VALID && issued.File != null) System.IO.File.WriteAllText("license.lic", issued.File.Content);
// (Offline) fully local verification — no network; the SDK matches the hwid locallystring lic = System.IO.File.ReadAllText("license.lic");LicenseResult offline = client.ValidateOffline(lic);// offline.Code: VALID | IN_GRACE_PERIOD | EXPIRED | HWID_MISMATCH | SIGNATURE_INVALID | OFFLINE_FILE_INVALIDThread safety
Section titled “Thread safety”SignoxClient is built from immutable options and is safe to call concurrently from multiple threads. Create one instance for the app’s lifetime and reuse it (hwid and cache are memoized).
Embedding the public key (important)
Section titled “Embedding the public key (important)”Always embed ProductPublicKey in your app. The SDK does not fetch the public key from the server — receiving it over the network would let a man-in-the-middle inject a forged key. The public key is not a secret, but swap prevention is the point. Never put the private key in a client (it exists only on the server).