PEM (Privacy-Enhanced Mail) представляет собой текстовый контейнер для криптографических ключей и сертификатов, закодированных в Base64 и обёрнутых в специальные маркеры вида:
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
или
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
В экосистеме JavaScript работа с PEM часто связана с библиотекой Jsrsasign, которая предоставляет инструменты для парсинга ASN.1 структур, извлечения криптографических параметров и преобразования их в современные форматы, включая JWK (JSON Web Key).
PEM является транспортным представлением ключа, тогда как JWK — это JSON-структура, предназначенная для веб-экосистемы и спецификации JOSE (JSON Object Signing and Encryption).
JWK для RSA-ключа может выглядеть так:
Пример структуры:
{
"kty": "RSA",
"n": "...",
"e": "...",
"d": "...",
"p": "...",
"q": "...",
"dp": "...",
"dq": "...",
"qi": "..."
}
PEM содержит ASN.1 DER-структуру, из которой эти значения извлекаются.
В Jsrsasign ключевая точка входа для преобразований — объект KEYUTIL.
Он выполняет следующие операции:
Основная функция загрузки PEM:
const key = KEYUTIL.getKey(pem);
На этом этапе происходит определение типа ключа:
После получения объекта ключа выполняется преобразование:
const jwk = KEYUTIL.getJWK(key);
Этот метод анализирует объект ключа и формирует JSON Web Key согласно спецификации RFC 7517.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr...
-----END PUBLIC KEY-----
import { KEYUTIL } from "jsrsasign";
const pem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr...
-----END PUBLIC KEY-----`;
const key = KEYUTIL.getKey(pem);
const jwk = KEYUTIL.getJWK(key);
console.log(jwk);
PEM очищается от заголовков и переводится из Base64 в бинарный DER-формат.
Jsrsasign использует ASN.1 парсер для извлечения структуры:
Из ASN.1 создаётся объект:
JWK требует кодирование в base64url, а не стандартный Base64:
+ и /=При обработке PEM приватного RSA ключа структура JWK расширяется:
const key = KEYUTIL.getKey(privatePem);
const jwk = KEYUTIL.getJWK(key);
Дополнительно появляются поля:
Эти параметры позволяют ускорить операции подписи и расшифровки.
Для эллиптических кривых структура отличается:
Пример:
const jwk = KEYUTIL.getJWK(KEYUTIL.getKey(ecPem));
Jsrsasign автоматически определяет тип PEM:
Это важно, потому что от формата зависит способ извлечения параметров.
Иногда используется явное разделение этапов:
const keyObj = KEYUTIL.getKey(pem);
Далее возможны операции:
После конвертации важно учитывать:
Jsrsasign не всегда выполняет строгую валидацию RFC, поэтому результат может требовать дополнительной проверки.
Отсутствие заголовков приводит к ошибке парсинга ASN.1.
Некоторые нестандартные форматы PKCS могут не распознаваться.
Даже один лишний символ делает DER-структуру нечитаемой.
После конвертации JWK может использоваться для:
Пример использования RSA JWK:
const header = { alg: "RS256", typ: "JWT" };
const sHeader = JSON.stringify(header);
const sPayload = JSON.stringify({ sub: "1234567890" });
const key = KEYUTIL.getKey(pem);
const jwk = KEYUTIL.getJWK(key);
const jwt = KJUR.jws.JWS.sign(null, sHeader, sPayload, jwk);
Jsrsasign автоматически преобразует значения:
Это критично, так как стандарт JWK требует URL-safe представление без padding.
Jsrsasign поддерживает двунаправленные преобразования:
Обратная операция:
const key = KEYUTIL.getKey(jwk);
const pem = KEYUTIL.getPEM(key);
Если PEM содержит сертификат:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Jsrsasign сначала извлекает публичный ключ из X.509 структуры, затем преобразует его в JWK.
Ключевая роль ASN.1 в процессе:
Без корректного ASN.1 парсинга преобразование PEM → JWK невозможно.
При массовой конвертации рекомендуется:
JWK, полученный через Jsrsasign, может использоваться как промежуточный формат для:
Однако может потребоваться адаптация структуры под строгий Web Crypto API формат.