Подпись сертификата в PKI-системе — это процесс создания цифровой подписи для X.509 сертификата с использованием закрытого ключа удостоверяющего центра (CA). В контексте JavaScript-библиотеки Jsrsasign этот процесс реализуется через формирование структуры TBSCertificate (To Be Signed Certificate), вычисление криптографической подписи и упаковку результата в полноценный X.509 объект.
Любой X.509 сертификат до подписи состоит из двух ключевых частей:
TBSCertificate включает:
Именно TBSCertificate является объектом, который подписывается закрытым ключом CA.
В Jsrsasign закрытый ключ CA обычно загружается через
KEYUTIL.
const caPrivateKeyPEM = `
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----`;
const caPrivateKey = KEYUTIL.getKey(caPrivateKeyPEM);
А сертификат CA (Issuer) может использоваться для получения Subject Distinguished Name:
const caCertPEM = `
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----`;
const caCert = new X509();
caCert.readCertPEM(caCertPEM);
const issuerDN = caCert.getSubjectString();
Важно понимать: именно issuerDN становится значением
Issuer в подписываемом сертификате.
Перед подписью необходимо сформировать структуру сертификата, включая публичный ключ субъекта:
const kp = KEYUTIL.generateKeypair("RSA", 2048);
const subjectPublicKey = kp.pubKeyObj;
const subjectKeyPEM = KEYUTIL.getPEM(subjectPublicKey);
Далее задаются основные поля сертификата:
В Jsrsasign используется KJUR.asn1.x509.Certificate (или
более низкоуровневые структуры ASN.1) для формирования сертификата.
const cert = new KJUR.asn1.x509.Certificate({
version: 3,
serial: { int: 1 },
sigalg: "SHA256withRSA",
issuer: issuerDN,
notbefore: "202601010000Z",
notafter: "202701010000Z",
subject: "/C=KZ/O=Example Org/OU=IT/CN=example.com",
pubkey: subjectKeyPEM,
ext: [
{
extname: "basicConstraints",
cA: false
},
{
extname: "keyUsage",
names: ["digitalSignature", "keyEncipherment"]
}
]
});
На этом этапе создаётся структура, которая ещё не подписана.
Подпись выполняется автоматически внутри Jsrsasign при вызове метода генерации сертификата. Библиотека:
Пример явного указания CA ключа:
const caKey = KEYUTIL.getKey(caPrivateKeyPEM);
cert.sign(caKey, "SHA256withRSA");
После этого объект содержит полностью сформированный X.509 сертификат.
Результат можно экспортировать в PEM-формат:
const pem = cert.getPEM();
console.log(pem);
Или в DER (Base64):
const der = cert.getHex();
Под капотом происходит следующая последовательность:
ASN.1 структура сериализуется:
Алгоритм определяется параметром sigalg:
Результат:
hash = SHA256(DER(TBSCertificate))
Для RSA:
signature = RSA_sign(hash, CA_private_key)
Для ECDSA:
signature = ECDSA_sign(hash, CA_private_key)
Финальный объект:
CA сертификат часто имеет критические ограничения:
{
extname: "basicConstraints",
cA: true,
critical: true
}
Это важно, потому что только сертификаты с cA: true
могут использоваться для подписи других сертификатов.
Когда CA подписывает сертификат, создаётся доверительная цепочка:
Root CA → Intermediate CA → End Entity
Jsrsasign не навязывает структуру цепочки, но позволяет реализовать многоуровневую модель через повторное использование ключей CA.
Если используется intermediate CA:
const intermediateKey = KEYUTIL.getKey(intermediateCAKeyPEM);
cert.sign(intermediateKey, "SHA256withRSA");
Issuer DN в этом случае берётся из intermediate CA сертификата.
После генерации можно проверить подпись:
const x509 = new X509();
x509.readCertPEM(pem);
const isValid = x509.verifySignature(caCert.getPublicKey());
Проверка включает:
Если Issuer DN не совпадает с Subject CA сертификата, цепочка доверия разрывается.
CA ключ RSA нельзя использовать с ECDSA алгоритмом и наоборот.
Если notbefore и notafter некорректны,
сертификат может считаться недействительным сразу после создания.
В production сценариях обычно применяются:
Jsrsasign позволяет эмулировать такую архитектуру полностью в JavaScript, что удобно для тестирования и генерации сертификатов в dev-средах.
CA может добавлять расширения:
ext: [
{
extname: "subjectAltName",
array: [{ dns: "example.com" }]
},
{
extname: "authorityKeyIdentifier",
kid: caCert.getPublicKey().pubKeyHex
}
]
Эти поля влияют на поведение TLS и проверку цепочек доверия.
Выбор алгоритма влияет на безопасность:
Jsrsasign поддерживает все основные схемы через единый интерфейс
sign().
Процесс можно свести к последовательности: