PEM и JWK представляют собой два принципиально разных способа представления криптографических ключей. PEM ориентирован на текстовое кодирование ключевых материалов в формате Base64 с обрамлением BEGIN/END блоками, тогда как JWK описывает ключ как структурированный JSON-объект с явным указанием параметров алгоритма, типа ключа и его компонентов.
В библиотеке Jose преобразование между этими форматами реализовано
через набор функций импорта и экспорта ключей, работающих поверх
стандарта WebCrypto API. Внутренне Jose оперирует объектами
CryptoKey, а преобразование PEM ↔︎ JWK является этапом
сериализации и десериализации ключевого материала.
PEM (Privacy-Enhanced Mail) используется для представления ключей в виде текстовых блоков:
Пример PEM публичного ключа:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----
Jose не работает напрямую со строками PEM в криптооперациях. PEM
сначала преобразуется в CryptoKey.
JWK (JSON Web Key) описывает ключ в виде JSON:
{
"kty": "RSA",
"n": "...",
"e": "AQAB",
"alg": "RS256",
"ext": true,
"key_ops": ["verify"]
}
Ключевые особенности JWK:
alg)В Jose используется importSPKI:
import { importSPKI } from 'jose'
const pemPublicKey = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh...
-----END PUBLIC KEY-----
`
const publicKey = await importSPKI(pemPublicKey, 'RS256')
После импорта ключ становится объектом CryptoKey,
пригодным для проверки JWT или других операций.
import { importPKCS8 } from 'jose'
const pemPrivateKey = `
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqh...
-----END PRIVATE KEY-----
`
const privateKey = await importPKCS8(pemPrivateKey, 'RS256')
После получения CryptoKey Jose позволяет получить
JWK-представление.
import { exportJWK } from 'jose'
const jwk = await exportJWK(publicKey)
console.log(jwk)
Результат будет содержать параметры, зависящие от типа ключа:
RSA:
{
"kty": "RSA",
"n": "...",
"e": "AQAB"
}
EC:
{
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
Если CryptoKey содержит приватную часть:
const jwkPrivate = await exportJWK(privateKey)
В этом случае JWK дополнительно включает параметр:
{
"d": "..."
}
import { importJWK } from 'jose'
const jwk = {
kty: 'RSA',
n: '...',
e: 'AQAB'
}
const key = await importJWK(jwk, 'RS256')
Если ключ содержит приватные параметры (d,
p, q и др.), он будет импортирован как
приватный.
Типовой сценарий включает два этапа:
import { importSPKI, importPKCS8 } from 'jose'
const publicKey = await importSPKI(pemPublic, 'RS256')
const privateKey = await importPKCS8(pemPrivate, 'RS256')
import { exportJWK } from 'jose'
const publicJwk = await exportJWK(publicKey)
const privateJwk = await exportJWK(privateKey)
Прямого экспорта в PEM в Jose нет, но преобразование выполняется через промежуточный слой.
import { importJWK } from 'jose'
const key = await importJWK(jwk, 'RS256')
Для PEM используется экспорт в SPKI/PKCS#8:
import { exportSPKI, exportPKCS8 } from 'jose'
const pemPublic = await exportSPKI(key)
const pemPrivate = await exportPKCS8(key)
import { exportSPKI } from 'jose'
const pem = await exportSPKI(publicKey)
import { exportPKCS8 } from 'jose'
const pem = await exportPKCS8(privateKey)
Jose строго связывает ключ с алгоритмом при импорте:
RS256 → RSA SHA-256ES256 → P-256 (ECDSA)EdDSA → Ed25519 / Ed448Ошибка в указании алгоритма приводит к невозможности импорта ключа.
RSA ключи в JWK содержат минимум два параметра:
n — модульe — экспонентаПриватные дополнения:
d, p, q, dp,
dq, qiПри экспорте из PEM в JWK Jose автоматически восстанавливает полную структуру, если ключ содержит приватную часть.
Для эллиптических кривых:
crv — кривая (P-256, P-384, P-521)x, y — координаты точкиd — приватный компонентПример:
{
kty: "EC",
crv: "P-256",
x: "...",
y: "...",
d: "..."
}
При переходе между PEM и JWK важно учитывать:
Возможные ограничения:
Invalid key format
Причина: ключ RSA импортируется с ES256.
Failed to import key
Причина: отсутствие BEGIN/END блоков или некорректный Base64.
Некоторые CryptoKey могут быть non-extractable:
extractable: false
В этом случае exportJWK завершится ошибкой.
Типовой поток в системах JWT:
CryptoKeyJWK часто используется внутри JWKS (JSON Web Key Set):
{
"keys": [
{
"kty": "RSA",
"n": "...",
"e": "AQAB",
"kid": "key-1"
}
]
}
Jose позволяет легко генерировать такие структуры через
exportJWK.
extractableПри преобразовании ключей в JWK рекомендуется добавлять
kid:
const jwk = await exportJWK(publicKey)
jwk.kid = '2026-rotation-key-1'
Это обеспечивает корректную ротацию ключей в системах аутентификации.