Rotating your license signing keys without locking out customers
Your license keys and leases are only as trustworthy as the key that signs them. Here is how to roll that signing key to a new one with zero downtime — overlapping trust with AdditionalPublicKeysBase64, a staged rollout, and how leases and offline files make the transition safe.
A license key is a claim — “this customer owns the Pro edition, 3 seats, until next March” — and your app believes that claim for exactly one reason: it was signed by a key your app trusts. That signing key is the root of the whole system. Everything downstream — the editions, the seat caps, the expiry, the leases that let an activated machine run offline — inherits its trust from that one key. Which is also why rotating it feels scary: change the root and you risk invalidating every license you have ever issued.
You don’t have to. This post walks through rotating a Keyright signing key with zero customer-visible downtime: what the key actually protects, why a naive swap locks everyone out, how overlapping trust makes the transition seamless, and how leases and offline files decide how long the overlap has to last.
What the signing key actually protects
Keyright is offline-first. When you issue a license, the service signs a canonical claims payload with your product’s private signing key and hands back a copy-pasteable key string. Your app embeds only the matching public key. At validation time the app verifies the signature locally — no network — and only then trusts the tier, expiry and seat count inside. Activation adds a second signed artifact: a short-lived lease (14 days by default) bound to the machine, also signed by that key, which is what lets an activated machine keep running offline until the lease’s grace window elapses.
So the key is doing a lot of load-bearing work, and it is embedded in software you have already shipped. That is exactly the property you want for security — the root of trust is on the client, not on a server an attacker could social-engineer — and exactly the property that makes rotation a design problem rather than a button press.
Why the naive swap is a disaster
Imagine you suspect the private key leaked. The obvious move: generate a new key pair, re-sign new licenses with it, ship an app build that embeds the new public key. Done?
No. The instant that build reaches a customer, every license and lease signed by the old key fails verification on their machine. The signature no longer matches the embedded key, so a paying customer who activated last week drops to the free edition on their next launch. You have turned a precautionary rotation into a field-wide outage.
The failure is structural: the app trusts exactly one key, and you changed it out from under artifacts that are still valid. The fix is to trust two keys for a while.
Overlapping trust: the one idea that makes it safe
Every Keyright SDK verifies an incoming license or lease against a primary public key and against a list of additional public keys. If any trusted key verifies the signature, the artifact is accepted. That turns a hard cutover into an overlap window.
Here is the .NET SDK during normal operation — one key:
var keyright = new KeyrightClient(new KeyrightOptions
{
Product = "my-app",
ServiceUrl = "https://license.example.com",
PublicKeyBase64 = "<current public key>",
});
And here it is mid-rotation. The new key is primary; the outgoing key rides along in AdditionalPublicKeysBase64:
var keyright = new KeyrightClient(new KeyrightOptions
{
Product = "my-app",
ServiceUrl = "https://license.example.com",
PublicKeyBase64 = "<NEW public key>",
// Keep the old key valid through the rotation window:
AdditionalPublicKeysBase64 = new[] { "<previous public key>" },
});
A license signed by the old key still verifies (it matches an additional key). A license or lease signed by the new key verifies too (it matches the primary). Nobody drops a tier. The two keys coexist for exactly as long as artifacts signed by the old one are still in circulation — and then you remove it.
The rotation, step by step
1. Rotate the key on the service. Each product owns its own signing key. One authenticated call rotates it; the old key is retained as a previous key so you can still see what it signed, and the response hands you the new public key to embed:
curl -X POST "$BASE/admin/products/my-app/signing-key" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"rotate": true}'
# → { "signingPublicKey": "<NEW public key>", ... }
(The same thing lives in the dashboard under the product’s Signing key tab — rotation, import and the previous-keys list sit in its Advanced section.) After this, newly issued licenses and newly signed leases use the new key. Licenses already issued are unchanged — their signatures were made at issue time and don’t retroactively change.
2. Ship a build that trusts both keys. Add the new key as PublicKeyBase64 and the outgoing key to AdditionalPublicKeysBase64, as above. Release it through your normal channel. From here on, clients accept artifacts signed by either key.
3. Let leases carry the new key into the field. You don’t have to chase anyone. Because an activated machine refreshes its lease as it nears expiry, every online customer naturally re-leases under the new key within one grace window — 14 days by default. After a couple of windows, essentially every active lease in the field is new-key-signed.
4. Drop the old key. Once no artifact signed by the old key can still be live — every lease signed by it has expired, and no standalone offline file signed by it is still in service — ship a build that removes it from AdditionalPublicKeysBase64. Now only the new key is trusted. The rotation is complete, and if the old private key had leaked, anything it signed is now worthless to an attacker.
What sets the length of the overlap
The overlap can’t be shorter than the longest-lived artifact the old key signed. Three cases:
- Online-activated machines move at the speed of your lease TTL (14 days by default). One grace window after the service switches keys, essentially everyone has re-leased.
- Long-offline machines that were issued a long-TTL offline lease (365 days by default) stay on the old key until that lease expires. If you have those, your overlap is a year, not two weeks — plan for it.
- Standalone offline license files don’t refresh at all; they move at the speed of your releases. A customer on an air-gapped install keeps the old key trusted until they take a build signed by the new one. Keep the old key in the list until you’ve confirmed those customers have migrated.
The rule of thumb: keep the old public key in AdditionalPublicKeysBase64 until the last artifact it signed has expired or been replaced, then remove it in the next release. There is no penalty for leaving it a little longer, and a real one for pulling it too soon.
If the private key actually leaked
Rotation re-points future trust, but a leaked private key means an attacker can forge licenses that your currently-shipped app still trusts — until they take a build that no longer trusts the old key. So pair rotation with revocation. Keyright can publish a signed revocation list, and online activation lets you kill specific leaked keys so an activated-but-revoked machine drops to the free edition on its next lease refresh. For a confirmed compromise, the honest sequence is: rotate, ship the dual-key build quickly, revoke the known-bad issued keys, and shorten the overlap as aggressively as your offline customers allow.
And remember the boundary that applies to all client-side licensing: verification runs on the user’s machine, so a determined attacker with a decompiler can try to patch the check out entirely. That is exactly why Keyright is wire-compatible with Nebula.NET — you obfuscate and harden the assembly that embeds the public key and performs the check, so patching it out costs real effort. The signing key is the cryptographic root; obfuscation protects the code that enforces it.
Wiring it up
Signing-key rotation in Keyright is a one-call server operation plus a one-line client change, made safe by overlapping trust and paced by your lease lifetime. Generate or rotate a key from the product’s Signing key tab (or the signing-key endpoint), embed the new one as PublicKeyBase64, keep the outgoing one in AdditionalPublicKeysBase64 for one lease window — longer if you ship offline files — then drop it. See the .NET SDK integration guide for the exact options and the security & leases page for how leases decide your overlap, or the Keyright product page for the whole picture. The free plan is enough to rehearse a full rotation end to end before you rely on it.
Try Nebula.NET
Harden your .NET code in minutes — start with the free edition.