Реализация генерации и валидации лицензионных ключей на сайте
Лицензионный ключ — это строка, которая кодирует права: какой продукт, какой план, дата истечения. Валидация должна работать как онлайн (API), так и оффлайн (криптографическая проверка подписи).
Форматы ключей
Простой случайный ключ: A3K7M-XQ2WP-N8VLR-5HZTB-J4YCS
Хранит только уникальность. Все данные о лицензии — в базе сервера. Онлайн-валидация обязательна.
Ключ с данными (Partial Key Verification): Часть ключа кодирует атрибуты лицензии. Позволяет частичную оффлайн-валидацию.
JWT-токен: eyJhbGciOiJSUzI1NiJ9... — полноценный токен с полезной нагрузкой и RSA-подписью.
Генерация JWT-лицензий
Асимметричная RSA-подпись позволяет клиентскому приложению проверить лицензию без обращения к серверу, используя только публичный ключ:
use Firebase\JWT\JWT;
class LicenseTokenService
{
public function issue(License $license): string
{
$privateKey = file_get_contents(storage_path('keys/license_private.pem'));
return JWT::encode([
'iss' => 'example.com',
'iat' => now()->timestamp,
'exp' => $license->expires_at?->timestamp ?? 9999999999,
'license_id' => $license->id,
'product' => $license->product_code,
'plan' => $license->plan,
'seats' => $license->max_seats,
'features' => $license->features,
], $privateKey, 'RS256');
}
public function verify(string $token): array
{
$publicKey = file_get_contents(storage_path('keys/license_public.pem'));
$payload = JWT::decode($token, new Key($publicKey, 'RS256'));
return (array) $payload;
}
}
Валидация в приложении (оффлайн)
// C# пример для desktop-приложения
public class LicenseChecker
{
private readonly string publicKey = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQEAr...
-----END PUBLIC KEY-----";
public LicenseResult Validate(string token)
{
var handler = new JwtSecurityTokenHandler();
var validation = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "example.com",
ValidateIssuerSigningKey = true,
IssuerSigningKey = GetPublicKey(),
ValidateLifetime = true,
};
try
{
var principal = handler.ValidateToken(token, validation, out _);
return new LicenseResult { IsValid = true, Plan = GetClaim(principal, "plan") };
}
catch (SecurityTokenExpiredException)
{
return new LicenseResult { IsValid = false, Error = "License expired" };
}
}
}
API для валидации
Route::post('/api/v1/licenses/validate', function (Request $request) {
$key = $request->input('key');
$license = License::where('key', $key)->first();
if (!$license) {
return response()->json(['valid' => false, 'error' => 'Invalid key'], 404);
}
$checks = [
'active' => $license->status === 'active',
'not_expired'=> !$license->expires_at || now()->isBefore($license->expires_at),
'seats_ok' => $license->activations()->where('revoked', false)->count() < $license->max_activations,
];
$valid = !in_array(false, $checks);
return response()->json([
'valid' => $valid,
'product' => $license->product_code,
'plan' => $license->plan,
'expires_at' => $license->expires_at,
'errors' => array_keys(array_filter($checks, fn($v) => !$v)),
]);
});
Сроки
Генерация и валидация лицензионных ключей с JWT и API: 4–6 рабочих дней.







