Асимметричные ключи (RSA и ECDSA)
Когда сторона, проверяющая токен, не должна иметь возможности его подделать, используйте
асимметричную подпись: приватный ключ подписывает, а свободно распространяемый публичный ключ
проверяет. Это руководство охватывает RSA (RS*) и ECDSA (ES*), а также один дополнительный
шаг, который они требуют, — kid.
kid обязателен
В отличие от HMAC, decode() выбирает асимметричный проверяющий ключ по kid (идентификатору
ключа) из заголовка. Поэтому при подписании вы должны передать PrivateKey значение kid, а при
проверке — указать список с ключом по этому же идентификатору.
Нет kid — нет проверки
Для любого токена RS*/ES* отсутствие kid в заголовке вызывает ошибку “Token header is
missing ‘kid’”, а kid без соответствующего ключа вызывает “Key with ‘kid’=… was not
found”. Всегда указывайте третий аргумент PrivateKey для асимметричной подписи.
RSA: подпись и проверка
Сгенерируйте пару (см. «Установка и требования»), затем:
<?php
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\JwtPayload;
use Flytachi\Jwt\Entity\PrivateKey;
$privateKey = openssl_pkey_get_private(file_get_contents('private.pem'));
$token = JWT::encode(
new JwtPayload(['sub' => 'user-42', 'exp' => time() + 3600]),
new PrivateKey($privateKey, 'RS256', 'rsa-key-1') // 3-й аргумент = kid
);Проверьте публичным ключом, указав тот же kid:
<?php
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\PublicKey;
$publicKey = openssl_pkey_get_public(file_get_contents('public.pem'));
$payload = JWT::decode($token, [
'rsa-key-1' => new PublicKey($publicKey, 'RS256'),
]);
echo $payload->getClaim('sub'); // user-42ECDSA: меньшие ключи, тот же процесс
ECDSA даёт вам гораздо более короткие ключи и подписи при эквивалентной безопасности. Код идентичен, за исключением алгоритма и кривой:
use Flytachi\Jwt\JWT;
use Flytachi\Jwt\Entity\JwtPayload;
use Flytachi\Jwt\Entity\PrivateKey;
use Flytachi\Jwt\Entity\PublicKey;
$privateKey = openssl_pkey_get_private(file_get_contents('ec-private.pem'));
$token = JWT::encode(
new JwtPayload(['sub' => 'user-42', 'exp' => time() + 3600]),
new PrivateKey($privateKey, 'ES256', 'ec-key-1')
);
$publicKey = openssl_pkey_get_public(file_get_contents('ec-public.pem'));
$payload = JWT::decode($token, [
'ec-key-1' => new PublicKey($publicKey, 'ES256'),
]);Кривые соответствуют алгоритмам
ES256 требует ключ P-256, ES384 — ключ P-384, а ES512 — ключ P-521. Библиотека
автоматически преобразует сырую подпись ECDSA в ASN.1 DER для OpenSSL — см.
«Модель безопасности». Генерируйте кривые, как показано в
«Установка и требования».
Публикуйте свои публичные ключи как JWKS
Поскольку публичный ключ безопасно публиковать, распространённый подход — предоставить его как JSON Web Key Set, который получают проверяющие стороны. Вы можете собрать одну запись из разобранного ключа или просто опубликовать стандартные поля, которые уже есть в ваших ключах:
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"kid": "rsa-key-1",
"use": "sig",
"n": "0vx7agoebGcQ...",
"e": "AQAB"
}
]
}Потребители затем проверяют токены, ни разу не касаясь вашего приватного ключа — см. «Проверка через JWKS».
Выбор между RSA и ECDSA
RSA (RS*) |
ECDSA (ES*) |
|
|---|---|---|
| Размер ключа (сопоставимая безопасность) | 2048–4096 бит | 256–521 бит |
| Размер подписи | большой | малый |
| Скорость подписи | медленнее | быстрее |
| Скорость проверки | быстрая | быстрая |
| Распространённость | универсальна | очень широкая |
Выбирайте RSA для максимальной совместимости со старыми системами; выбирайте ECDSA для меньших токенов и более быстрого подписания. Оба варианта держат приватный ключ приватным.
Связанные материалы
- «Проверка через JWKS» — проверка сторонних токенов и ротация ключей
- «Алгоритмы» — полная матрица поддерживаемых алгоритмов
- «Справочник API» —
PrivateKey,PublicKey,JWK