Подпись CSR закрытым ключом

CSR (Certificate Signing Request) в формате PKCS

В библиотеке jsrsasign работа с CSR реализована через объект KJUR.asn1.csr.CSRUtil, а криптографические операции опираются на модуль KEYUTIL, RSAKey, а также хэш-функции и алгоритмы подписи, встроенные в библиотеку.


Структура CSR и роль подписи

CSR в PKCS#10 состоит из трёх ключевых компонентов:

  • информация о субъекте (subject DN)
  • публичный ключ
  • цифровая подпись, сформированная приватным ключом

Подпись выполняет две функции:

  • подтверждает владение приватным ключом
  • защищает целостность CSR (любое изменение данных делает подпись недействительной)

Генерация ключевой пары

Перед созданием CSR необходимо сформировать асимметрическую пару ключей.

const { KEYUTIL } = KJUR;

// генерация RSA ключа 2048 бит
const kp = KEYUTIL.generateKeypair("RSA", 2048);

const privateKey = kp.prvKeyObj;
const publicKey = kp.pubKeyObj;

В реальных сценариях чаще используется импорт заранее созданного приватного ключа:

const privateKey = KEYUTIL.getKey(pemPrivateKey);

Формирование CSR через Jsrsasign

Основной способ создания CSR — использование KJUR.asn1.csr.CSRUtil.newCSRPEM.

const csrPem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
    subject: {
        str: "/C=RU/ST=Region/L=City/O=Company/OU=IT/CN=example.com"
    },
    sbjpubkey: publicKey,
    sigalg: "SHA256withRSA",
    sbjprvkey: privateKey
});

Ключевые параметры:

  • subject.str — Distinguished Name в формате X.500
  • sbjpubkey — публичный ключ
  • sbjprvkey — приватный ключ для подписи CSR
  • sigalg — алгоритм подписи

Алгоритмы подписи CSR

Jsrsasign поддерживает широкий набор алгоритмов:

  • SHA256withRSA
  • SHA384withRSA
  • SHA512withRSA
  • SHA256withECDSA
  • SHA384withECDSA

Наиболее распространённый вариант:

sigalg: "SHA256withRSA"

ECDSA используется для эллиптических ключей:

sigalg: "SHA256withECDSA"

Подпись CSR вручную через KJUR

Для более низкоуровневого контроля CSR можно собрать вручную через PKCS10:

const csr = new KJUR.asn1.csr.PKCS10({
    subject: { str: "/CN=example.com" },
    sbjpubkey: publicKey,
    sigalg: "SHA256withRSA",
    sbjprvkey: privateKey
});

csr.sign();
const pem = csr.getPEM();

Метод sign() выполняет криптографическую операцию:

  • хэширование tbsCertificateRequest
  • применение алгоритма подписи
  • упаковку результата в ASN.1 структуру

Проверка CSR после подписи

Jsrsasign позволяет проверить корректность CSR:

const isValid = KJUR.asn1.csr.CSRUtil.verifyCSRPEM(csrPem);

Логика проверки:

  • извлекается публичный ключ из CSR
  • вычисляется подпись
  • сверяется с данными TBS (to-be-signed)

Добавление SAN (Subject Alternative Name)

В современных TLS-сценариях CN недостаточно, поэтому используется расширение SAN.

const csrPem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
    subject: {
        str: "/CN=example.com"
    },
    extensions: [
        {
            extname: "subjectAltName",
            array: [
                { dns: "example.com" },
                { dns: "www.example.com" },
                { ip: "192.168.1.1" }
            ]
        }
    ],
    sbjpubkey: publicKey,
    sbjprvkey: privateKey,
    sigalg: "SHA256withRSA"
});

SAN является критическим расширением, так как большинство CA игнорируют CN при проверке домена.


Разбор CSR после создания

CSR можно декодировать для проверки структуры:

const info = KJUR.asn1.csr.CSRUtil.getInfo(csrPem);
console.log(info);

Результат включает:

  • subject
  • public key type
  • extensions
  • signature algorithm

Внутренний процесс подписи CSR

Подпись CSR в Jsrsasign происходит в несколько этапов:

  1. Формируется CertificationRequestInfo
  2. Кодируется в DER (ASN.1)
  3. Вычисляется хэш (SHA-256 или другой алгоритм)
  4. Хэш подписывается приватным ключом (RSA/ECDSA)
  5. Подпись добавляется в CSR структура

Для RSA используется схема PKCS#1 v1.5 или PSS (в зависимости от конфигурации).


Работа с PEM форматом

Jsrsasign полностью работает с PEM-строками:

-----BEGIN CERTIFICATE REQUEST-----
...
-----END CERTIFICATE REQUEST-----

Конвертация:

const pem = csr.getPEM();
const parsed = KJUR.asn1.csr.CSRUtil.getInfo(pem);

Типичные ошибки при подписи CSR

1. Несоответствие ключей

Ошибка возникает, если публичный и приватный ключ не образуют пару.


2. Неподдерживаемый алгоритм

Некорректный sigalg приводит к исключению при подписании.


3. Повреждённый PEM

Любое изменение строки CSR делает подпись недействительной.


4. Отсутствие SAN

Некоторые CA отклоняют CSR без расширения SAN даже при корректной подписи.


Сравнение с OpenSSL CSR

Jsrsasign по сути повторяет поведение OpenSSL:

OpenSSL:

openssl req -new -key private.key -out request.csr

Jsrsasign:

CSRUtil.newCSRPEM({...})

Разница заключается в том, что Jsrsasign позволяет формировать CSR полностью в JavaScript без внешних бинарных зависимостей.


Использование ECDSA при подписи CSR

ECDSA CSR требует правильного формата ключа:

const kp = KEYUTIL.generateKeypair("EC", "secp256r1");

const csrPem = KJUR.asn1.csr.CSRUtil.newCSRPEM({
    subject: { str: "/CN=example.com" },
    sbjpubkey: kp.pubKeyObj,
    sbjprvkey: kp.prvKeyObj,
    sigalg: "SHA256withECDSA"
});

ECDSA обеспечивает меньший размер ключа при сопоставимом уровне безопасности RSA 3072+.


Интеграция CSR с TLS-инфраструктурой

Сформированный CSR используется в сценариях:

  • получение сертификата от CA
  • автоматизация ACME (Let’s Encrypt)
  • генерация клиентских сертификатов (mTLS)
  • подписка внутренних PKI систем

Jsrsasign позволяет формировать CSR прямо в браузере или Node.js без обращения к OpenSSL.


Работа с цепочкой доверия после CSR

После подписания CSR и выпуска сертификата CA возвращает X.509 сертификат, который должен соответствовать:

  • публичному ключу CSR
  • subject DN
  • расширениям SAN

Несоответствие любого параметра делает сертификат недействительным для клиента TLS.