Skip to content

Integración del SDK

Integración del SDK de .NET

Un recorrido completo, de cero a licenciado: crea un producto en Keyright, embebe tu clave pública con Keyright.NET, restringe características sin conexión, activa en línea y emite claves a los clientes — fallando cerrado por diseño.

Este es el viaje completo — desde un espacio de trabajo de Keyright vacío hasta una aplicación .NET lista para distribuir que desbloquea características de pago contra una clave de licencia real. Cubre ambos lados: la configuración del proveedor que haces una vez en el panel de Keyright, y el código de cliente que embebes con el SDK Keyright.NET. Cada bloque de código usa la superficie real del SDK; cópialos tal cual.

Si solo quieres la ruta condensada del proveedor, consulta Primeros pasos. Esta página es la versión de extremo a extremo para el desarrollador.

Qué construirás

Emites claves de licencia del lado del servidor en Keyright — cada tenant (tu espacio de trabajo) las firma con su propia clave privada RSA que nunca sale del servidor. Tu aplicación embebe solo la clave pública correspondiente y verifica licencias sin conexión contra ella, de modo que una comprobación de licencia no necesita red. Para la aplicación de puestos y la revocación también activas en línea: la aplicación intercambia una clave de licencia por un lease firmado de vida corta vinculado a la máquina, y luego sigue funcionando sin conexión hasta que ese lease caduca. Cualquier cosa que no cuadre — firma errónea, producto equivocado, caducidad, revocación, un reloj retrasado — se resuelve a la edición Free en lugar de lanzar una excepción. Falla cerrado.

Paso 1 — Crea tu producto en Keyright

Inicia sesión en el panel en /dashboard (p. ej. https://keyright.delta1labs.com/dashboard) y abre Products → Add product. Dale un nombre y un slug — el slug es el identificador que el SDK de tu aplicación envía como Product, así que elige algo estable y en minúsculas como acme-app. Apúntalo; lo pegarás en el SDK en el Paso 5.

Tu espacio de trabajo ya tiene una clave de firma RSA, generada cuando se creó el tenant. Cada licencia y lease de cada producto en tu espacio de trabajo se firma con la mitad privada de esa clave, que se almacena cifrada en el servidor y nunca sale de él. Tú solo manejas la mitad pública.

Lo mismo por la API:

curl -X POST $BASE/admin/products -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
  -d '{"name":"Acme App","slug":"acme-app"}'

($BASE es la URL de tu servicio emisor, $TOKEN un token de administrador/sesión del tenant — consulta Primeros pasos.)

Paso 2 — Obtén tu clave pública

Abre la pestaña Integration del panel y copia la Signing public key — una cadena base64 SubjectPublicKeyInfo. O obtenla desde la API:

curl $BASE/admin/public-key -H "X-Admin-Token: $TOKEN"

Esta clave no es un secreto. Se distribuye dentro de tu binario compilado. Toda la seguridad proviene de que la clave privada permanezca en el servidor: la clave pública solo puede verificar firmas, nunca acuñarlas. Embeberla, decompilar tu aplicación para leerla, incluso publicarla — nada de eso permite a nadie falsificar una licencia.

Paso 3 — Define tus planes y derechos

Un producto tiene niveles (p. ej. pro, enterprise). Cada nivel lleva un número de puestos y una plantilla de derechos — las banderas nombradas y los límites numéricos que se incorporan a cada licencia de ese nivel. Los derechos son cómo tu aplicación pregunta “¿está permitida esta característica?” en tiempo de ejecución.

Añade un nivel desde la página del producto en el panel (Add tier), o por la API:

curl -X POST $BASE/admin/products/acme-app/tiers -H "X-Admin-Token: $TOKEN" -H "content-type: application/json" \
  -d '{"name":"pro","seats":3,"entitlements":{"export":"true","max-projects":"10"}}'

Los valores de derechos son cadenas en el protocolo: "true"/"false" para banderas, un entero o el literal "unlimited" para límites. Una cadena de nivel se corresponde con una edición que tu aplicación puede activar — enterprise → Enterprise, cualquier otra → Licensed, y sin licencia alguna → Free.

Paso 4 — Instala el SDK

Keyright.NET está en NuGet. Tiene como objetivos múltiples netstandard2.0 y net8.0, así que se ejecuta en .NET Framework 4.8 y .NET 6–10.

dotnet add package Keyright.NET

Los SDK hermanos verifican exactamente los mismos formatos de licencia y lease para otros runtimes — Node.js, Python y Java — de modo que una línea de productos multilenguaje puede compartir un tenant de Keyright y una clave pública. ¿No hay SDK para tu lenguaje? Llama a la API HTTP directamente. Esta página cubre el cliente de .NET.

Paso 5 — Inicializa el cliente

Construye un KeyrightClient en el arranque con el slug del producto del Paso 1, la clave pública del Paso 2 y (para la activación en línea) la URL de tu servicio. Usa la fábrica estática Initialize — valida las opciones y carga la clave por adelantado:

using Keyright.Client;

static readonly KeyrightClient License = KeyrightClient.Initialize(new KeyrightOptions
{
    Product         = "acme-app",                          // must match the product slug you issue keys for
    PublicKeyBase64 = "MIIBIjANBgkq...",                   // the base64 public key from Step 2
    ServiceUrl      = "https://licensing.acme.example",    // YOUR Keyright issuing service — see note below; omit if offline-only

    // Optional: keep an old key valid during a rotation window (Step 9)
    // AdditionalPublicKeysBase64 = { "<previous public key>" },
});

ServiceUrl es tu servicio emisor de Keyright, no el nuestro — ponlo a la URL base de la API de tu tenant gestionado o, si ejecutas Keyright autoalojado, la URL base de tu propia instancia (p. ej. https://licensing.acme.example). El mismo SDK funciona contra cualquiera de los dos; nada está codificado a un endpoint de Delta1. Solo Product y PublicKeyBase64 son obligatorios. El SDK busca una licencia en varias fuentes, en orden de precedencia (de mayor a menor): un LicenseString explícito, luego un LicenseFilePath explícito, luego la variable de entorno KEYRIGHT_LICENSE (una ruta a un archivo de licencia), luego ConfigLicensePath, luego la ruta de datos de aplicación del SO ({LocalApplicationData}/Keyright/{product}/license.json), y finalmente el lease de activación en caché. Normalmente no defines ninguna de estas — la activación (Paso 7) escribe la caché del lease por ti.

Paso 6 — Restringe características sin conexión

Llama a Validate(). Resuelve la mejor licencia de las fuentes de arriba, verifica la firma RSA, la coincidencia de producto, el bloqueo por equipo, la caducidad, la lista de revocación opcional distribuida y el estado de prueba/manipulación-de-reloj — todo sin conexión, y nunca lanza una excepción. Ante cualquier fallo devuelve un LicenseInfo en la edición Free que lleva el motivo.

LicenseInfo info = License.Validate();

if (info.IsPaid)                       // true for any edition above Free
{
    // unlock paid features
}

if (info.Edition == Edition.Enterprise)
{
    // unlock enterprise-only features
}

Restringe características individuales según los derechos en lugar de la edición, de modo que cambiar la plantilla de un nivel no implique distribuir código nuevo:

// Boolean flag
if (License.IsEnabled("export"))
{
    ShowExportCommand();
}

// Numeric limit — pass the fail-closed fallback yourself
long maxProjects = License.GetLimit("max-projects", fallback: 1);
if (currentProjectCount >= maxProjects)
{
    PromptToUpgrade();
}

IsEnabled y GetLimit validan cada uno en el acto y fallan cerrado: una bandera ausente se lee como deshabilitada, un límite ausente o no analizable devuelve tu fallback. Si compruebas varios derechos a la vez, valida una sola vez y reutiliza el resultado para evitar trabajo repetido:

LicenseInfo info = License.Validate();
bool canExport  = info.Entitlements.IsEnabled("export");
long maxSeats   = info.Entitlements.GetLimit("max-seats", 1);

Para averiguar por qué falló una comprobación (para un diálogo de “Register” o diagnóstico), lee info.Status y el info.Message orientado a personas, y Validate(out LicenseSourceKind source) te dice qué fuente ganó.

Paso 7 — Activa en línea con una clave de licencia

Cuando un cliente introduce una clave, llama a ActivateAsync. Hace POST de la clave más un id de máquina estable a tu servicio, que consume un puesto y devuelve un lease firmado de vida corta vinculado a esa máquina. El SDK verifica el lease contra tu clave pública embebida y lo almacena en caché localmente, de modo que cada Validate() posterior tiene éxito sin red hasta que transcurra la ventana de gracia del lease.

ActivateAsync no lanza excepción para las rutas de fallo ordinarias (clave errónea, límite de puestos, sin conexión, revocada) — devuelve un LicenseInfo que falla cerrado igual que Validate(). Inspecciona el resultado:

LicenseInfo info = await License.ActivateAsync(customerEnteredKey, ct);

if (info.IsValid && info.IsPaid)
{
    // Activated. The lease is cached; the app now works offline until it expires.
    ShowLicensedUi(info.StatusBadge);           // e.g. "Enterprise" or "Enterprise Trial"
}
else
{
    // Surface info.Message; the app stays in Free mode.
    ShowActivationError(info.Message);          // "All seats for this license are in use.", etc.
}
  • Los puestos se aplican del lado del servidor. Activar más máquinas de las que la licencia permite devuelve un resultado de límite de puestos y ningún lease. Reactivar una máquina que ya está vinculada es idempotente — no se consume un puesto extra.
  • Gracia sin conexión. Si el servicio es inalcanzable, ActivateAsync recurre a cualquier lease en caché que siga siendo válido, de modo que una breve caída no deja fuera al usuario. Cuando el lease se acerca a su caducidad, la aplicación debe volver a alcanzar el servidor para refrescarlo.
  • La revocación surte efecto en el siguiente refresco — consulta el Paso 9.

ActivateAsync solo lanza excepción por errores de programación: un ServiceUrl ausente o una clave vacía.

Máquinas aisladas (air-gapped). Para una máquina que nunca puede alcanzar el servicio, un operador firma un lease sin conexión para su id de máquina (panel Licenses → offline lease, o POST /admin/licenses/{id}/offline-lease) y entrega el JSON por archivo. Impórtalo — esto lanza excepción si el lease es inválido, así que manéjalo:

try
{
    LicenseInfo info = License.ImportOfflineLease(File.ReadAllText("acme.lease.json"));
}
catch (InvalidOperationException ex)
{
    // wrong signature / product / machine, or expired
}

Paso 8 — Emite claves de licencia a tus clientes

Puedes repartir claves de tres formas:

  1. Desde el panel. Licenses → Issue: elige el producto y nivel, define el titular, puestos, caducidad y email de contacto, y Keyright genera la clave. El diálogo de emisión también tiene un conmutador Trial para acuñar la clave como una prueba por tiempo limitado (consulta el Paso 9 y Pruebas gratuitas de autoservicio).
  2. Automáticamente desde tu proveedor de facturación. Conecta el fulfillment de tu tienda a POST /webhooks/fulfill/{slug} (firmado con HMAC). Un evento “paid” acuña una licencia y envía la clave por email; un “refund”/“cancel” la revoca — sin paso manual.
  3. Desde la API de administración. POST /admin/licenses (autogenera una clave salvo que pases una). Consulta la Referencia de la API HTTP para la forma completa de la petición.

Sea cual sea la forma en que emitas, la clave es lo que el cliente pega en tu aplicación para ActivateAsync en el Paso 7.

Paso 9 — Pruebas, bloqueo por equipo, revocación y rotación de claves

  • Pruebas. Activa las pruebas de autoservicio por producto y deja que los clientes reclamen una clave desde tu propio sitio; una clave de prueba se activa por la misma ruta ActivateAsync exacta que una de pago. Muestra el estado directamente desde LicenseInfo — info.IsTrial, info.StatusBadge (p. ej. “Enterprise Trial”), info.ExpiryUtc y info.DaysRemaining para una insignia de “27 days left”. La duración de una prueba se cuenta desde la primera activación, y se reemplaza sin fisuras cuando el cliente activa luego una clave de pago. Flujo completo: Pruebas gratuitas de autoservicio.
  • Bloqueo por equipo. El SDK deriva una huella digital de máquina estable, con una pequeña NodeLockTolerance (por defecto 1) para que una NIC o un disco cambiados no dejen fuera al usuario. Para controlar qué identifica a una máquina, implementa IMachineComponents y define MachineComponents en las opciones. La petición de activación envía este id de máquina para que los puestos se cuenten por dispositivo.
  • Revocación. Revoca una clave con POST /admin/licenses/{id}/revoke (o la acción por fila del panel). El cliente cae a Free (estado Revoked) en el siguiente refresco del lease. También puedes distribuir una lista de revocación firmada con tu compilación (RevocationListJson / RevocationListPath) para que una aplicación puramente sin conexión siga honrando las revocaciones.
  • Rotación de claves. Cuando rotas la clave de firma del tenant, distribuye una compilación con la nueva clave pública en PublicKeyBase64 y la saliente en AdditionalPublicKeysBase64 — las licencias y leases firmados por cualquiera de las dos siguen validándose durante la transición. Quita la clave antigua de la lista una vez que todo lease firmado por ella haya caducado.

Paso 10 — Prueba de extremo a extremo

  1. En el panel, habilita las pruebas para acme-app (una duración corta está bien), luego emítete una clave de prueba a ti mismo.
  2. Ejecuta tu aplicación. Antes de la activación, Validate() devuelve Free — tus características de pago siguen bloqueadas.
  3. Llama a ActivateAsync con la clave de prueba. Observa cómo la aplicación cambia al estado licenciado y la insignia cambia a p. ej. “Pro Trial”. El lease ahora está en caché en disco.
  4. Prueba sin conexión. Desconecta la red y reinicia la aplicación. Validate() sigue devolviendo el LicenseInfo licenciado desde el lease en caché — sin ida y vuelta al servidor — hasta que termina la ventana de gracia del lease.
  5. Prueba la revocación. Revoca la licencia, reconecta y deja que el lease se refresque; la aplicación debería volver a Free.

Solución de problemas

  • La activación dice que la clave no se reconoció. La causa más común es una discrepancia de producto: la clave se emitió para un producto (o un tenant) distinto del slug Product con el que tu cliente está configurado. El servidor acota la activación por producto, así que una clave válida para acme-app no activará un cliente inicializado con Product = "other-app". Confirma que el slug del Paso 5 coincide con el producto bajo el que emitiste la clave.
  • Todo se lee como Free. Es el diseño — el SDK falla cerrado ante cualquier problema de verificación en lugar de lanzar una excepción. Lee info.Status (p. ej. NoLicense, SignatureInvalid, Expired, MachineMismatch, Revoked) e info.Message para ver cuál es. Un SignatureInvalid casi siempre significa que la clave pública embebida no coincide con el tenant que firmó la clave.
  • Una licencia por tiempo limitado de repente no valida. Si el reloj del sistema se mueve hacia atrás más allá de ClockTamperToleranceHours (por defecto 24h) en una prueba o suscripción, el SDK lo trata como manipulación del reloj y devuelve el estado ClockTampered. No se queda pegado — ajusta la hora correcta y la validación se recupera en la siguiente llamada. Las licencias perpetuas nunca están sujetas a esta comprobación.

Véase también