X.509 certificates
sm-crypto-wasm/x509 offers read-only parsing of Chinese-algorithm certificates plus single-certificate signature verification:
- parse DER / PEM / base64 / hex certificates;
- read version, serial number, issuer/subject, validity, public key and extensions;
- verify one certificate against an issuer public key;
- compute SM3 / SHA-256 fingerprints.
It does not implement certificate-chain path validation, policy validation, name constraints, or CRL / OCSP / AIA fetching. For chain validation, walk the issuer / authorityKeyIdentifier links and call verifySignature() yourself.
Parsing
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 (second precision)
cert.publicKeyAlgorithm; // 'SM2' | 'RSA' | 'UNKNOWN'
cert.supported; // whether this library can use the key
cert.subjectPublicKey; // raw key; SM2 is 65 bytes 04 || X || Y
// PEM bundle or concatenated DER
const chain = parseCertificates(bundle);Extensions
Common extensions are exposed as convenience fields; unknown extensions keep their OID, critical flag and raw value:
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 | undefinedValidity and signatures
cert.checkValidity();
cert.checkValidity(new Date('2030-01-01T00:00:00Z'));
const ok = cert.verifySignature(caCertificate);verifySignature() accepts three issuer forms:
- a
Certificateparsed by this library; - DER / PEM certificate bytes;
- a raw public key (64 or 65 bytes).
SM2 user ids:
- GM/T certificates use the default id
1234567812345678; - OpenSSL's X.509 path uses the empty id by default;
- when
userIDis omitted the default id is tried first, then the empty id:
cert.verifySignature(ca, { userID: new Uint8Array(0) });Issuing
import { sm2, sm3 } from 'sm-crypto-wasm';
import { createSelfSignedCertificate, issueCertificate, parseDistinguishedName } from 'sm-crypto-wasm/x509';
await sm2.ensureLoaded();
await sm3.ensureLoaded(); // default SKI uses 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, // required when signing with a 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 },
});Constraints:
- certificates are always v3;
ca=falsecannot be combined withkeyCertSign;pathLenis CA-only;- a non-CA certificate (
basicConstraints.ca !== true) cannot act as an issuer; - serial numbers must be positive; the default is 16 random bytes with the top bit cleared;
- SAN DNS/email/URI values must be ASCII; IP values must be valid IPv4/IPv6.
SM2 user id:
- the default is the empty id (matching OpenSSL's X.509 path, so
openssl verifyworks); - for the GM/T default id use
userID: DEFAULT_SM2_USER_ID.
import { DEFAULT_SM2_USER_ID } from 'sm-crypto-wasm/x509';
createSelfSignedCertificate({ ..., userID: DEFAULT_SM2_USER_ID });SKI:
- defaults to the first 20 bytes of
SM3(publicKey); subjectKeyIdentifier: { hash: 'sha1' }matches OpenSSL'shashalgorithm;- AKI equals SKI for self-signed certificates and uses the issuer certificate's SKI when signing with a CA.
The library does not generate or manage private keys.
Fingerprints
cert.fingerprint(); // SM3 (32-byte hex)
cert.fingerprint('sha256'); // SHA-256 (built-in pure JS)SM3 requires the SM3 wasm module to be loaded first:
import { sm3 } from 'sm-crypto-wasm';
await sm3.ensureLoaded();Errors
Structural problems (truncation, trailing bytes, malformed DER) throw CertificateError:
import { CertificateError } from 'sm-crypto-wasm/x509';
try {
parseCertificate(input);
} catch (e) {
if (e instanceof CertificateError) { /* not a certificate / bad structure */ }
}Unknown extensions, unknown algorithms, unknown Name attribute types and RSA certificates never throw:
- unknown content is preserved as raw bytes;
- RSA certificates parse normally but report
supported === falseandverifySignature()returnsfalse.
Scope
| Capability | Status |
|---|---|
| DER / PEM / base64 / hex parsing | ✅ |
| KeyUsage / BasicConstraints / SAN / SKI / AKI / EKU | ✅ |
| Single-certificate SM2 verification (GM/T + OpenSSL user id) | ✅ |
| SM3 / SHA-256 fingerprints | ✅ |
| Certificate-chain path validation | ❌ |
| CRL / OCSP / AIA | ❌ |