Eine Lizenz, vier Runtimes: denselben Keyright-Schlüssel aus .NET, Node, Python und Java verifizieren
Eine Produktlinie ist selten eine einzige Sprache — eine .NET-Desktop-App, eine Node-CLI, ein Python-Datenwerkzeug, ein JVM-Dienst, alles als ein Produkt verkauft. Die bequeme Antwort gibt jedem seine eigene Lizenzierung und lässt den Kunden mit vier Schlüsseln jonglieren. Die bessere: Ein einziger Mandant signiert eine Lizenz mit einem privaten RSA-Schlüssel, jede Komponente bettet denselben öffentlichen Schlüssel ein, und vier SDKs verifizieren offline das identische signierte Lizenz- und Lease-Format. Dieser Beitrag zeigt denselben Ablauf aus Initialisieren-Validieren-Begrenzen in C#, JavaScript, Python und Java nebeneinander, erklärt, warum der Maschinen-Fingerabdruck byteweise kompatibel über alle hinweg ist, sodass ein Platz jede Komponente auf einer Maschine abdeckt, und behandelt die zwei Dinge, die wirklich pro Komponente sind — der lokale Lease-Cache und der Lizenzauflösungspfad — sowie, wie eine Schlüsselrotation die ganze Linie auf einmal durchläuft.
Die meisten Produkte, die man „eine App” nennt, sind in Wirklichkeit mehrere Programme im Trenchcoat. Was ein Kunde als Acme Suite kauft, mag eine WPF-Desktop-App in C# sein, ein dotnet-und-npm-Kommandozeilenwerkzeug in Node, eine Daten knetende Bibliothek in Python und ein langlebiger Dienst auf der JVM — vier Runtimes, ein Kauf. Die Frage, die entscheidet, ob das Lizenzieren dieses Produkts angenehm oder elend ist, ist einfach: Bekommt der Kunde eine Lizenz, die jede Komponente ehrt, oder vier getrennte Aktivierungen, die auseinanderdriften, sobald jemand die Preisseite anrührt?
Keyrights Antwort ist, dass alle vier SDKs — .NET, Node.js, Python und Java — exakt dasselbe signierte Lizenz- und Lease-Format gegen den gleichen öffentlichen Schlüssel verifizieren. Ein Mandant signiert; vier Runtimes verifizieren; der Kunde sieht einen Schlüssel. Dieser Beitrag zeigt diesen Ablauf in allen vier Sprachen auf einmal und widmet dann seine Zeit den zwei Dingen, die wirklich geteilt sind (das Signaturmodell und der Maschinen-Fingerabdruck), und den zwei, die wirklich pro Komponente sind (der Lease-Cache und der Auflösungspfad), denn dort gelingt oder stolpert ein polyglotter Rollout.
Das geteilte Modell, einmal
Das Modell ist identisch, egal welches Runtime fragt. Sie stellen einen Lizenzschlüssel serverseitig aus; Ihr Mandant signiert ihn mit einem privaten RSA-Schlüssel, der den Server nie verlässt. Jede Komponente Ihres Produkts bettet nur den passenden öffentlichen Schlüssel ein — eine base64-SubjectPublicKeyInfo-Zeichenkette, die kein Geheimnis ist und nur verifizieren, nie prägen kann. Eine Routineprüfung verifiziert den Schlüssel offline gegen diesen öffentlichen Schlüssel: Signatur, Produkt-Slug, Node-Lock, Ablauf, Widerruf, Zustand der Uhrmanipulation. Alles, was nicht stimmt, löst sich zur Free-Edition auf, statt eine Ausnahme zu werfen. Es scheitert fail-closed, überall, auf dieselbe Weise.
Derselbe Ablauf in vier Sprachen
Hier ist die ganze Client-Geschichte — einmal initialisieren, offline validieren, eine Funktion anhand einer Berechtigung begrenzen — in jedem SDK. Beachten Sie, dass sich nichts außer der Sprache ändert: derselbe Produkt-Slug, derselbe öffentliche Schlüssel, dieselbe export-Berechtigung.
.NET — validate() gibt ein LicenseInfo zurück; die Aktivierung ist asynchron:
using Keyright.Client;
static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
Product = "acme-suite", // one slug for the whole product line
PublicKeyBase64 = "MIIBIjANBgkq...", // the same public key in every component
ServiceUrl = "https://licensing.acme.example", // your issuing service
});
LicenseInfo info = License.Validate(); // offline, never throws, fails closed to Free
if (info.IsPaid && License.IsEnabled("export"))
EnableExport();
Node.js — validate() gibt { info, source } zurück; activate ist asynchron:
const { KeyrightClient } = require('keyright');
const license = KeyrightClient.initialize({
product: 'acme-suite',
publicKeyBase64: 'MIIBIjANBgkq...',
serviceUrl: 'https://licensing.acme.example',
});
const { info } = license.validate(); // synchronous, fails closed to free
if (info.isPaid && info.entitlements.isEnabled('export'))
enableExport();
Python — validate() gibt ein Tupel (info, source) zurück, mit validate_info() für nur die Info:
from keyright import KeyrightClient, KeyrightOptions
license = KeyrightClient.initialize(KeyrightOptions(
product="acme-suite",
public_key_base64="MIIBIjANBgkq...",
service_url="https://licensing.acme.example",
))
info = license.validate_info() # fails closed to Free
if info.is_paid and info.entitlements.is_enabled("export"):
enable_export()
Java — validate() gibt ein Result mit öffentlichen info/source-Feldern zurück; die Aktivierung ist synchron:
import com.delta1labs.keyright.*;
KeyrightClient client = KeyrightClient.initialize(
new KeyrightOptions()
.product("acme-suite")
.publicKeyBase64("MIIBIjANBgkq...")
.serviceUrl("https://licensing.acme.example"));
LicenseInfo info = client.validateInfo(); // fails closed to Free
if (info.isPaid() && info.entitlements.isEnabled("export"))
enableExport();
Vier Runtimes, ein Verhalten. Die einzigen Unterschiede sind idiomatisch — PascalCase gegen camelCase gegen snake_case, ein Tupel gegen ein Objekt gegen einen out-Parameter für den Auflösungs-source, async in C# und Node gegen synchrone Aufrufe in Python und Java. Die RSA-Verifikation, die Garantie, fail-closed zu Free zu scheitern, und die Berechtigungssemantik sind byteweise gleich.
Der Gewinn: Ein Platz deckt die ganze Maschine ab
Die Funktion, die dies zu mehr als einer Bequemlichkeit macht, ist das Node-Locking. Jedes SDK leitet einen Maschinen-Fingerabdruck ab — verankert an der OS-Maschinen-GUID plus weichen Signalen wie Hostname und OS — und dieser Fingerabdruck ist byteweise kompatibel über alle vier SDKs. Die Dokumentation des Java-SDK sagt es klar: Sein Fingerabdruck ist „byteweise kompatibel mit den anderen SDKs”. Die praktische Konsequenz ist groß.
Wenn irgendeine Komponente aktiviert, sendet sie machineId = MachineFingerprint.current().toBoundString() (dieselbe Zeichenkette in jeder Sprache), und der Server bindet einen Platz an (key, machineId). Weil die Maschinen-ID identisch über die .NET-App, die Node-CLI, das Python-Werkzeug und den JVM-Dienst auf einer Maschine ist, und weil das erneute Aktivieren einer bereits gebundenen Maschine idempotent ist, lösen alle vier Komponenten auf dieser Maschine zum selben Platz auf. Eine Drei-Platz-Lizenz sind drei Maschinen, nicht zwölf Komponenten-Aktivierungen. Ihr Kunde installiert die Suite auf drei Laptops und ist voll lizenziert — genau das mentale Modell, das er bereits hat, und genau das, was ein Zähler pro Komponente falsch gemacht hätte.
Um die ID einer Maschine für einen Support-Anruf oder eine air-gapped-Aktivierung zu bekommen, bietet jedes SDK dasselbe in seinem eigenen Idiom — MachineFingerprint.current().toBoundString() in .NET und Java, .to_bound_string() in Python, MachineFingerprint.current().toBoundString() in Node — und eine kurze Anzeigeform KR-XXXX-XXXX für Menschen.
Was wirklich pro Komponente ist
Zwei Dinge überqueren Komponentengrenzen nicht automatisch, und ein sauberer Rollout plant für beide.
Der Lease-Cache ist lokal zu jedem SDK. Wenn eine Komponente aktiviert, cached sie den signierten Lease in ihrem eigenen OS-app-data-Pfad (.../Keyright/<product>/lease.json und eine Geschwister-Lease-Datei). Der Cache der .NET-App ist nicht der Cache der Node-CLI. Also aktiviert sich in der Standardkonfiguration jede Komponente einmal — die erste macht einen echten Umlauf und verbraucht den Platz; jede spätere Komponente auf derselben Maschine aktiviert erneut, passt kostenlos zum bestehenden Platz und cached ihren eigenen Lease. Das ist harmlos, aber es sind N Aktivierungen. Wenn Sie lieber einmal für die ganze Suite aktivieren, haben Sie zwei saubere Optionen:
- Richten Sie jede Komponente auf eine einzige Lizenzdatei über die gemeinsame Umgebungsvariable. Alle vier SDKs ehren
KEYRIGHT_LICENSEals Auflösungsquelle (Node liest sie, wenn SieenvVarName: 'KEYRIGHT_LICENSE'setzen; die anderen verwenden sie standardmäßig), also lizenziert ein Installer, der eine aktivierte Lizenzdatei ablegt undKEYRIGHT_LICENSEexportiert, das ganze Bündel ohne Umlauf pro Komponente. - Oder lassen Sie eine Komponente die Aktivierung besitzen und ihren Lease an einen Pfad schreiben, den die anderen über
configLicensePath/LicenseFilePathlesen.
Die Auflösungsreihenfolge ist die eigene jedes SDK. Jedes SDK sucht eine Lizenz in derselben Rangfolge — eine explizite Lizenzzeichenkette, dann ein expliziter Dateipfad, dann die Umgebungsvariable KEYRIGHT_LICENSE, dann ein Config-Pfad, dann das OS-app-data-Standardverzeichnis, dann der gecachte Lease —, aber es durchläuft diese Liste in seinem Prozess, gegen seine konfigurierten Pfade. Das ist eine Funktion: Ein containergehosteter JVM-Dienst kann seine Lizenz per KEYRIGHT_LICENSE aus seinem Geheimnisspeicher gefüttert bekommen, während die C#-Desktop-App auf dem Laptop desselben Kunden eine interaktive Aktivierung und ihren gecachten Lease verwendet, und beide verifizieren das identische signierte Dokument.
Den Schlüssel über die ganze Linie rotieren
Weil hinter dem ganzen Produkt ein Schlüsselpaar steht, ist eine Signaturschlüsselrotation eine einzige koordinierte Veröffentlichung statt vier unabhängiger Durcheinander. Jedes SDK stellt dieselbe Notausstieg bereit: Neben dem neuen öffentlichen Schlüssel listen Sie den ausscheidenden, und Lizenzen und Leases, die von einem von beiden signiert sind, validieren während des Übergangs weiter.
// .NET
AdditionalPublicKeysBase64 = { "<previous public key>" },
// Node
additionalPublicKeysBase64: ['<previous public key>'],
# Python
additional_public_keys_base64=["<previous public key>"],
// Java (public field, no fluent setter)
opts.additionalPublicKeysBase64.add("<previous public key>");
Liefern Sie jede Komponente mit dem neuen Schlüssel als primär und dem alten in der Zusatzliste, lassen Sie das Feld leerlaufen, während die alten Leases ablaufen, und entfernen Sie dann den alten Schlüssel in einer späteren Veröffentlichung. Der Beitrag Ihre Signaturschlüssel rotieren behandelt das Timing; die einzige polyglotte Falte ist, dass „eine Veröffentlichung ausliefern” jetzt vier Builds bedeutet, die dieselben zwei Schlüssel tragen.
Das Fazit
Eine Produktlinie in vier Sprachen braucht keine vier Lizenzsysteme — sie braucht ein signiertes Format und vier Verifizierer davon, was genau das ist, was die Keyright-SDKs sind. Das Signaturmodell ist geteilt (ein privater Schlüssel signiert, ein öffentlicher Schlüssel verifiziert, überall), der Maschinen-Fingerabdruck ist geteilt (byteweise kompatibel, also deckt ein Platz jede Komponente auf einer Maschine ab), und die Berechtigungssemantik ist geteilt (dieselben Flags und Limits, fail-closed aus demselben Claim gelesen). Was lokal ist — der Lease-Cache und der Auflösungspfad — ist absichtlich lokal, und die Umgebungsvariable KEYRIGHT_LICENSE kollabiert es zu einer einzigen Aktivierung, wenn Sie das wollen. Der Kunde kauft eine Lizenz; C#, JavaScript, Python und die JVM ehren sie alle; und Sie pflegen einen Mandanten statt vier.
Siehe die Anleitungen pro Sprache für die vollständige Client-Oberfläche — .NET, Node.js, Python und Java — und containerisierte Dienste lizenzieren für die Komponente der Suite, die horizontal skaliert.
Nebula.NET testen
Härten Sie Ihren .NET-Code in wenigen Minuten — starten Sie mit der kostenlosen Edition.