Skip to content

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 ​

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 (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:

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

Validity and signatures ​

ts
cert.checkValidity();
cert.checkValidity(new Date('2030-01-01T00:00:00Z'));

const ok = cert.verifySignature(caCertificate);

verifySignature() accepts three issuer forms:

  • a Certificate parsed 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 userID is omitted the default id is tried first, then the empty id:
ts
cert.verifySignature(ca, { userID: new Uint8Array(0) });

Issuing ​

ts
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=false cannot be combined with keyCertSign; pathLen is 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 verify works);
  • for the GM/T default id use userID: DEFAULT_SM2_USER_ID.
ts
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's hash algorithm;
  • 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 ​

ts
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:

ts
import { sm3 } from 'sm-crypto-wasm';
await sm3.ensureLoaded();

Errors ​

Structural problems (truncation, trailing bytes, malformed DER) throw CertificateError:

ts
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 === false and verifySignature() returns false.

Scope ​

CapabilityStatus
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❌

Released under the Apache-2.0 License