X.509 证书解析
sm-crypto-wasm/x509 提供只读的国密证书解析与单证书验签:
- 解析 DER / PEM / base64 / hex 证书;
- 读取版本、序列号、签发者/主体、有效期、公钥、扩展;
- 用签发者公钥验证单张证书的签名;
- 计算 SM3 / SHA-256 指纹。
不提供证书链路径校验、策略校验、名称约束、CRL / OCSP / AIA 拉取。 需要链校验时,请按 issuer / authorityKeyIdentifier 自己逐级调用 verifySignature()。
解析
ts
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 bundle 或连续 DER
const chain = parseCertificates(bundle);扩展
常用扩展解析为便捷字段;未知扩展保留 OID + critical + 原始值:
ts
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有效期与验签
ts
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; - OpenSSL 的 X.509 路径默认使用空标识;
- 省略
userID时先按默认标识验证,失败后按空标识重试:
ts
cert.verifySignature(ca, { userID: new Uint8Array(0) }); // 显式指定签发
ts
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 字节随机数(最高位清零);
- SAN 的 DNS/email/URI 必须为 ASCII,IP 必须是合法 IPv4/IPv6。
SM2 用户标识:
- 默认空标识(与 OpenSSL 的 X.509 路径一致,
openssl verify可直接验证); - GM/T 默认标识:
userID: DEFAULT_SM2_USER_ID。
ts
import { DEFAULT_SM2_USER_ID } from 'sm-crypto-wasm/x509';
createSelfSignedCertificate({ ..., userID: DEFAULT_SM2_USER_ID });SKI:
- 默认
SM3(公钥)前 20 字节; subjectKeyIdentifier: { hash: 'sha1' }兼容 OpenSSL 的hash算法;- AKI 自签时等于 SKI,CA 签发时取签发者证书的 SKI。
本模块不生成密钥、不管理私钥。
指纹
ts
cert.fingerprint(); // SM3(32 字节 hex)
cert.fingerprint('sha256'); // SHA-256(内置纯 JS 实现)SM3 指纹需要先预热 SM3 wasm:
ts
import { sm3 } from 'sm-crypto-wasm';
await sm3.ensureLoaded();错误处理
证书结构、截断、尾部多余字节等问题统一抛 CertificateError:
ts
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 用户标识) | ✅ |
| SM3 / SHA-256 指纹 | ✅ |
| 证书链路径校验 | ❌ |
| CRL / OCSP / AIA | ❌ |