How to license your software: a practical guide
A practical, end-to-end guide to licensing your software: pick a licensing model, issue signed license keys instead of a homegrown string, deliver them to customers, verify offline and activate online, enforce seats and node-locking, gate features with entitlements, and handle trials, renewals and revocation.
To license your software, you pick a licensing model, issue each customer a cryptographically signed license rather than a homegrown key string, deliver it at purchase, and verify it inside your app — an offline signature check, optionally paired with online activation to enforce seats and revocation — then gate features on entitlements and handle trials, renewals and revocation on the server. This guide walks that whole path end to end, product-neutral in the mechanics, so you can wire up real licensing whether you build it yourself or adopt a service.
Step 1 — Choose a licensing model
Everything downstream follows from this choice, so make it first. The common models:
- Perpetual — the customer buys the software once and runs that version forever. Simple, but there is no recurring revenue and no built-in reason for the license to ever stop working.
- Subscription / term — the license is valid for a fixed window (typically a year) and carries an expiry the app enforces. This is the default for most modern software because it aligns revenue with ongoing value.
- Seat-based — the license permits a set number of activations, one per machine or user, and you enforce the cap. Standard for team and enterprise sales.
- Trial / evaluation — a time-limited license that unlocks the paid experience for a fixed number of days, then expires.
Most real products combine these: a subscription with a seat count, or a perpetual license with a support-and-updates window. You do not have to pick exactly one. (For a full reference on each model and when to use it, see our companion post, software license types explained.)
Step 2 — Issue a signed license, not a key string
The single most important decision is what a license is on the wire. The tempting shortcut — generate a random or patterned key string and have your app accept anything matching that pattern — fails structurally: if your app can decide a key is valid by looking at the key, then everything needed to mint a valid key is inside your app, and a decompiler turns your validation code into a keygen.
The correct unit is a signed license: a small payload of facts — licensee, product, tier, seats, expiry, entitlements — with a cryptographic signature over it. Your server signs the payload with an RSA private key that never leaves the server; your app embeds only the matching public key. A public key can verify a signature but can never create one, so:
- Decompiling your app reveals only the public key, which is useless for forgery.
- Changing one byte of the payload (bumping
tierfromfreetopro) breaks the signature, so validation fails.
This is the same asymmetry behind TLS and code signing, pointed at licensing. In .NET the primitives ship in the box, which is exactly the trap — the signature check is a few lines, so people assume the whole system is easy, and it isn’t (Step 5 onward is where the real work lives).
Step 3 — Deliver the license to your customer
Once you can sign a license, you need to get it to the buyer without a manual email for every sale. In practice there are three delivery paths, and a real product uses more than one:
- On purchase, automatically. Wire your checkout or Merchant-of-Record provider’s “paid” event to your licensing backend so a successful payment mints and emails the key, and a refund or cancellation revokes it — no human in the loop.
- From an admin console, for manual sales, resellers, and support-issued keys.
- Self-service, where the customer starts a trial from your own site and receives a key instantly (Step 8).
The key you deliver is the string the customer later pastes into your app to activate. Everything else — the entitlements, the expiry, the seat count — travels inside the signed payload, not in the visible key.
Step 4 — Verify the license in your app
Verification has two halves, and you almost always want both.
Offline signature check. At startup, your app recomputes the signature check against its embedded public key and reads the now-trusted fields: product match, expiry, node-lock. This needs no network, so it works air-gapped and survives outages. The cardinal rule is to fail closed — any problem (bad signature, wrong product, expired, revoked, a clock rolled backward to cheat a trial, a missing file) must resolve to the unlicensed state, never throw and never unlock. If a check failed open, the easiest crack would be to make it fail on purpose.
Online activation. When you need to enforce seats or revoke keys promptly, add one online step: the app exchanges the license key plus a stable machine id for a short-lived, signed lease bound to that machine. The lease is verified offline and cached locally, so after a single activation the app keeps working offline until the lease nears expiry — at which point it reconciles seats and revocation with the server. A brief outage is covered by the still-valid cached lease, a grace window, so you don’t lock out a paying customer over a flaky connection.
Step 5 — Enforce seats and node-locking
You cannot count seats offline — a lone client has no idea how many other machines run the same key. The moment you promise “3 seats,” you need a server that records activations, enforces the cap atomically, makes re-activating an already-bound machine idempotent, and lets a customer release a seat when they retire a device. Pair that with node-locking: bind each seat to a machine fingerprint derived from stable hardware and OS attributes — with tolerance, so a swapped network card or upgraded disk doesn’t lock an honest user out and generate a support ticket.
Step 6 — Gate features with entitlements and tiers
Ship one binary and unlock different capabilities per plan by putting the capabilities in the license. Define a tier (e.g. pro, enterprise) whose entitlement template carries named flags ("export": "true") and numeric limits ("max-projects": "10"), and gate features on those entitlements rather than a hard-coded tier check — IsEnabled("export"), GetLimit("max-projects", fallback: 1). Then changing what a plan includes is a dashboard edit, not a new release. Every entitlement read should fail closed too: a missing flag reads as off, a missing limit returns your fallback.
Step 7 — Handle trials, renewals and revocation
These three are what turn a license check into a business:
- Trials are time-limited keys that unlock the paid experience and auto-expire. Make them one-per-email-per-product and idempotent so a refresh never resets the clock, and let a trial convert to a paid license with no reinstall.
- Renewals extend an existing subscription’s expiry (and can adjust seats) without issuing a new key — the customer keeps the same license.
- Revocation kills a refunded, charged-back or leaked key. Online, it takes effect on the next lease refresh; offline, you distribute a signed revocation list your app honors with no network call.
Build it, or buy it
The signature check is an afternoon. Everything from Step 3 onward — key management and rotation, server-side seat counting, revocation that reaches offline clients, trials with abuse controls, a customer portal so seat moves and re-downloads don’t become support tickets — is a stateful, security-sensitive backend that no customer will ever thank you for. That’s the real build-vs-buy question, and we break down the itemized cost in build vs buy: software licensing for .NET.
Where Keyright fits
Keyright is the licensing infrastructure we build at Delta1 Labs, and it hands you this entire guide as a service. It issues signed offline license files and short-lived online activation leases from one system; enforces node-locking and seats server-side with self-service seat release; carries entitlements and tiers so one binary unlocks per plan; supports perpetual and term licenses, self-service trials, renewals, transfers, and one-click revocation that reaches both online and offline clients. Every tenant gets its own isolated RSA signing key — the private half stored encrypted server-side and never exposed — plus a hosted customer portal and SDKs for .NET, Node, Python and Java that all fail closed by design.
To wire it up end to end, start with the Keyright getting-started guide; the free plan covers real licensing before you pay a cent, and you can see the plans here.
Try Nebula.NET
Harden your .NET code in minutes — start with the free edition.