{"id":51417357,"url":"https://github.com/blamejs/pki","last_synced_at":"2026-07-04T21:00:30.287Z","repository":{"id":369277165,"uuid":"1288987110","full_name":"blamejs/pki","owner":"blamejs","description":"Pure-JavaScript PKI toolkit that owns its stack — X.509, ASN.1/DER, CMS, PQC-first.","archived":false,"fork":false,"pushed_at":"2026-07-04T12:48:12.000Z","size":1468,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-04T13:19:38.752Z","etag":null,"topics":["asn1","cryptography","nodejs","pki","post-quantum","webcrypto","x509","zero-dependencies"],"latest_commit_sha":null,"homepage":"https://pkijs.com","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/blamejs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":"THREAT-MODEL.md","audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":"GOVERNANCE.md","roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-04T07:43:09.000Z","updated_at":"2026-07-04T12:47:26.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/blamejs/pki","commit_stats":null,"previous_names":["blamejs/pki"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/blamejs/pki","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blamejs%2Fpki","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blamejs%2Fpki/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blamejs%2Fpki/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blamejs%2Fpki/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/blamejs","download_url":"https://codeload.github.com/blamejs/pki/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/blamejs%2Fpki/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35135342,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asn1","cryptography","nodejs","pki","post-quantum","webcrypto","x509","zero-dependencies"],"created_at":"2026-07-04T21:00:25.321Z","updated_at":"2026-07-04T21:00:30.276Z","avatar_url":"https://github.com/blamejs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg src=\"assets/pkijs-logo.png\" alt=\"@blamejs/pki\" width=\"200\" /\u003e\n\n# @blamejs/pki\n\n**A pure-JavaScript PKI toolkit that owns its stack.**\n\nX.509, ASN.1/DER, OID, CMS, OCSP, timestamping, and PKCS formats — with an\nin-house, fail-closed DER codec and a post-quantum-first algorithm registry.\nNo npm runtime dependencies. No TypeScript. No Web Crypto ceiling.\n\n[![npm version](https://img.shields.io/npm/v/@blamejs/pki.svg?label=%40blamejs%2Fpki\u0026color=2563eb)](https://www.npmjs.com/package/@blamejs/pki)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0)\n[![node](https://img.shields.io/node/v/@blamejs/pki.svg)](https://nodejs.org)\n[![Zero runtime deps](https://img.shields.io/badge/runtime%20deps-0-2ea043)](#security-posture)\n[![PQC-first](https://img.shields.io/badge/crypto-PQC--first-2563eb)](#security-posture)\n\n[pkijs.com](https://pkijs.com) · [Roadmap](ROADMAP.md) · [Security](SECURITY.md) · [Changelog](CHANGELOG.md)\n\n\u003c/div\u003e\n\n---\n\n## Why this toolkit\n\nMost JavaScript PKI code inherits its ASN.1 parser and its algorithm coverage\nfrom somewhere else — an external DER library with its own CVE history, or the\nWeb Crypto API with its limits on streaming, opaque keys, and algorithm reach.\n`@blamejs/pki` owns those layers:\n\n- **Its own DER codec.** Strict, canonical, bounded. Malformed input is rejected\n  in bounded time, not walked into a stack overflow.\n- **An OID-named algorithm registry.** Every algorithm, attribute, and extension\n  is named through one two-way OID table (`pki.oid`), so a new signature or KEM\n  algorithm — including a post-quantum one — is a registry entry, not a special\n  case in `parse`. OID-driven sign/verify resolution rides the same table as the\n  signing surface lands.\n- **Fail-closed everywhere.** Every parse, sign, and verify path throws on\n  failure. No path returns zero, a default, or partial output in place of a real\n  verdict.\n- **Zero dependencies in your `package.json`.** The cryptography runs on Node's\n  built-in `node:crypto` — the full classical set plus post-quantum ML-DSA and\n  SLH-DSA signatures via the platform OpenSSL 3.5. ML-KEM key generation is\n  available today, with KEM encapsulation on the roadmap. Nothing is vendored,\n  nothing is installed; `npm audit` has nothing to say because there is no\n  dependency tree.\n\n## Install\n\n```sh\nnpm i @blamejs/pki\n```\n\nRequires Node.js 24.18+ (runs on the shipped runtime — no build step, no\ntranspilation).\n\n```js\nvar pki = require(\"@blamejs/pki\");\n```\n\n## Quickstart\n\n### Parse an X.509 certificate\n\n`pki.x509.parse` accepts a DER `Buffer` or a PEM string/Buffer and returns a\nfully-decoded, validated certificate — distinguished names rendered and\nstructured, the validity window as real `Date`s, algorithms and extensions\nnamed through the OID registry, and the exact signed `tbsBytes` for a downstream\nverifier.\n\n```js\nvar pki = require(\"@blamejs/pki\");\nvar fs  = require(\"node:fs\");\n\nvar pem  = fs.readFileSync(\"cert.pem\", \"utf8\");\nvar cert = pki.x509.parse(pem);\n\ncert.subject.dn;                    // \"CN=example.com, O=Example Org, C=US\"\ncert.issuer.dn;                     // \"CN=example.com, O=Example Org, C=US\"\ncert.serialNumberHex;              // \"7057e1ebeec2e5f7…\"\ncert.signatureAlgorithm.name;      // \"sha256WithRSAEncryption\"\ncert.subjectPublicKeyInfo.algorithm.name;  // \"rsaEncryption\"\ncert.validity.notAfter;            // Date — 2027-07-04T07:16:15.000Z\n\ncert.extensions.forEach(function (ext) {\n  ext.name;      // \"subjectKeyIdentifier\" (or null when the OID is unknown)\n  ext.critical;  // boolean\n  ext.value;     // Buffer — the raw extnValue OCTET STRING contents\n});\n```\n\nMalformed bytes throw a typed error rather than returning a half-parsed object:\n\n```js\ntry {\n  pki.x509.parse(Buffer.from([0x30, 0x03, 0x02, 0x01, 0x00]));\n} catch (e) {\n  e.constructor.name;  // \"CertificateError\"\n  e.code;              // e.g. \"x509/not-a-certificate\" — stable domain/reason string\n}\n```\n\n### Convert PEM ↔ DER\n\n```js\nvar der = pki.x509.pemDecode(pem, \"CERTIFICATE\");   // Buffer of DER bytes\nvar out = pki.x509.pemEncode(der, \"CERTIFICATE\");   // 64-column PEM string\n```\n\n### Decode and build ASN.1 / DER directly\n\nThe codec under every structure is public. Decode returns a zero-copy node tree;\nthe builders emit canonical DER.\n\n```js\n// Build a canonical-DER SEQUENCE, then decode it back.\nvar der = pki.asn1.build.sequence([\n  pki.asn1.build.oid(\"2.5.4.3\"),          // commonName\n  pki.asn1.build.utf8(\"example.com\"),\n]);\n\nvar node = pki.asn1.decode(der);\nnode.tagNumber === pki.asn1.TAGS.SEQUENCE;   // true\nnode.children.length;                        // 2\npki.asn1.read.oid(node.children[0]);         // \"2.5.4.3\"\npki.asn1.read.string(node.children[1]);      // \"example.com\"\n```\n\nThe decoder is strict by construction — non-DER shapes are refused, not\ntolerated:\n\n```js\ntry {\n  pki.asn1.decode(Buffer.from([0x30, 0x80, 0x00, 0x00]));  // indefinite length\n} catch (e) {\n  e.constructor.name;  // \"Asn1Error\"\n  e.code;              // \"asn1/indefinite-length\"\n}\n```\n\nSize and depth are bounded before a byte is walked; override the caps per call\nwhen you need to:\n\n```js\npki.asn1.decode(der, { maxBytes: pki.C.BYTES.mib(4), maxDepth: 32 });\n```\n\n### Resolve object identifiers\n\nEvery algorithm, attribute type, and extension is named by an OID. The registry\nis a two-way map, seeded with the RFC 5280 set, the classical algorithm set, and\nthe NIST post-quantum arcs (ML-DSA, ML-KEM, SLH-DSA).\n\n```js\npki.oid.name(\"1.2.840.113549.1.1.11\");  // \"sha256WithRSAEncryption\"\npki.oid.byName(\"sha256\");               // \"2.16.840.1.101.3.4.2.1\"\npki.oid.toArcs(\"2.5.4.3\");              // [2, 5, 4, 3]\n\n// Extend it with your own arc:\npki.oid.register(\"1.3.6.1.4.1.99999.1\", \"acmeCorpExtension\");\n```\n\n### Sign with post-quantum ML-DSA — or any classical algorithm\n\n`pki.webcrypto` is a standard W3C WebCrypto (`SubtleCrypto`) engine over\n`node:crypto`. The post-quantum suite lives in the same API as RSA, ECDSA, and\nEdDSA — pick the algorithm, the rest is identical:\n\n```js\nvar subtle = pki.webcrypto.subtle;\nvar data   = Buffer.from(\"sign me\");\n\n// FIPS 204 ML-DSA-65 — a post-quantum signature.\nvar kp  = await subtle.generateKey({ name: \"ML-DSA-65\" }, true, [\"sign\", \"verify\"]);\nvar sig = await subtle.sign({ name: \"ML-DSA-65\" }, kp.privateKey, data);\nvar ok  = await subtle.verify({ name: \"ML-DSA-65\" }, kp.publicKey, sig, data); // true\n\n// The classical set — ECDSA, RSA-PSS, Ed25519, AES-GCM, ECDH, HKDF, … — is the\n// same call shape, and every key it exports is OpenSSL/NSS-interoperable.\n```\n\n## What ships today (v0.1.0)\n\nThe core codec and certificate-reading surface are here and stable. Everything\nis callable today; nothing below is a stub.\n\n| Namespace | What it does |\n|---|---|\n| `pki.asn1` | Strict, bounded DER codec — `decode` (zero-copy node tree), `encode`, `build.*` canonical-DER value builders, `read.*` typed readers, `TAGS`, OID-content encode/decode |\n| `pki.oid` | Two-way OID ↔ name registry — `name`, `byName`, `register`, `toArcs`/`fromArcs`, `toDER`/`fromDER`; seeded with RFC 5280 + NIST PQC arcs |\n| `pki.webcrypto` | A W3C WebCrypto (`SubtleCrypto`) engine over `node:crypto` — `sign`/`verify`/`encrypt`/`decrypt`/`deriveBits`/`digest`/`generateKey`/`importKey`/`exportKey` across RSA, ECDSA, ECDH, Ed25519/Ed448, AES, HMAC, HKDF, PBKDF2, SHA — **and** post-quantum ML-DSA-44/65/87 and SLH-DSA signatures, plus ML-KEM-512/768/1024 key generation (KEM encapsulation on the roadmap). Zero-dependency, OpenSSL-interoperable |\n| `pki.x509` | Parse DER / PEM certificates into structured, validated fields — `parse`, `pemDecode`, `pemEncode` |\n| `pki.C` / `pki.constants` | Version-stable constants — functional scale helpers (`C.TIME.*`, `C.BYTES.*`), codec `LIMITS`, `version` |\n| `pki.errors` | The `PkiError` taxonomy — `defineClass` plus `ConstantsError` / `Asn1Error` / `OidError` / `PemError` / `CertificateError`, each carrying a stable `code` in `domain/reason` form |\n| `pki` CLI | `pki version`, `pki oid \u003cdotted\\|name\u003e`, `pki parse \u003ccert\u003e` |\n\n### CLI\n\n```sh\npki version                              # @blamejs/pki v0.1.0\npki oid 1.2.840.113549.1.1.11           # sha256WithRSAEncryption\npki oid sha256                           # 2.16.840.1.101.3.4.2.1\npki parse cert.pem                       # structured JSON summary of a certificate\n```\n\n### What's coming\n\nCertificate build/sign/verify, CRLs, CSRs (PKCS#10), certification-path\nvalidation, CMS (SignedData / EnvelopedData / EncryptedData / AuthenticatedData),\nOCSP, RFC 3161 timestamping, PKCS#8 / SPKI / PBES2 / PKCS#12, and the\npost-quantum certificate and CMS surface (ML-DSA / ML-KEM / SLH-DSA and hybrid\ncomposites) are on the roadmap and ride this same core. See\n[ROADMAP.md](ROADMAP.md) for the full plan and current status of each area, and\n[CHANGELOG.md](CHANGELOG.md) for what has landed.\n\n## Security posture\n\n- **Zero npm runtime dependencies, nothing vendored.** The cryptography runs on\n  Node's built-in `node:crypto`; the toolkit vendors no third-party code — a\n  platform built-in ships zero bytes and stays OpenSSL-interoperable by\n  construction. There is no dependency tree, transitive or vendored, to\n  compromise or keep current.\n- **Fail-closed DER.** The decoder rejects every non-canonical shape — indefinite\n  length, non-minimal length or tag encodings, trailing bytes, over-long or\n  over-deep input — with a typed `Asn1Error` before it walks the structure. Size\n  and depth caps are enforced up front, so adversarial input is bounded work, not\n  a stack overflow.\n- **Fail-closed verification.** Every verify path throws on failure. A default\n  that accepts-on-error is treated as a bug, not an ergonomic.\n- **PQC-first crypto.** Post-quantum ML-DSA and SLH-DSA signatures run in the\n  WebCrypto engine (`pki.webcrypto`) alongside the classical set today, and\n  ML-KEM key generation is available with KEM encapsulation on the roadmap.\n  Every algorithm is named in the OID registry (`pki.oid`); OID-driven\n  sign/verify resolution rides that registry as the signing surface lands, and\n  there is no classical-only default where a post-quantum option exists.\n- **Signed releases.** Release tags are annotated and SSH-signed; published\n  tarballs carry provenance and an SBOM. See\n  [SECURITY.md → Verifying release authenticity](SECURITY.md#verifying-release-authenticity).\n\nReport a vulnerability privately — see [SECURITY.md](SECURITY.md). For usage\nquestions and support channels, see [SUPPORT.md](SUPPORT.md).\n\n## Documentation\n\nFull primitive-by-primitive reference lives at [pkijs.com](https://pkijs.com),\ngenerated from the source comment blocks so it cannot drift from the shipped API.\n\n## License\n\n[Apache-2.0](LICENSE). Third-party attribution — currently none, since the\ntoolkit vendors nothing — is tracked in [NOTICE](NOTICE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblamejs%2Fpki","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblamejs%2Fpki","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblamejs%2Fpki/lists"}