Inspect: render any PKI structure as readable text
openssl x509 -text. certificate(input) ingests a PEM string, a DER Buffer, or an already-parsed certificate and returns a familiar OpenSSL-style report: version, serial, signature algorithm, the issuer and subject distinguished names, the validity window, the public-key details (curve or modulus size plus the raw point/modulus), every decoded extension with its critical flag, and the signature. It renders purely from the toolkit's own strict parser and two-way OID registry, with no OpenSSL dependency and no drift-prone second naming table, so it names extension and algorithm OIDs an OpenSSL build shows only as raw bytes. The format is stable and OpenSSL-*familiar*, never byte-identical to any one OpenSSL version (those disagree across releases). Rendering is best-effort: a malformed extension falls back to a hex dump and does not throw.pki.inspect.certificate
pki.inspect.certificate(input) -> string
Render a certificate as a human-readable, OpenSSL-familiar text report. input is a PEM string, a DER Buffer, or a pki.schema.x509.parse result. A value that is none of those throws inspect/bad-input; a malformed certificate throws inspect/bad-certificate; but a malformed individual extension is rendered as a hex dump and does not fail the whole report. Pure, with no OpenSSL dependency.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
var cert = pki.schema.x509.parse(der);
pki.inspect.certificate(cert).split("\n")[0]; // "Certificate:"
}
example();
References
- spec RFC 5280
pki.inspect.crl
pki.inspect.crl(input) -> string
Render a certificate revocation list as an openssl crl -text-familiar text report: issuer, Last/Next Update, the CRL extensions, each revoked entry (serial, revocation date, entry extensions), and the signature. input is a PEM string, a DER Buffer, or a pki.schema.crl.parse result; a non-CRL throws inspect/bad-crl, a wrong-type input inspect/bad-input. A malformed individual extension renders as hex and does not fail the report.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var caCert = await pki.x509.sign({ subject: "Issuing CA", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
extensions: { basicConstraints: { cA: true }, keyUsage: ["keyCertSign", "cRLSign"] } }, { key: key });
var crlDer = await pki.crl.sign({ thisUpdate: new Date("2026-01-01T00:00:00Z"),
nextUpdate: new Date("2026-02-01T00:00:00Z"), crlNumber: 1n, revoked: [] },
{ cert: caCert, key: key });
pki.inspect.crl(crlDer).split("\n")[0]; // "Certificate Revocation List (CRL):"
}
example();
References
- spec RFC 5280
pki.inspect.csr
pki.inspect.csr(input) -> string
Render a PKCS#10 certification request as an openssl req -text-familiar text report: subject, the subject public key, the requested extensions and other attributes, and the signature. input is a PEM string, a DER Buffer, or a pki.schema.csr.parse result; a non-CSR throws inspect/bad-csr, a wrong-type input inspect/bad-input. Best-effort like certificate.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var csrDer = await pki.csr.sign({ subject: "req.example", subjectPublicKey: await pki.key.export(pair.publicKey) },
{ key: await pki.key.export(pair.privateKey) });
pki.inspect.csr(csrDer).split("\n")[0]; // "Certificate Request:"
}
example();
References
- spec RFC 2986
pki.inspect.cms
pki.inspect.cms(input) -> string
Render a CMS message as an openssl cms -cmsout -print-familiar text report. A SignedData shows the content type, digest algorithms, encapsulated content, embedded certificates/CRLs, and each SignerInfo (signer identifier, algorithms, signed/unsigned attributes, signature); a non-SignedData ContentInfo renders a stable top-field summary. input is a PEM string, a DER Buffer, or a pki.schema.cms.parse result; a non-CMS throws inspect/bad-cms. Best-effort.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var cert = await pki.x509.sign({ subject: "Signer", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
var cmsDer = await pki.cms.sign(Buffer.from("hello"), { cert: cert, key: key });
pki.inspect.cms(cmsDer).split("\n")[0]; // "CMS ContentInfo:"
}
example();
References
- spec RFC 5652
pki.inspect.pkcs8
pki.inspect.pkcs8(input) -> string
Render a PKCS#8 private key as a text report: the version, the key algorithm and its parameters, the presence and length of the private key and of a public half, and the type of each attribute. No byte string from inside the file is rendered. That is structural rather than a screen on the private key, because RFC 5958 has the parser check that a v2 OneAsymmetricKey HAS a public half and well formed attributes, never that either is unrelated to the private key: private bytes can sit in the public BIT STRING or in any attribute value, so a report that wrote either out could be made to write the key out. pki.key.export is the verb for the bytes themselves. input is a PEM string, a DER Buffer, or a pki.schema.pkcs8.parse result; a non-PKCS#8 throws inspect/bad-pkcs8, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
pki.inspect.pkcs8(await pki.key.export(pair.privateKey)).split("\n")[0]; // "PKCS#8 Private Key:"
}
example();
References
- spec RFC 5958
pki.inspect.pkcs12
pki.inspect.pkcs12(input) -> string
Render a PKCS#12 store's outer structure as a text report: the version, the integrity mode, the MAC algorithm and its iteration count, and one line per safe bag giving its type. No private key is rendered, and an encrypted safe's contents are not decrypted, because this verb takes no password: the report names pki.pkcs12.open as what reads the contents. input is a PEM string, a DER Buffer, or a pki.schema.pkcs12.parse result; a non-PKCS#12 throws inspect/bad-pkcs12, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var cert = await pki.x509.sign({ subject: "p12.example", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
var store = await pki.pkcs12.build({ safeContents: [{ bags: [{ type: "cert", cert: cert }] }] }, { password: "1234" });
pki.inspect.pkcs12(store).split("\n")[0]; // "PKCS#12 Store:"
}
example();
References
- spec RFC 7292
pki.inspect.crmf
pki.inspect.crmf(input) -> string
Render CRMF certificate request messages as a text report: one block per message giving its request id, the certificate template's subject, issuer, validity and public key, the requested extensions, and whether a proof of possession and registration controls are present. input is a PEM string, a DER Buffer, or a pki.schema.crmf.parse result; a non-CRMF throws inspect/bad-crmf, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.crmf.build({ certReqId: 0, certTemplate: { subject: "crmf.example",
publicKey: await pki.key.export(pair.publicKey) } }, { key: await pki.key.export(pair.privateKey) });
pki.inspect.crmf(der).split("\n")[0]; // "CRMF Certificate Request Messages:"
}
example();
References
- spec RFC 4211
pki.inspect.cmp
pki.inspect.cmp(input) -> string
Render a CMP message as a text report: the header's protocol version, sender and recipient, transaction id and nonces, the body arm the message carries, and whether protection and extra certificates are present. The body's inner content is named rather than expanded, since each arm is its own structure that its own verb reads. input is a PEM string, a DER Buffer, or a pki.schema.cmp.parse result; a non-CMP throws inspect/bad-cmp, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var cert = await pki.x509.sign({ subject: "cmp.example", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
var der = await pki.cmp.build({ header: { sender: { directoryName: "CN=cmp.example" },
recipient: { directoryName: "CN=A CA" } },
body: { ir: { certTemplate: { subject: [{ commonName: "cmp.example" }], publicKey: await pki.key.export(pair.publicKey) } } } },
{ cert: cert, key: key });
pki.inspect.cmp(der).split("\n")[0]; // "CMP Message:"
}
example();
References
- spec RFC 9810
pki.inspect.csrattrs
pki.inspect.csrattrs(input) -> string
Render an EST CSR Attributes response as a text report: one line per item, each either a bare object identifier the server asks the client to supply or an attribute with its type and values. input is a PEM string, a DER Buffer, or a pki.schema.csrattrs.parse result; a non-CsrAttrs throws inspect/bad-csrattrs, a wrong-type input inspect/bad-input.
Example
var b = pki.asn1.build;
var der = b.sequence([b.oid("1.2.840.113549.1.9.7")]);
pki.inspect.csrattrs(der).split("\n")[0]; // "EST CSR Attributes:"
References
pki.inspect.trustanchor
pki.inspect.trustanchor(input) -> string
Render an RFC 5914 trust anchor list as a text report: one block per anchor giving which of the three forms it takes, its public key, key identifier and title where present, and the certificate path controls that bound what it may anchor. input is a PEM string, a DER Buffer, or a pki.schema.trustanchor.parse result; a non-list throws inspect/bad-trustanchor, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var b = pki.asn1.build;
var spki = await pki.key.export(pair.publicKey);
var der = b.sequence([b.explicit(2, b.sequence([b.raw(spki), b.octetString(Buffer.alloc(20, 1))]))]);
pki.inspect.trustanchor(der).split("\n")[0]; // "Trust Anchor List:"
}
example();
References
- spec RFC 5914
pki.inspect.ocspRequest
pki.inspect.ocspRequest(input) -> string
Render an OCSP request as a text report: the version, the requestor name where one is given, one block per requested certificate giving the CertID's hash algorithm and the issuer name and key hashes with the serial asked about, the request extensions, and whether the request is signed. input is a PEM string, a DER Buffer, or a pki.schema.ocsp.parseRequest result; a non-request throws inspect/bad-ocsp-request, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var cert = await pki.x509.sign({ subject: "ocsp.example", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
var der = await pki.ocsp.buildRequest({ cert: cert, issuer: cert });
pki.inspect.ocspRequest(der).split("\n")[0]; // "OCSP Request:"
}
example();
References
- spec RFC 6960
pki.inspect.ocspResponse
pki.inspect.ocspResponse(input) -> string
Render an OCSP response as a text report: the response status, and for a basic response the responder id, the time it was produced, one block per answer giving the CertID, the certificate's status with its revocation time and reason where it is revoked, and the validity window of the answer, plus the response extensions and the certificates carried for the relying party to chain. input is a PEM string, a DER Buffer, or a pki.schema.ocsp.parseResponse result; a non-response throws inspect/bad-ocsp-response, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey);
var cert = await pki.x509.sign({ subject: "ocsp.example", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") }, { key: key });
var der = await pki.ocsp.sign({ responderID: "byName",
responses: [{ cert: cert, issuer: cert, status: "good", thisUpdate: new Date("2026-01-02T00:00:00Z") }] },
{ cert: cert, key: key });
pki.inspect.ocspResponse(der).split("\n")[0]; // "OCSP Response:"
}
example();
References
- spec RFC 6960
pki.inspect.tsp
pki.inspect.tsp(input) -> string
Render an RFC 3161 timestamp response as a text report: the PKI status with its status strings and failure info, and the TSTInfo inside the token, giving the policy, the message imprint, the serial, the time it was stamped with its accuracy and ordering, the nonce, and the TSA name. The token's own CMS structure, its signer identifier and its signed attributes, is what pki.inspect.cms renders, and a bare timestamp TOKEN is a CMS ContentInfo rather than a response, so that verb reads one of those directly. input is a PEM string, a DER Buffer, or a pki.schema.tsp.parseResponse result; a non-response throws inspect/bad-tsp, a wrong-type input inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var key = await pki.key.export(pair.privateKey), spki = await pki.key.export(pair.publicKey);
var ca = await pki.x509.sign({ subject: "tsa.example", subjectPublicKey: spki,
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
extensions: { keyUsage: ["digitalSignature"], extendedKeyUsage: ["timeStamping"], extendedKeyUsageCritical: true } },
{ key: key });
var imprint = { hashAlgorithm: "sha256", hashedMessage: Buffer.alloc(32, 7) };
var token = await pki.tsp.sign(imprint, { cert: ca, key: key }, { policy: "1.2.3", serialNumber: 1 });
pki.inspect.tsp(pki.tsp.response(token, {})).split("\n")[0]; // "Timestamp Response:"
}
example();
References
- spec RFC 3161
pki.inspect.attrcert
pki.inspect.attrcert(input) -> string
Render an RFC 5755 attribute certificate as a text report: the version, the holder and the issuer in whichever form each takes, the serial, the validity period, the attributes the certificate asserts, its extensions, and the signature algorithm. input is a PEM string, a DER Buffer, or a pki.schema.attrcert.parse result; a non-attribute-certificate throws inspect/bad-attrcert, a wrong-type input inspect/bad-input. An X.509-1997 v1 attribute certificate is not parsed by this build, and the report surfaces that refusal rather than rendering a partial one.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.attrcert.sign({ holder: { entityName: { directoryName: "CN=Alice" } },
notBeforeTime: new Date("2026-01-01T00:00:00Z"), notAfterTime: new Date("2027-01-01T00:00:00Z"),
attributes: { role: { roleName: { uniformResourceIdentifier: "urn:role:admin" } } } },
{ name: "CN=An AA", publicKey: await pki.key.export(pair.publicKey), key: await pki.key.export(pair.privateKey) });
pki.inspect.attrcert(der).split("\n")[0]; // "Attribute Certificate:"
}
example();
References
- spec RFC 5755
pki.inspect.asn1
pki.inspect.asn1(input, opts?) -> string
Render the TLV tree of input (a PEM string or DER Buffer) as a structural dump, one line per value, in the shape openssl asn1parse prints: the byte offset, the nesting depth, the header length, the content length, whether the value is constructed, the tag name, and for a primitive its rendered content. An OBJECT IDENTIFIER carries its dotted form and its registry name. A UTCTime or GeneralizedTime renders the bytes as written rather than a formatted date, so a malformed time is visible instead of normalized away.
Two routes produce a report and the first line names which one ran. pki.asn1.decode reads the input first, so a conforming file is rendered from the same decoder the rest of the toolkit parses with. When that decode refuses the bytes, the report names the refusal and a header-by-header walk renders what it can read: a truncated file, trailing bytes, a non-minimal length, an indefinite length, a segmented string. The last line of every report states the outcome: the offset where the structure ends, or the value the walk could not read through and why, or the bytes that follow the top-level value, or the cap that stopped the report and where the next value begins. A partial dump cannot be mistaken for a whole structure. The walk returns text and never a node tree, and it carries the depth and node caps, so tolerating a broken encoding here relaxes nothing the parsers enforce.
No value is withheld here. Where the other reports name a key and print none of it, this one prints bytes, private key material included, which is what a byte-level dump is for. The caps bound how much of a value is rendered, not whether it is. pki.inspect.any and pki.inspect.pkcs8 are the reports that name a key without printing it.
Options
maxBytes: number, // default and ceiling: C.LIMITS.DER_MAX_BYTES (16 MiB); refuses a
// longer input, and tightens only -- a larger value is refused
maxDepth: number, // default: C.LIMITS.DER_MAX_DEPTH (64); stops at this nesting depth
maxNodes: number, // default: C.LIMITS.DUMP_MAX_NODES; stops after this many nodes
maxValueBytes: number, // default: C.LIMITS.DUMP_MAX_VALUE_BYTES; renders at most this
// many bytes of one value and counts the rest
Example
var b = pki.asn1.build;
var der = b.sequence([b.integer(1), b.oid("2.5.4.3"), b.nullValue()]);
console.log(pki.inspect.asn1(der));
// ASN.1 structure: 12 bytes, decoded as DER
// 0:d=0 hl=2 l= 10 cons: SEQUENCE
// 2:d=1 hl=2 l= 1 prim: INTEGER :1
// 5:d=1 hl=2 l= 3 prim: OBJECT IDENTIFIER :2.5.4.3 (commonName)
// 10:d=1 hl=2 l= 0 prim: NULL
// Complete: the structure ends at offset 12, the end of the input.
console.log(pki.inspect.asn1(der.subarray(0, 7)));
// ASN.1 structure: 7 bytes, header walk
// DER decode refused: asn1/truncated: content length overruns the buffer
// 0:d=0 hl=2 l= 10 cons: SEQUENCE
// 2:d=1 hl=2 l= 1 prim: INTEGER :1
// Incomplete: the walk could not read the value at offset 5: content length 3 overruns the 0 bytes that remain.
References
- spec X.690
- spec
ISO/IEC 8825-1 - defends
ASN.1-parser-DoS (CWE-400)
pki.inspect.any
pki.inspect.any(input) -> string
Detect which PKI format input (a PEM string or DER Buffer) encodes and render it with the matching report, the inspect analogue of pki.schema.parse. Every format pki.schema.all() detects has a report and its own named verb, so the same bytes render the same whether the format is detected or asked for by name. An X.509-1997 v1 attribute certificate is detected so it can be named, and it is refused with the reason its own parser gives rather than as an unsupported format. An unrecognized input throws inspect/bad-input.
Example
async function example() {
var pair = await pki.key.generate("Ed25519");
var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z") },
{ key: await pki.key.export(pair.privateKey) });
pki.inspect.any(der); // routes to the right report by detected format
}
example();
References
- spec RFC 5280