Developer FAQ SDK v4.0

Security & Anti-Tampering

How UnifiedLicensing protects your licenses against forgery, clock manipulation, key sharing, and reverse engineering.

Honest scope: this page documents only features that actually ship in the SDKs. Client-side protection raises the cost of piracy — it cannot make your app uncrackable. The authoritative security boundary is always your licensing server.
Q1

What tampering protection does UnifiedLicensing provide?

The SDKs defend licenses with three independent layers. Each layer catches what the others miss:

LayerWhat it detectsRunsAvailability
RSA-2048 signature verification Forged or edited license data — any field changed after signing breaks the signature Client, on every validation All SDKs (JS, Python, PHP, .NET)
Clock tampering detection System time rolled backward, or jumped forward, to extend trials or bypass expiry Client, during offline/hybrid validation JS, Python, .NET
Hardware fingerprinting (machine_id) A license key activated on more machines than allowed Client collects, server decides All SDKs (JS, Python, PHP, .NET)
Why three layers

Signatures stop forgery, clock checks stop time-based bypasses, fingerprints stop key sharing. None of them alone covers the other attacks — together they cover the realistic threat model for distributed software.

Q2

How does RSA-2048 signature verification work?

Every license response your server issues is cryptographically signed. The SDK refuses to trust any license data whose signature doesn't verify.

  1. You generate an RSA-2048 key pair (private key stays on your server / vendor dashboard; the public key ships inside your app as VendorPublicKey).
  2. The server signs the license payload — expiry date, tier, features, machine binding — with the private key.
  3. The SDK verifies the signature locally using the embedded public key before accepting the license, including for offline/cached validations.
.NET — UnifiedLicenseConfig.cs
// Signature verification is on by default.
var config = new UnifiedLicenseConfig
{
    VendorApiKey = "your-api-key",
    ProductKey   = "your-product-key",
    SignatureVerification = true,   // default
    VendorPublicKey = "-----BEGIN PUBLIC KEY-----..."
};

If an attacker edits the expiry date in a cached license file, or hand-crafts a "lifetime" license, verification fails with an invalid signature error — they'd need your private key to produce a valid signature, and forging RSA-2048 is not practical.

Keep the private key private

Only the public key belongs in your application. If your private key ever leaks, rotate it immediately and re-issue licenses — and remember the server database remains the source of truth regardless (see Q5).

Q3

How does clock tampering detection work?

Trial periods and expiry dates are checked against the system clock — so the classic bypass is "set the clock back." The SDK counters by comparing wall-clock time against a monotonic source that users can't set from the OS settings:

  • Backward jump: if the current UTC time is earlier than the last recorded validation, the clock was rolled back → tampering flagged.
  • Forward jump: if wall-clock time advanced far more than the monotonic timer did between two checks, time was skipped ahead beyond a tolerance (60 seconds) → flagged.

This is the reference implementation from the .NET SDK; the JavaScript and Python SDKs apply the same two-check strategy during offline validation:

.NET — Security/ClockTamperDetector.cs
public bool DetectTampering()
{
    if (!_lastValidation.HasValue)
    {
        _lastValidation = DateTime.UtcNow;
        _lastTicks      = Environment.TickCount64;  // monotonic
        return false;
    }

    var now         = DateTime.UtcNow;
    var currentTicks = Environment.TickCount64;

    // Check 1: time moved backward
    if (now < _lastValidation.Value)
    {
        Console.WriteLine("Clock tampering detected: Time moved backward");
        return true;
    }

    // Check 2: wall clock jumped vs. monotonic elapsed time
    var elapsedMs   = currentTicks - _lastTicks;
    var expectedNow = _lastValidation.Value.AddMilliseconds(elapsedMs);
    var difference  = Math.Abs((now - expectedNow).TotalMilliseconds);

    if (difference > 60000)   // > 60s unexplained drift
    {
        Console.WriteLine($"Clock tampering detected: Time jump of {difference}ms");
        return true;
    }

    _lastValidation = now;
    _lastTicks      = currentTicks;
    return false;
}

When tampering is detected during offline validation, validation fails with a "Clock tampering detected" error instead of trusting the manipulated clock. The next successful online validation re-syncs trusted time from the server, so legitimate users who travel across time zones or fix a genuinely wrong BIOS clock recover automatically.

Q4

What is IntegrityGuard, and which SDKs have it?

IntegrityGuard is a .NET-only runtime protection module. It runs five watchdog timers at randomized intervals that continuously re-check the process environment while your app runs. It is not available in the JavaScript, Python, or PHP SDKs — interpreted/web environments don't offer the process-level introspection it relies on.

TimerCheckTriggers failure when…
Timer ADebugger detectionA native/managed debugger is attached to the process
Timer BProcess blacklistKnown RE tools are running (x64dbg, OllyDbg, WinDbg, IDA, Cheat Engine, Process Hacker, dnSpy, ILSpy, Reflector, Fiddler, Wireshark…)
Timer CFingerprint checkHardware fingerprint drifted below 90% similarity vs. startup (VM migration, cloned disk)
Timer DDLL integrityThe executing assembly's file was modified after deployment (write time > creation time)
Timer EHash validationThe stored integrity hash no longer matches computed state

IntegrityGuard is opt-in (disabled by default) and configured through the standard config object — there is no separate global switch to disable it at runtime:

.NET — enabling IntegrityGuard
var config = new UnifiedLicenseConfig
{
    VendorApiKey = "your-api-key",
    ProductKey   = "your-product-key",

    // Protection features (all opt-in)
    EnableIntegrityGuard = true,
    EnableAntiDebug      = true,
    EnableAntiTamper     = true
};

var manager = new UnifiedLicenseManager(config);
Development tip

IntegrityGuard will flag your own debugger. Wrap the flags in #if DEBUG so Release builds are protected while you can still debug locally:

.NET — per-build configuration
var config = new UnifiedLicenseConfig
{
    VendorApiKey = "your-api-key",
    ProductKey   = "your-product-key"
#if DEBUG
    ,
    EnableIntegrityGuard = false   // allow debugging
#endif
};
Q5

Can someone bypass the license check by modifying the SDK?

They can try — and client-side patching is always theoretically possible. That's why the architecture assumes the client will be attacked and puts the decisive checks server-side:

  • The server database is the source of truth. A license only validates if it exists in your database with valid status. An attacker who patches the client binary still has to get a positive answer from your API — a patched client can't invent activations, and unknown keys are rejected server-side no matter what the client says.
  • Licenses can't be forged. Even fully offline, editing a cached license breaks the RSA-2048 signature (Q2). Creating a new valid license requires your private key.
  • Machines can't be faked cheaply. Activations are bound to a machine_id; the server enforces activation limits independently of what the client reports.
What this means in practice

Cracking a UnifiedLicensing-protected app requires either breaking RSA-2048 (infeasible) or compromising your server/database (a very different, much harder attack than editing a local file).

Residual risk — stated plainly

A determined attacker with full offline access can patch out the client-side check itself (e.g., NOP the validation branch in a disassembler). No licensing SDK on the market prevents that. Your mitigations are server-side enforcement, heartbeats that re-validate periodically, and revocation — plus making the crack cost more than the license (see Q8).

Q6

What happens when a license key is shared publicly?

Key sharing is the most common piracy pattern, and it's handled by four cooperating mechanisms:

  1. Machine locking. On first activation the key is bound to the device's machine_id. Every later validation presents the same fingerprint — a different machine doesn't match.
  2. Activation limits. Each tier defines max_activations. Once the limit is reached, additional machines are rejected at activation time. You control the limit per tier, and can reset activations for legitimate customers (new PC, reinstall) from the vendor dashboard.
  3. Heartbeats. SDKs report license_key + machine_id + fingerprint to the server on a schedule (daily / weekly / monthly, with 24-hour rate limiting built in). This gives you visibility into how many distinct machines are actually using a key — abnormal spread is visible in your dashboard.
  4. Revocation. Because every online validation (and hybrid-mode background sync) checks current status server-side, you can kill a leaked key centrally. At the next sync the SDK stops accepting it — including cached/offline usage once the grace period ends.
Honest limitation

An app that never reconnects keeps running on a stale cache until its grace period expires (default 7 days, configurable via GracePeriodHours). Revocation is only as fast as the client's next contact with the server — that's inherent to any offline-tolerant licensing system.

Q7

How do I test the security features?

Verify each layer independently, in a development environment with test keys — never against production licenses:

#TestHowExpected result
1 Signature verification Activate a valid license, then edit any field in the cached license file/blob (e.g., push the expiry date out) Next validation fails: invalid signature
2 Forged license Hand-craft a license payload without signing it (or sign with a self-made key) Rejected — signature doesn't match your vendor public key
3 Clock rollback Start a trial, close the app, set the system clock back several days, relaunch Clock tampering detected; trial/expiry not extended
4 Clock skip-forward Jump the system clock forward past the expiry date License reported expired based on server-synced time on next online validation
5 Machine binding Copy the app + cached license to a second machine (or VM) and validate Activation/validation rejected: machine mismatch or activation limit reached
6 Revocation Revoke a test key in the dashboard, then trigger a validation Key rejected immediately online; cached usage dies at grace-period end
7 IntegrityGuard (.NET only) Build Release with IntegrityGuard enabled, attach x64dbg / run under Cheat Engine Guard triggers within one watchdog interval and fires the tamper response
Before shipping

Repeat tests 1–6 on every platform you ship (JS, Python, PHP, .NET). Behavior differences usually come from storage locations and grace-period defaults, not from missing features.

Q8

Should I add code obfuscation on top of the SDK?

Obfuscation complements licensing: the SDK protects the license, obfuscation makes the surrounding code harder to analyze and patch. Whether it's worth it depends heavily on platform:

PlatformTypical toolingProtection gainedVerdict
.NET Control-flow obfuscation, string encryption, renaming (commercial obfuscators or ConfuserEx-class tools) High — IL decompilers otherwise reconstruct near-source C# Recommended for commercial desktop apps
JavaScript (browser) Minification + mangling (bundler output) Low — everything ships to the client; determined users can read it Minify, don't over-invest
Node.js Bundling/packaging into a single executable (e.g., pkg-style), minification Low–medium — source is often recoverable from snapshots Rely mainly on server-side checks
Python Compile to bytecode/native (Nuitka, PyInstaller + Cython for hot paths) Medium — plain .pyc decompiles easily; native compilation helps a lot Worth it if distributing compiled
PHP (server-side) Not needed — your code never leaves your server N/A — clients only talk to your API Skip; server code isn't distributed
Order of operations

Apply obfuscation as a post-build step after your licensing integration compiles and passes tests. Obfuscation rewrites symbols and control flow, so testing licensing on the obfuscated build (Q7) is part of your release checklist.

Q9

What's the difference between licensing and DRM?

They're related but solve different problems, and UnifiedLicensing is a licensing system:

Licensing (UnifiedLicensing)DRM
GoalControl who may use your software and at what tierControl access to content/media itself
ProtectsYour revenue and business rules (tiers, seats, trials, expiry)The media files — video, audio, ebooks — end-to-end
MechanismKey validation, signatures, machine binding, heartbeats, revocationContent encryption at rest and in playback, licensed decoders, output controls
User frictionActivate once; works offline within grace periodOngoing — every playback session touches the DRM layer
Typical stackUnifiedLicensing SDK + your product logicWidevine, FairPlay, PlayReady (or similar)
Good fit forDesktop apps, SaaS clients, plugins, games, dev toolsStreaming media, published content distributions

If you're shipping an application and want to enforce per-seat, per-tier, trial, and subscription rules — that's licensing, and it's exactly what these SDKs do. If you need to encrypt the media assets themselves so they're unusable outside an approved player, you need a DRM system alongside (or instead of) licensing.