Сертификаты X.509
sm-crypto-wasm/x509 предоставляет только чтение сертификатов с китайскими алгоритмами и проверку подписи одного сертификата:
- разбор сертификатов в DER / PEM / base64 / hex;
- чтение версии, серийного номера, издателя/субъекта, срока действия, открытого ключа и расширений;
- проверка подписи одного сертификата открытым ключом издателя;
- вычисление отпечатков SM3 / SHA-256.
Библиотека не реализует проверку цепочки, политик, ограничений имён, а также загрузку CRL / OCSP / AIA. Для проверки цепочки проходите по связям issuer / authorityKeyIdentifier и вызывайте verifySignature() самостоятельно.
Разбор
import { parseCertificate, parseCertificates } from 'sm-crypto-wasm/x509';
const cert = parseCertificate(pem);
cert.version; // 1 | 2 | 3
cert.serialNumber; // bigint
cert.serialNumberHex; // '1003'
cert.issuer.toString(); // 'C=CN,O=Example,CN=Example CA'
cert.subject.commonName; // 'server.example.test'
cert.validity.notBefore; // Date (точность до секунды)
cert.publicKeyAlgorithm; // 'SM2' | 'RSA' | 'UNKNOWN'
cert.supported; // может ли библиотека использовать этот ключ
cert.subjectPublicKey; // открытый ключ; SM2 — 65 байт 04 || X || Y
// PEM-набор или подряд идущие DER
const chain = parseCertificates(bundle);Расширения
Распространённые расширения доступны как поля; неизвестные сохраняются с OID, флагом critical и исходным значением:
cert.basicConstraints; // { ca: true, pathLen: 2 }
cert.keyUsage; // { digitalSignature, keyCertSign, ... }
cert.subjectAltNames; // [{ type: 'dns' | 'ip' | 'email' | 'uri', value }]
cert.extendedKeyUsage; // ['1.3.6.1.5.5.7.3.1']
cert.subjectKeyIdentifier; // Uint8Array
cert.authorityKeyIdentifier;// { keyIdentifier?, authorityCertSerialNumber? }
cert.getExtension('1.2.3.4'); // Extension | undefinedСрок действия и подписи
cert.checkValidity();
cert.checkValidity(new Date('2030-01-01T00:00:00Z'));
const ok = cert.verifySignature(caCertificate);verifySignature() принимает три формы издателя:
Certificate, разобранный библиотекой;- байты сертификата DER / PEM;
- открытый ключ (64 или 65 байт).
Идентификатор пользователя SM2:
- сертификаты GM/T используют идентификатор по умолчанию
1234567812345678; - X.509-путь OpenSSL по умолчанию использует пустой идентификатор;
- если
userIDне указан, сначала проверяется идентификатор по умолчанию, затем пустой:
cert.verifySignature(ca, { userID: new Uint8Array(0) });Выпуск
import { sm2, sm3 } from 'sm-crypto-wasm';
import { createSelfSignedCertificate, issueCertificate, parseDistinguishedName } from 'sm-crypto-wasm/x509';
await sm2.ensureLoaded();
await sm3.ensureLoaded(); // SKI по умолчанию использует SM3
const caKeys = await sm2.generateKeyPair();
const ca = createSelfSignedCertificate({
subject: parseDistinguishedName('C=CN,O=Example,CN=Example CA'),
privateKey: caKeys.privateKeyPem,
notAfter: new Date('2035-01-01T00:00:00Z'),
basicConstraints: { ca: true, pathLen: 1 },
keyUsage: ['keyCertSign', 'cRLSign'],
});
const leafKeys = await sm2.generateKeyPair();
const leaf = issueCertificate({
subject: { commonName: 'server.example.test' },
subjectPublicKey: leafKeys.publicKeyPem, // обязательно при выпуске через CA
notAfter: new Date('2030-01-01T00:00:00Z'),
basicConstraints: { ca: false },
keyUsage: ['digitalSignature', 'keyEncipherment'],
extendedKeyUsage: ['1.3.6.1.5.5.7.3.1'],
subjectAltNames: [{ type: 'dns', value: 'server.example.test' }],
signer: { privateKey: caKeys.privateKeyPem, certificate: ca.certificate },
});Ограничения:
- сертификаты всегда v3;
ca=falseнельзя сочетать сkeyCertSign;pathLenтолько для CA;- не-CA сертификат (
basicConstraints.ca !== true) не может быть издателем; - серийный номер должен быть положительным; по умолчанию — 16 случайных байт со сброшенным старшим битом;
- DNS/email/URI в SAN должны быть ASCII; IP должен быть корректным IPv4/IPv6.
Идентификатор пользователя SM2:
- по умолчанию пустой (как в X.509-пути OpenSSL, поэтому
openssl verifyработает); - для идентификатора GM/T передайте
userID: DEFAULT_SM2_USER_ID.
import { DEFAULT_SM2_USER_ID } from 'sm-crypto-wasm/x509';
createSelfSignedCertificate({ ..., userID: DEFAULT_SM2_USER_ID });SKI:
- по умолчанию первые 20 байт
SM3(publicKey); subjectKeyIdentifier: { hash: 'sha1' }соответствует алгоритму OpenSSL;- AKI при самоподписи равен SKI, при выпуске через CA берётся SKI сертификата издателя.
Библиотека не генерирует и не хранит закрытые ключи.
Отпечатки
cert.fingerprint(); // SM3 (hex, 32 байта)
cert.fingerprint('sha256'); // SHA-256 (встроенная реализация на JS)Для SM3 нужно заранее загрузить wasm-модуль SM3:
import { sm3 } from 'sm-crypto-wasm';
await sm3.ensureLoaded();Ошибки
Структурные проблемы (обрыв, лишние байты, некорректный DER) вызывают CertificateError:
import { CertificateError } from 'sm-crypto-wasm/x509';
try {
parseCertificate(input);
} catch (e) {
if (e instanceof CertificateError) { /* не сертификат / плохая структура */ }
}Неизвестные расширения, алгоритмы, типы атрибутов Name и сертификаты RSA не вызывают ошибок:
- неизвестное содержимое сохраняется как исходные байты;
- сертификаты RSA разбираются, но дают
supported === false, аverifySignature()возвращаетfalse.
Область
| Возможность | Статус |
|---|---|
| Разбор DER / PEM / base64 / hex | ✅ |
| KeyUsage / BasicConstraints / SAN / SKI / AKI / EKU | ✅ |
| Проверка подписи SM2 одного сертификата (GM/T + OpenSSL id) | ✅ |
| Отпечатки SM3 / SHA-256 | ✅ |
| Проверка цепочки сертификатов | ❌ |
| CRL / OCSP / AIA | ❌ |