Generador de claves de licencia para .NET: por qué no deberías hacer el tuyo propio
Un sistema de licencias de .NET de verdad no es una cadena de clave aleatoria: es una licencia firmada que se verifica contra una clave pública incrustada, con caducidad, bloqueo por equipo, derechos y revocación. Aquí tienes por qué un generador de claves casero fracasa y cómo es un sistema de licencias firmadas en condiciones.
Si buscaste un “generador de claves de licencia para .NET”, la respuesta honesta es que no quieres un generador de claves: quieres un sistema de licencias firmadas, y las dos cosas no son lo mismo. Un generador produce una cadena de clave que tu app valida por su forma, lo que significa que la lógica que decide “esta clave es válida” es el mismo secreto que un atacante extrae para construir un keygen. Una licencia de verdad es un pequeño documento firmado: el servidor lo firma con una clave privada que nunca se distribuye, tu app verifica la firma contra una clave pública incrustada, y la falsificación se vuelve criptográficamente imposible. Este artículo explica por qué la vía casera fracasa y cómo es la versión correcta en .NET.
Qué entiende la gente por “generador de claves”, y por qué es el modelo mental equivocado
El diseño clásico es así: genera claves con un patrón reconocible (caracteres agrupados, un dígito de control, quizá un segmento derivado del nombre del cliente), entrega una a cada comprador y haz que la app acepte cualquier clave que coincida con el patrón. Parece seguridad porque las claves tienen aspecto oficial.
No lo es, y la razón es estructural. Si tu app puede decidir que una clave es válida inspeccionando la propia clave, entonces todo lo necesario para fabricar una clave válida está dentro de tu app. Un decompilador convierte tu rutina de validación en una rutina de generación: eso es literalmente lo que es un “keygen”. Como los ensamblados de .NET se decompilan limpiamente de nuevo a C# legible, esto no es un riesgo teórico; es un resultado de la primera tarde para cualquiera que se moleste en hacerlo.
E incluso si el formato fuera de algún modo imposible de adivinar, una clave basada en formato no tiene respuesta a las preguntas que de verdad hacen funcionar un negocio de software:
- ¿Cómo caduca? Una suscripción necesita una fecha de fin que la clave lleve y la app aplique.
- ¿Cómo se vincula a una máquina? Nada impide que una misma clave se pegue en mil instalaciones.
- ¿Cómo desbloqueas distintas características por plan? La clave es opaca; no puede decir “este cliente tiene exportación pero no la API”.
- ¿Cómo matas una clave filtrada o reembolsada? No puedes retirar una cadena que ya está circulando.
Un generador no produce nada de eso. Produce una cadena.
Qué es una licencia de verdad: un documento firmado
Reemplaza “generar una clave” por “firmar una licencia”. La unidad no es una cadena aleatoria: es una pequeña carga útil de hechos (titular de la licencia, producto, nivel, puestos, caducidad, derechos) con una firma criptográfica adjunta.
Aquí está la asimetría que hace que funcione. Tu servidor guarda una clave privada RSA y la usa para firmar la carga útil. Tu app solo incrusta la clave pública correspondiente. Una clave pública puede verificar que una firma coincide con una carga útil, pero nunca puede crear una. Así que:
- Decompila la app, lee la clave pública, publícala en una valla publicitaria: un atacante aún no puede falsificar una licencia, porque falsificar requiere la clave privada que nunca salió de tu servidor.
- Cambia un solo byte de la carga útil (sube el
tierdefreeapro) y la firma ya no coincide. La validación falla.
Este es el mecanismo exacto que hay detrás de TLS y de la firma de código, apuntado al licenciamiento. En .NET las primitivas vienen de serie (System.Security.Cryptography.RSA), y verificar una firma son unas pocas líneas. Que es justo la trampa: la comprobación de la firma es fácil, así que la gente da por hecho que todo el sistema es fácil, y no lo es.
Las partes que una firma por sí sola no te da
Una firma válida prueba que la licencia es genuina y está inalterada. No hace, por sí misma, nada de lo siguiente, y un sistema de verdad lo necesita todo:
Caducidad y defensa contra manipulación del reloj
Una suscripción o prueba lleva una caducidad que la app compara con la hora actual. Eso invita de inmediato al ataque obvio (atrasa el reloj y una prueba no termina nunca), así que la validación sin conexión tiene que recordar la última hora que vio legítimamente y tratar un gran salto hacia atrás como manipulación. El SDK de Keyright hace esto: atrasar el reloj más allá de una ventana ClockTamperToleranceHours (24 h por defecto) en una licencia con límite de tiempo produce un estado ClockTampered y rechaza hasta que se corrija la hora. Las licencias perpetuas no están sujetas a ello.
Bloqueo por equipo
Vincular una licencia a una máquina necesita una huella estable con tolerancia, para que un cliente que cambia una NIC o un disco no se quede bloqueado. Demasiado estricto y generas tickets de soporte; demasiado laxo y el bloqueo no significa nada. Keyright deriva la huella y aplica un NodeLockTolerance configurable (1 por defecto).
Derechos
Distribuir un único binario que desbloquea distintas capacidades por plan significa que la licencia tiene que llevar esas capacidades. Keyright incorpora una plantilla de derechos en cada nivel (indicadores con nombre, "export": "true", y límites numéricos, "max-projects": "10") y el SDK los lee localmente: IsEnabled("export"), GetLimit("max-projects", fallback: 1). Controla las características por derechos, no por una comprobación de nivel fija, y cambiar un plan no significa distribuir código nuevo.
Revocación
Una licencia filtrada, reembolsada o con cargo revertido tiene que morir, y una carga útil firmada emitida el año pasado no sabe nada de un reembolso de la semana pasada. En línea, Keyright revoca una clave del lado del servidor y el cliente baja a gratuita en su siguiente refresco de lease. Sin conexión, puede distribuir una lista de revocación firmada que tu app respeta sin llamada de red. Un generador de claves basado en formato no tiene historia de revocación alguna.
Cómo es el flujo correcto en .NET
Con Keyright el “generador” se reemplaza por un servidor que firma, y tu cliente verifica. Cada tenant obtiene su propio par de claves RSA aislado; la mitad privada se almacena cifrada con AES-256-GCM del lado del servidor y nunca se expone, así que tus firmas nunca comparten clave con nadie más. Tu app solo incrusta la mitad pública:
var client = KeyrightClient.Initialize(new KeyrightOptions {
Product = "acme-app",
PublicKeyBase64 = "MIIBIjANBgkq...", // not a secret — ships in your binary
ServiceUrl = "https://keyright.delta1labs.com",
});
// Offline: verify the signature, product, node-lock, expiry — no network, never throws.
LicenseInfo info = client.Validate();
if (info.IsPaid && client.IsEnabled("export")) { /* unlock the feature */ }
Validate() verifica la firma contra la clave pública incrustada, comprueba el bloqueo por equipo y la caducidad, respeta cualquier lista de revocación distribuida y falla en cerrado: cualquier problema se resuelve en la edición gratuita con un Status (SignatureInvalid, Expired, Revoked, MachineMismatch, …) en lugar de lanzar una excepción o, peor, desbloquear. Para la aplicación de puestos y la revocación inmediata añades una llamada en línea ActivateAsync(key), que intercambia la clave por un lease vinculado a la máquina.
Sé honesto sobre el límite
La firma mata la falsificación en seco: nadie keygenea una licencia que no puede firmar. Pero el código que lee el resultado todavía se ejecuta en la máquina del usuario, y la rama que desbloquea una característica puede ser parcheada por un atacante decidido con un decompilador. Eso no es un argumento en contra de la firma; es la razón por la que la combinas con dos cosas: activación en línea, para que los puestos y la revocación se apliquen en un servidor que el atacante no controla, y ofuscación, para que parchear la barrera del cliente sea caro. La firma derrota al falsificador; el resto derrota al parcheador.
Sáltate el generador, publica el sistema
La conclusión no es “no puedes escribir un verify de RSA”: puedes, en una tarde. Es que un sistema de licencias es firma más caducidad, bloqueo por equipo, derechos, revocación, puestos y un cliente que falla en cerrado, y un generador de claves aleatorias no te da nada de eso mientras aparenta que sí. Keyright es ese sistema, ya construido: firma RSA por tenant, verificación sin conexión, derechos y revocación, con un SDK que prioriza .NET. La guía de primeros pasos recorre el camino completo desde un espacio de trabajo vacío hasta una app con licencia lista para publicar, y el plan gratuito cubre el licenciamiento real antes de que pagues un céntimo.
Prueba Nebula.NET
Endurece tu código .NET en minutos — empieza con la edición gratuita.