JWK (JSON Web Key) представляет собой JSON-структуру, описывающую
криптографический ключ в стандартизированном виде. PEM (Privacy Enhanced
Mail) — текстовый формат, основанный на Base64-кодировании бинарного
DER-представления ключа и обёрнутый в заголовки вида
-----BEGIN PUBLIC KEY-----.
Ключевое различие между ними заключается в представлении данных:
Jsrsasign предоставляет инструменты для взаимного преобразования этих форматов без необходимости ручной работы с ASN.1 структурой.
RSA-ключ в формате JWK содержит набор параметров:
{
"kty": "RSA",
"n": "modulus_base64url",
"e": "exponent_base64url",
"d": "private_exponent",
"p": "prime1",
"q": "prime2",
"dp": "d mod (p-1)",
"dq": "d mod (q-1)",
"qi": "q^-1 mod p"
}
Для публичного ключа достаточно:
n — модульe — публичная экспонентаДля приватного ключа присутствует полный набор CRT-параметров.
EC-ключи используют другие поля:
{
"kty": "EC",
"crv": "P-256",
"x": "x_coordinate",
"y": "y_coordinate",
"d": "private_key"
}
PEM-кодирование — это Base64 DER-структуры с разделителями:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh...
-----END PUBLIC KEY-----
Для RSA приватного ключа:
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqh...
-----END PRIVATE KEY-----
Внутренне PEM всегда сводится к ASN.1 DER структуре.
В библиотеке Jsrsasign ключевую роль играет объект
KEYUTIL.
Он обеспечивает:
const jwk = {
kty: "RSA",
n: "base64url_modulus",
e: "AQAB"
};
const keyObj = KEYUTIL.getKey(jwk);
const pem = KEYUTIL.getPEM(keyObj);
console.log(pem);
Результатом будет PEM публичного ключа RSA.
Если JWK содержит приватные параметры:
const jwkPrivate = {
kty: "RSA",
n: "base64url_modulus",
e: "AQAB",
d: "base64url_private_exponent",
p: "prime1",
q: "prime2",
dp: "...",
dq: "...",
qi: "..."
};
const keyObj = KEYUTIL.getKey(jwkPrivate);
const pemPrivate = KEYUTIL.getPEM(keyObj, "PKCS8PRV");
console.log(pemPrivate);
Здесь используется формат PKCS#8 для приватного ключа.
Для эллиптических кривых:
const ecJwk = {
kty: "EC",
crv: "P-256",
x: "base64url_x",
y: "base64url_y"
};
const ecKey = KEYUTIL.getKey(ecJwk);
const ecPem = KEYUTIL.getPEM(ecKey);
console.log(ecPem);
Если присутствует d, ключ становится приватным.
Jsrsasign выполняет цепочку преобразований:
Ключевой этап — интерпретация JWK параметров в соответствии с типом
алгоритма (kty).
Хотя задача касается JWK → PEM, важно понимать симметрию:
const pem = `-----BEGIN PUBLIC KEY-----...`;
const keyObj = KEYUTIL.getKey(pem);
const jwk = KEYUTIL.getJWK(keyObj);
Это позволяет проверять корректность преобразования.
JWK использует base64url, отличающийся от стандартного Base64:
+ заменяется на -/ заменяется на _=Jsrsasign автоматически выполняет нормализацию, но при ручной обработке важно учитывать это различие.
1. Неполный JWK RSA ключ
Если отсутствуют p, q, d,
Jsrsasign не сможет сформировать приватный PEM.
2. Неверный kty
Допустимые значения:
RSAECoct (симметричные ключи, не конвертируются в PEM
напрямую)3. Несоответствие кривой EC
Поле crv должно быть строго поддерживаемым:
P-256P-384P-521const { KEYUTIL } = require("jsrsasign");
const jwk = require("./key.json");
const key = KEYUTIL.getKey(jwk);
const pem = KEYUTIL.getPEM(key);
При подключении через CDN:
<script src="https://cdn.jsdelivr.net/npm/jsrsasign/lib/jsrsasign-all-min.js"></script>
<script>
const key = KEYUTIL.getKey(jwk);
const pem = KEYUTIL.getPEM(key);
</script>
Иногда используется явное создание ключа:
const key = new RSAKey();
key.setPublic(jwk.n, jwk.e);
const pem = KEYUTIL.getPEM(key);
Такой подход полезен при частично заполненных JWK.
Jsrsasign поддерживает два основных формата приватных ключей:
KEYUTIL.getPEM(keyObj, "PKCS1PRV");
KEYUTIL.getPEM(keyObj, "PKCS8PRV");
PKCS#8 предпочтителен для современных систем.
После преобразования PEM можно проверить корректность:
const keyObj2 = KEYUTIL.getKey(pem);
Если ключ восстанавливается без ошибок, конвертация считается успешной.
При массовой конвертации JWK важно учитывать:
getKeyJsrsasign не выполняет строгую валидацию JWK по RFC на входе, поэтому ошибки часто проявляются на этапе ASN.1 генерации.
При конвертации JWK → PEM приватный ключ становится доступен в текстовом виде. Это требует:
В системах JWT часто требуется перевод JWK из JWKS endpoint в PEM:
const jwk = jwks.keys[0];
const key = KEYUTIL.getKey(jwk);
const pem = KEYUTIL.getPEM(key);
Это позволяет использовать PEM в библиотеках, ожидающих X.509 формат.