Самоподписанный сертификат всегда начинается с асимметрической криптографии. В Jsrsasign ключевая пара создаётся на основе RSA или ECDSA, где приватный ключ используется для подписи, а публичный — для проверки.
В большинстве практических сценариев применяется RSA:
const kp = KEYUTIL.generateKeypair("RSA", 2048);
const privateKey = kp.prvKeyObj;
const publicKey = kp.pubKeyObj;
Ключевой момент: приватный ключ должен оставаться строго на стороне генерации. Публичный может быть встроен в сертификат и распространяться без ограничений.
Самоподписанный сертификат отличается тем, что субъект и издатель совпадают. Это ключевая особенность всей конструкции.
В Jsrsasign работа ведётся через объект
KJUR.asn1.x509.Certificate.
const cert = new KJUR.asn1.x509.Certificate({
version: 3
});
Далее задаются основные поля, формирующие структуру X.509.
В самоподписанном сертификате оба поля идентичны:
const dn = "/C=RU/O=Example Org/OU=IT Department/CN=example.local";
cert.setSubject({ str: dn });
cert.setIssuer({ str: dn });
Структура Distinguished Name должна быть согласована с требованиями X.509. Обычно используются следующие атрибуты:
Публичный ключ внедряется в сертификат как основа для дальнейшей верификации:
cert.setPublicKey(publicKey);
На этом этапе сертификат уже содержит криптографическую идентичность, но ещё не является доверенным, так как отсутствует подпись.
Каждый сертификат обязан иметь ограниченный срок действия. Jsrsasign требует явного указания даты начала и окончания:
cert.setNotBefore("240101000000Z");
cert.setNotAfter("250101000000Z");
Формат времени соответствует ASN.1 UTCTime или GeneralizedTime в зависимости от диапазона.
Критически важно: истечение срока делает сертификат недействительным независимо от остальных параметров.
Серийный номер должен быть уникальным для каждого сертификата:
cert.setSerialNumber({ hex: "01a3b5c7d9e2f1" });
В реальных системах серийный номер часто генерируется случайным образом или на основе времени и хэша.
Расширения определяют поведение сертификата в инфраструктуре.
Определяет, может ли сертификат выступать как удостоверяющий центр:
cert.addExtension(new KJUR.asn1.x509.BasicConstraints({
cA: false
}));
Для самоподписанного пользовательского сертификата обычно
устанавливается cA: false.
Определяет допустимые криптографические операции:
cert.addExtension(new KJUR.asn1.x509.KeyUsage({
bin: "101000"
}));
Типичные флаги:
Используется для дополнительных доменов:
cert.addExtension(new KJUR.asn1.x509.SubjectAltName({
critical: false,
array: [{ dns: "example.local" }]
}));
Ключевой этап, превращающий структуру в полноценный сертификат — цифровая подпись.
cert.sign(privateKey, "SHA256withRSA");
Алгоритм подписи должен соответствовать типу ключа. Для RSA чаще всего используется SHA-256.
После подписи объект становится неизменяемым с точки зрения криптографической целостности.
Финальная форма сертификата обычно требуется в PEM-формате:
const pem = cert.getPEM();
PEM представляет собой Base64-кодированный DER с заголовками:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
Процесс можно рассматривать как последовательность строго определённых этапов:
const kp = KEYUTIL.generateKeypair("RSA", 2048);
const cert = new KJUR.asn1.x509.Certificate({ version: 3 });
const dn = "/C=RU/O=Example Org/OU=IT/CN=example.local";
cert.setSubject({ str: dn });
cert.setIssuer({ str: dn });
cert.setPublicKey(kp.pubKeyObj);
cert.setNotBefore("240101000000Z");
cert.setNotAfter("250101000000Z");
cert.setSerialNumber({ hex: "01a3b5c7d9e2f1" });
cert.addExtension(new KJUR.asn1.x509.BasicConstraints({ cA: false }));
cert.addExtension(new KJUR.asn1.x509.KeyUsage({ bin: "101000" }));
cert.addExtension(new KJUR.asn1.x509.SubjectAltName({
array: [{ dns: "example.local" }]
}));
cert.sign(kp.prvKeyObj, "SHA256withRSA");
const pemCert = cert.getPEM();
const pemPrivateKey = KEYUTIL.getPEM(kp.prvKeyObj, "PKCS8PRV");
При вызове sign() библиотека выполняет несколько
критических операций:
Эти шаги обеспечивают целостность и проверяемость сертификата.
Самоподписанный сертификат не требует внешнего удостоверяющего центра, но это накладывает ограничения:
При этом он полностью корректен с точки зрения X.509 и широко используется в:
Jsrsasign поддерживает несколько форматов представления ключей и сертификатов.
Человеко-читаемый формат, основной для обмена:
Бинарное представление ASN.1 структуры:
const der = cert.getEncodedHex();
DER используется в системных API и низкоуровневых протоколах.
Ошибки, возникающие при создании самоподписанного сертификата, чаще всего связаны с:
Каждая из этих ошибок приводит либо к невозможности подписи, либо к невалидному сертификату.
Уровень безопасности напрямую зависит от:
Jsrsasign опирается на JavaScript-криптобэкенд, поэтому качество случайности критично для всей цепочки генерации.