pkinative-cli — Command-line reference
pkinative-cli 1.0.0 puts the whole pkinative engine on the command line: 18 commands, offline, with a stable
--jsoncontract for scripts, CI and AI agents. It pins pkinative ^1.0.0, its only runtime dependency.
pkinative-cli is a thin, strict front end over pkinative. Every PKI decision — parsing, path validation, revocation, signature verification, CMS, timestamps, key import — is the engine's; the CLI adds what a library deliberately does not do: it reads files and stdin, tells PEM from DER, bounds every read, turns options into flags, writes artefacts safely, renders reports, and maps every failure to one of 13 stable error classes while carrying the engine's PKI_* code verbatim. Its 18 commands reach every runtime export of pkinative 1.0.0 (117: 104 functions, the 7 classes of the PkiError family, 6 constants), and a test in the CLI repository proves it.
| Command | Subcommands | Engine API behind it |
|---|---|---|
pem |
decode, encode |
decodePem, encodePem |
oid |
name, encode, decode, validate, list |
getOidName; encodeOid, encodeObjectIdentifier, encodeRelativeOid; decodeOid, readObjectIdentifier, readRelativeOid; isValidOid; OID_REGISTRY |
fingerprint |
— | computeFingerprint, computeFingerprintAsync (--webcrypto), formatFingerprint, computeKeyIdentifier (--key-id), shake256 (--shake256) |
asn1 |
decode, encode |
decodeAsn1, decodeAsn1Sequence, the eleven read* readers, encodeAsn1Node; the encode* builders (encodeSequence, encodeInteger, encodeTime, …) |
cert |
inspect, create, encode, decode-extension, verify-signature, check-name, match-name, check-purpose |
parseCertificate, getExtension, formatDistinguishedName; createCertificate; the X.509 encoders (encodeDistinguishedName, encodeSubjectAltName, encodeSignatureAlgorithm, …); decodeExtensionValue; verifyCertificateSignature, verifySelfSignature; checkServerName; matchDnsName; checkExtendedKeyUsage |
csr |
inspect, create, verify |
parseCertificationRequest, createCertificationRequest, verifyCertificationRequest |
chain |
verify, build, validate |
verifyCertificateChain; buildCertificatePath; validateCertificatePath, with verifyCertificateSignature on each link |
crl |
inspect, find, verify-signature, check |
parseCertificateList, findRevocation, verifyCrlSignature, checkRevocation |
ocsp |
request, cert-id, inspect, verify-signature, check |
createOcspRequest, encodeOcspCertId, parseOcspResponse, verifyOcspSignature, checkOcspStatus |
cms |
sign, verify, inspect, verify-signer, add-attribute, add-timestamp |
createSignedData, verifySignedData, parseSignedData, verifySignerInfoSignature, addUnsignedAttribute; addTimeStampToken after verifyTimeStampToken |
tsp |
request, inspect, verify |
createTimeStampRequest; parseTimeStampResponse, parseTimeStampToken, parseTstInfo; verifyTimeStampToken |
key |
inspect, check |
parsePrivateKeyInfo, parseEncryptedPrivateKeyInfo; importPrivateKey, decryptPrivateKey |
p12 |
inspect, verify-mac, bags, open |
parsePkcs12, verifyPkcs12Mac, openSafeContents, openPkcs12 |
doctor |
— | canVerify, canSign, canDecrypt, plus the Node.js and engine version checks |
limits |
— | DEFAULT_PKI_LIMITS |
explain |
— | the error, reason and diagnostic registries of the pinned pkinative release |
schema |
— | — (the CLI's own JSON Schemas, draft 2020-12, and its capability manifest) |
completion |
— | — (generated from the same command registry as --help) |
That is 53 invocations: 47 subcommands and the 6 commands that take none. The CLI repository's knowledge base maps every engine export to the invocations that reach it.
Installation #
npm install --global pkinative-cli
pkinative doctor
The binary is pkinative. It runs on Node.js ^22.22.2 || ^24.14.1 || >=25.8.2 — the first releases that fix CVE-2026-21713, a variable-time PKCS#12 MAC comparison in Web Crypto — and pkinative doctor checks that floor. The package has exactly one runtime dependency, pkinative ^1.0.0, imported through one module and never inlined into the CLI's own CommonJS binary. It declares Node.js only; its CI also smoke-runs the built CLI under Deno and Bun, as information rather than a promise.
Every release is published from CI with npm provenance, a Sigstore build-provenance attestation and CycloneDX and SPDX SBOMs attached to the GitHub release. To check what you installed:
npm pack pkinative-cli@1.0.0
gh attestation verify pkinative-cli-1.0.0.tgz --repo Nizoka/pkinative-cli
When to use the CLI #
| Use the CLI when… | Use the library when… | Use the MCP server when… |
|---|---|---|
| You write shell scripts, Makefiles or CI jobs | You build a service in Node.js, Deno, Bun, a browser or a worker | A conversational assistant needs PKI tools |
| Certificates, CRLs and signatures live on disk or arrive through a pipe | The bytes live in memory and you call the engine directly | The bytes arrive as PEM or base64 in a tool call, or sit in a sandbox directory |
You want exit codes, --dry-run and one JSON envelope per run |
You want the full API, its types and an ExternalSigner |
You want typed tools with schemas, prompts and resources |
The three surfaces share one engine, so a verdict is the same whichever one computes it; Choosing your surface holds the capability × surface table.
Quick start #
# What is this certificate?
pkinative cert inspect server.pem
# Is it valid for this host, now, with revocation?
pkinative chain verify server.pem --untrusted intermediate.pem --trust root.pem \
--host www.example.com --crl ca.crl --require-revocation
# Issue a self-signed CA, then a leaf under it
pkinative cert create --spec ca.json --key ca.key -o ca.pem
pkinative cert create --spec leaf.json --key ca.key --issuer ca.pem --public-key leaf.csr -o leaf.pem
# Sign and verify a file
pkinative cms sign --content report.pdf --cert signer.pem --key signer.key --detached -o report.p7s
pkinative cms verify report.p7s --content report.pdf --trust root.pem
# Machine-readable everything
pkinative chain verify server.pem --trust root.pem --untrusted ca.pem --json --summary
Every subcommand has a runnable example in the CLI repository's samples/ directory, as a .sh and a .ps1 script, and those are the examples its CI runs and pins.
The agent contract #
The CLI is built to be driven by programs — CI steps, scripts, AI agents — without parsing prose. Everything in this section is stable across 1.x, and fields are only ever added.
- stdout is the artefact or the report — nothing else, ever. PEM, DER or hex artefacts, text or JSON reports, schemas and completion scripts go to stdout.
- stderr is the channel. Notes and diagnostics go there, and under
--json(orPKINATIVE_JSON=1) exactly one JSON envelope, as the last line. --jsonis agent mode. Report commands print compact JSON on stdout (the engine's result in pkinative's wire form) and the envelope on stderr.--format text|jsonon a report command overrides the default;--format jsonwithout--jsonprints indented JSON.- A negative verdict prints its full report on stdout and exits 1 with an envelope (
chain verify,cms verify,ocsp check, …): read both. --helpis text for people whatever--jsonsays;--version --jsonprints one object on stdout,{"name":"pkinative-cli","version":"1.0.0","pkinative":"1.0.0"}. Neither writes an envelope.
| Exit | Meaning |
|---|---|
0 |
Success; also a closed pipe (EPIPE), which ends the process quietly |
1 |
Any failure but usage: parse, I/O, policy, a limit, a negative verdict (the report is printed first) |
2 |
Usage error (E_USAGE) |
130 / 143 |
Interrupted by SIGINT / SIGTERM; the file being written is removed, finished outputs stay |
A status envelope and two error envelopes, as an agent sees them (output of the 1.0.0 binary on the CLI's own test fixtures):
{"ok":true,"command":"chain verify","valid":true,"pathLength":3,"signaturesChecked":true,"diagnostics":[]}
{"ok":false,"command":"cert inspect","error":{"code":"E_INPUT","message":"leaf.key.pem holds no CERTIFICATE or X509 CERTIFICATE or TRUSTED CERTIFICATE PEM block (labels present: PRIVATE KEY).","pkiCode":"PKI_PEM_UNEXPECTED_LABEL","remedy":"pkinative pem decode <file> (lists the labels present)"},"diagnostics":[]}
{"ok":false,"command":"cert inspect","error":{"code":"E_LIMIT","message":"certificate \"leaf.crt.pem\" exceeds --max-input-bytes (100 bytes; observed at least 1005). Raise the bound only for trusted input.","detail":{"limit":"maxInputBytes","flag":"--max-input-bytes","configured":100,"observed":1005},"remedy":"--max-input-bytes <size> (trusted input only)"},"diagnostics":[]}
| Envelope field | Present | Meaning |
|---|---|---|
ok, command |
always | The outcome, and the command with its subcommand ("chain verify") |
error.code |
failures | One of the 13 E_* classes — branch on this |
error.message |
failures | For people: the CLI's context, then the engine's message |
error.pkiCode |
engine failures | pkinative's frozen PKI_* code, verbatim — the exact cause |
error.detail |
when known | limit, flag, configured, observed; offset, path, algorithm |
error.remedy |
when a flag or command lifts it | The CLI flag that changes the outcome, e.g. --allow-sha1, --ber, --max-depth <value> |
error.reasons |
negative verdicts | The engine's PkiReason list in the wire form |
diagnostics |
always | Every PkiDiagnostic the engine emitted: { code, severity, message, standard, path, offset? } |
config |
a config file supplied defaults | The .pkinativerc.json that did |
Command-specific success fields — valid, pathLength, signaturesChecked, output, bytes (the DER length of an artefact), encoding, nonce, count, dryRun, … — are listed per invocation by pkinative schema manifest. Without --json, a failure prints error E_<CLASS> (PKI_<CODE>): <message> on stderr, then a remedy: … line when there is one.
JSON reports use pkinative's wire form: bigint becomes a decimal string, Uint8Array lowercase hex, epochMilliseconds stays a number, absent optional members are omitted and member names are unchanged. The CLI adds nothing to an engine result except where it is a CLI view: key and p12 reports are allow-list views that never carry key bytes, and command-level wrappers ({ blocks }, { oids }, { valid, reasons }) are documented per command.
Token economy. --summary prints the command's minimal JSON shape (codes instead of full reasons, names instead of objects); --fields a,b.c keeps only these dot-paths, a segment on an array mapping over it; an unknown path is omitted, except under an array, where each element it matches nothing in becomes null; --pretty indents; --quiet silences text notes and diagnostics on stderr, while the envelope still carries the diagnostics.
Inputs. A PKI input is a path, - for stdin, or stdin when the path is absent — refused on an interactive terminal. It is PEM when its first non-blank bytes are -----BEGIN , DER otherwise. Where one object is expected, a PEM file with several matching blocks is refused (E_INPUT) rather than reduced to its first block; pem decode --index extracts one. The repeatable inputs (--trust, --untrusted, --chain, --crl, --ocsp) accept PEM bundles. Time is --at <ISO 8601 | epoch ms | now>, UTC unless zoned: pin it for reproducible verdicts.
Parsing rules. Global flags and subcommand flags may sit anywhere on the line; a boolean flag never consumes the next token; --flag=value is the explicit form; a lone - is a value (stdin or stdout); -- ends the flags. Short aliases: -i (--input), -o (--output), -f (--format), -q, -h, -V. Each flag is given once unless the manifest marks it repeatable, and a subcommand takes at most the operands the manifest declares, so a verdict never depends on argument order.
The schema subjects #
pkinative schema (or schema list) lists the twelve subjects; every JSON Schema is draft 2020-12 with an $id versioned by the CLI release.
| Subject | Kind | Prints |
|---|---|---|
manifest |
document | Commands, flags, operands, exit codes, error classes, the wire form, each invocation's outputs and envelope status fields |
errors |
document | The E_* classes and the 57 PKI_* → E_* mappings with their remedies |
limits |
document | The 22 bounds: flag, kind, effective value |
status |
schema | The --json success envelope |
error |
schema | The --json failure envelope |
asn1-spec |
schema | The asn1 encode node spec |
cert-spec |
schema | The cert create spec |
csr-spec |
schema | The csr create spec |
config |
schema | The .pkinativerc.json file |
cert-encode-spec |
schema | The cert encode spec of each structure ($defs/<structure>) |
report |
schema | report <command> [<subcommand>]: the --json report on stdout, generated from the TypeScript types and held to every sample |
summary |
schema | summary <command> [<subcommand>]: its --summary shape |
pkinative schema manifest --pretty
pkinative schema report chain verify
pkinative schema summary cert inspect
Refused combinations #
Usage errors are E_USAGE, exit 2, and they are detected before anything is written — save the one only the input can reveal, p12 open on an RSA key without --rsa-scheme (below). Beyond an unknown flag, a malformed value or a missing required flag, the CLI refuses:
| Where | Refused |
|---|---|
| Every command | --password, --pass, --passin and --key-password (before anything else runs); --password-stdin=<value>; two password sources at once; the password from stdin when an input is read from stdin too; combined short flags (-qf) and -o=value; a single-value flag given twice, a switch given twice, a flag under both its names; more operands than the invocation takes; an operand beside the flag it stands for; a --max-* value that is zero, not an integer, or beyond Number.MAX_SAFE_INTEGER (there is no "unbounded"); an --at that is not ISO 8601, epoch milliseconds or now, or a date that does not exist; --encoding pem with no PEM label; raw DER to an interactive terminal; a .pkinativerc.json key outside the nine presentation keys; --config naming a file that does not exist |
pem |
decode --output without --index; encode without --label |
oid |
name, encode or validate without an OID; encode with more than one; --relative without --tlv; decode with both, or neither, a hex argument and --input |
fingerprint |
--key-id with --shake256; --webcrypto with --key-id or --shake256 |
asn1 |
decode --sequence with --path, --read or --reencode; --read with --reencode; a --path that is not dot-separated indices; encode without --spec |
cert |
inspect --extension with --raw-extensions, or naming a kind outside the 19; create or encode <structure> without --spec; create with no subject key (it is derived from --key only for a self-signed certificate from an unencrypted key); encode without one known structure; encode signature-algorithm with --spec; decode-extension without --oid, or with both or neither of --value and --input; check-name without exactly one of --host and --ip; match-name without two operands; check-purpose without --purpose |
Signing (cert create, cert encode signature-algorithm, csr create, cms sign) |
neither or both of --key and --p12; an RSA key without --rsa-scheme; --rsa-scheme or --salt-length on a non-RSA key; --salt-length with pkcs1; pkcs1 for an id-RSASSA-PSS key; --hash on Ed25519 or Ed448; --hash SHA-1 without --allow-sha1; an encrypted key or a PKCS#12 without a password; an encrypted key whose type nothing names (--key-type, the certificate or the public key) |
csr |
create without --spec, or with no public key (derived from --key only for an unencrypted key) |
chain |
verify, build or validate without --trust; verify with both --host and --ip; validate without a path |
crl |
find without exactly one of --serial and --cert; verify-signature without --issuer; check without --cert |
ocsp |
request or cert-id without --issuer; a nonce outside 1–32 octets (RFC 8954), also for chain verify --ocsp-nonce; check without --cert and --issuer |
cms |
sign without --content or --content-digest, with both, or with --content-digest but no --detached; sign without a signer certificate (--cert, or a --p12 that holds it); verify without --trust; verify-signer without --cert; add-attribute without --attribute; add-timestamp without --token |
tsp |
request with both, or neither, of --data and --digest; verify with both, or neither, of --token and --response, without --trust, or without what was stamped (--request, --data or --digest) |
p12 |
verify-mac, bags or open without a password; open on an RSA key without --rsa-scheme — only the file can reveal it, so the report (PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED, the key not imported) is printed first, then E_USAGE exits 2 with the remedy --rsa-scheme pkcs1|pss, and no --certs-out is written |
explain |
neither a code nor --list |
schema |
an unknown subject; an argument after a subject other than report and summary; report or summary without a known invocation |
completion |
no shell, or a shell other than bash, zsh, fish, powershell (pwsh is accepted for PowerShell) |
--config together with --no-config is not refused: --no-config wins and the named file is never read.
Error classes, codes and remedies #
Every failure is one of 13 classes. pkinative's 57 frozen error codes map to them by a fixed table — typed against the engine's code union, so an engine release that added a code would fail the CLI's type-check rather than leak as E_RUNTIME — and the engine's code always rides along, verbatim, in error.pkiCode.
| Class | Exit | PKI_* codes mapped to it |
When |
|---|---|---|---|
E_USAGE |
2 | PKI_INVALID_OPTION, PKI_API_MISUSE, PKI_LIMIT_INVALID |
A missing, unknown or invalid flag, argument or option value |
E_INPUT |
1 | PKI_INVALID_INPUT, PKI_ASN1_VALUE_OUT_OF_RANGE, PKI_PEM_LABEL_INVALID, PKI_PEM_UNEXPECTED_LABEL, PKI_CMS_CONTENT_TYPE_UNEXPECTED |
A spec, label or value is unacceptable; a key does not belong to its certificate |
E_PARSE |
1 | the other 35: every remaining PKI_ASN1_*, PKI_OID_INVALID, PKI_PEM_NO_BLOCK, PKI_PEM_LABEL_MISMATCH, PKI_PEM_UNTERMINATED, PKI_PEM_BASE64_INVALID, the ten PKI_X509_*, PKI_CMS_STRUCTURE_INVALID, PKI_KEY_STRUCTURE_INVALID |
The bytes are not the DER, PEM or JSON they claim to be |
E_IO |
1 | — | A filesystem failure, including a refused overwrite (remedy: --overwrite) |
E_SECURITY |
1 | PKI_CRYPTO_ALGORITHM_REFUSED, PKI_KEY_ENCRYPTION_UNSUPPORTED, PKI_KEY_MAC_UNSUPPORTED |
A policy refusal: SHA-1, a legacy PKCS#12 cipher or MAC, a PBES1 key |
E_LIMIT |
1 | PKI_LIMIT_EXCEEDED |
A bound was exceeded: one of the 22 pkinative limits, or --max-content-size |
E_UNSUPPORTED |
1 | PKI_PEM_HEADERS_FORBIDDEN, PKI_CRYPTO_ALGORITHM_UNSUPPORTED, PKI_CRYPTO_KEY_UNSUPPORTED, PKI_CMS_VERSION_UNSUPPORTED, PKI_CMS_CONTENT_NOT_OCTET_STRING, PKI_KEY_VERSION_UNSUPPORTED |
The algorithm, key or structure is not supported |
E_CRYPTO |
1 | PKI_CRYPTO_UNAVAILABLE |
Web Crypto could not run the operation |
E_PASSWORD |
1 | PKI_CRYPTO_DECRYPTION_FAILED |
A wrong password, or a MAC that does not match it |
E_NOT_FOUND |
1 | — | A named item is absent: an extension, a PEM block, a signer, a code |
E_VERIFY_FAILED |
1 | — | A signature, chain, request, timestamp or MAC verdict is negative; error.reasons says why |
E_CHECK_FAILED |
1 | PKI_STRICT_DIAGNOSTIC |
A check returned reasons (revocation, OCSP, name, purpose), or --strict escalated a warning |
E_RUNTIME |
1 | PKI_INTERNAL |
Anything else; the CLI's seeded fuzz suite holds that no input reaches it |
E_IO, E_NOT_FOUND and E_VERIFY_FAILED carry no engine code: they are the CLI's own (a filesystem error, a missing item, a negative verdict whose reasons are in error.reasons). pkinative schema errors prints the whole 57 → 13 table.
Remedies. The engine's messages name library options that do not exist on a command line, so the envelope adds remedy — the flag or command that lifts the refusal. A code absent from this table has no flag-level remedy: the input itself must change. A few call sites give a remedy of their own where the right advice depends on what was being read (cert decode-extension, a legacy-encrypted PKCS#8 --key), and a limit the CLI enforces before the engine runs names its own flag.
pkiCode |
remedy |
|---|---|
PKI_ASN1_LENGTH_NON_MINIMAL, PKI_ASN1_INDEFINITE_LENGTH_FORBIDDEN, PKI_ASN1_CONSTRUCTED_STRING_FORBIDDEN, PKI_ASN1_BOOLEAN_INVALID |
--ber (the producer wrote BER, not DER) |
PKI_ASN1_TRAILING_DATA |
Pass exactly one object; asn1 decode --sequence for concatenated objects; --allow-trailing (asn1 decode, cms inspect, cms verify) when the container defines what follows |
PKI_PEM_BASE64_INVALID |
--pem-mode lax (whitespace, line length, headers); a missing = padding must be restored first |
PKI_PEM_UNEXPECTED_LABEL |
pkinative pem decode <file> (lists the labels present) |
PKI_STRICT_DIAGNOSTIC |
Drop --strict, or fix the producer the diagnostic names |
PKI_CRYPTO_ALGORITHM_REFUSED |
--allow-sha1 (only for legacy material you already trust) |
PKI_CRYPTO_DECRYPTION_FAILED |
--password-file <file> | --password-stdin | PKINATIVE_PASSWORD (check the password) |
PKI_X509_EXTENSION_MALFORMED |
cert inspect --raw-extensions (keeps every extension undecoded) |
PKI_KEY_ENCRYPTION_UNSUPPORTED |
From a PKCS#12: convert it once with OpenSSL 3.4 or later (openssl pkcs12 -in legacy.p12 -legacy -aes256 -out bundle.pem, then openssl pkcs12 -export -in bundle.pem -pbmac1_pbkdf2 -out modern.p12). From a PKCS#8 --key: re-encrypt it with PBES2 (openssl pkcs8 -topk8 -v2 aes-256-cbc -v2prf hmacWithSHA256) |
PKI_KEY_MAC_UNSUPPORTED |
--allow-unverified-integrity (p12 open; only for a file whose origin you trust), or re-export it with openssl pkcs12 -export -pbmac1_pbkdf2 |
PKI_API_MISUSE |
pkinative <command> --help (the options given are incomplete or inconsistent for this input) |
PKI_LIMIT_EXCEEDED |
--max-<limit> <value>, the flag detail.flag names (pkinative limits lists them); raise a bound for trusted input only |
Diagnostics are not errors. The engine's PKI_DIAG_* codes ride in the envelope's diagnostics[] and print as warning PKI_DIAG_… at <path>: <message> (<standard>) lines on stderr unless --quiet. --strict escalates the first warning to E_CHECK_FAILED (pkiCode: PKI_STRICT_DIAGNOSTIC) before any report or artefact is written — including a warning that a verify or check report collects, not only one raised while parsing.
pkinative explain <code> explains any code the CLI can print: the 13 E_* classes, and from the registries of the pinned pkinative release every PKI_* error (with its CLI class, exit code and the flag that lifts it), every PKI_REASON_* reason and every PKI_DIAG_* diagnostic. pkinative explain --list [--kind error|reason|diagnostic|cli] enumerates them. The errors guide explains the engine's three vocabularies, and Where the codes surface compares the library, CLI and MCP views of the same failure.
pkinative explain PKI_CRYPTO_ALGORITHM_REFUSED
pkinative explain E_VERIFY_FAILED --json
pkinative explain --list --kind reason
Command reference #
Eighteen commands in six groups: Encodings (pem, oid, fingerprint, asn1), Certificates (cert, csr), Paths & revocation (chain, crl, ocsp), Signatures & time-stamps (cms, tsp), Keys (key, p12) and Meta (doctor, limits, explain, schema, completion). Every invocation also accepts the global options; the tables below list each subcommand's own flags, and pkinative <command> --help prints the same reference. A flag marked (repeatable) may be given several times; <file> may be - for stdin.
Encodings #
pkinative pem #
RFC 7468 textual encoding. decode lists every block (label, size, offset, headers), or writes one block's bytes with --index; encode wraps DER bytes in one block. The global --pem-mode lax tolerates whitespace, line length and RFC 1421 headers; a missing base64 padding is refused in both modes.
pkinative pem decode [<file>] [--index <n> [--encoding pem|der|hex] [-o <file>]]
pkinative pem encode [<file>] --label <LABEL> [-o <file>]
pem decode |
Description |
|---|---|
--input, -i <file> |
PEM text (default: the operand, or stdin) |
--label <LABEL> |
Refuse any block with another label |
--index <n> |
Extract block n (from 0); E_NOT_FOUND past the last block |
--encoding pem|der|hex |
Encoding of the extracted block (default der) |
--output, -o <file> |
Write the block to a file (default stdout); needs --index |
--format, -f text|json |
Format of the listing |
pem encode |
Description |
|---|---|
--input, -i <file> |
DER bytes (default: the operand, or stdin); PEM input is refused (E_INPUT) |
--label <LABEL> |
The block label, e.g. CERTIFICATE (required) |
--output, -o <file> |
Write the PEM text to a file (default stdout) |
pkinative oid #
Object identifiers: names, X.690 §8.19–8.20 encodings, validation and the registry pkinative knows.
pkinative oid name <oid>...
pkinative oid encode <oid> [--tlv] [--relative] [--encoding hex|der|pem]
pkinative oid decode <hex> [--tlv] [--relative]
pkinative oid decode --input <file> --tlv
pkinative oid validate <oid>...
pkinative oid list [--filter <text>]
| Subcommand | Flags |
|---|---|
name <oid>... |
--format, -f text|json. Prints each OID's registered name, or that it is not registered |
encode <oid> |
--tlv (the whole TLV — tag 06, or 0D with --relative — instead of the content octets); --relative (RELATIVE-OID, needs --tlv); --encoding pem|der|hex (default hex); --label <LABEL> (with --encoding pem); --output, -o <file> |
decode <hex> |
--input, -i <file> (read the bytes from a file instead of the operand); --tlv (the bytes are a whole TLV); --relative (needs --tlv); --format, -f |
validate <oid>... |
--format, -f. Exit 1 (E_CHECK_FAILED) when one is invalid |
list |
--filter <text> (keep entries whose OID, name or standard contains it); --format, -f |
pkinative fingerprint #
Digests of one PKI object (certificate, request, CRL, public key, …) given as one PEM block or DER object; no subcommand.
pkinative fingerprint [<file>] [--alg SHA-256] [--separator :] [--case upper]
pkinative fingerprint [<file>] --key-id [--alg SHA-1|SHA-256]
pkinative fingerprint [<file>] --shake256 <bytes>
| Flag | Description |
|---|---|
--input, -i <file> |
The object (default: the operand, or stdin) |
--alg SHA-1|SHA-256|SHA-384|SHA-512 |
Digest (default SHA-256; SHA-1 for --key-id, as RFC 5280 §4.2.1.2 specifies) |
--separator <text> |
Between bytes (default :; "" for none) |
--case upper|lower |
Hex letter case (default upper) |
--webcrypto |
Digest with Web Crypto instead of the pure-TypeScript path (fingerprint mode only) |
--key-id |
The key identifier of the public key of a certificate, request or PUBLIC KEY |
--shake256 <bytes> |
SHAKE256 of the input, this many bytes (at most 1 MiB) |
--format, -f text|json |
Report format |
pkinative asn1 #
X.690 DER, or BER with the global --ber. decode prints the node tree like openssl asn1parse (offset, depth, lengths, tag, value), reads one node with a typed reader, or re-encodes it; encode turns a JSON node spec into DER (pkinative schema asn1-spec).
pkinative asn1 decode [<file>] [--path i.j.k] [--read <type>] [--reencode]
pkinative asn1 decode [<file>] --sequence [--allow-trailing]
pkinative asn1 encode --spec <spec.json> [--encoding der|hex|pem]
asn1 decode |
Description |
|---|---|
--input, -i <file> |
The object, DER or one PEM block (default: the operand, or stdin) |
--path <i.j.k> |
Select a node by child indices from the root; E_NOT_FOUND past a node's children |
--read <type> |
Read the node: boolean, integer, small-integer, enumerated, null, bit-string, octet-string, oid, relative-oid, string, time |
--string-type <type> |
With --read string on an IMPLICIT-tagged node: utf8, numeric, printable, teletex, ia5, visible, universal, bmp |
--time-type UTCTime|GeneralizedTime |
With --read time on an IMPLICIT-tagged node |
--reencode |
Re-encode the node (byte-identical for DER; the status says identical) |
--sequence |
Decode concatenated top-level objects |
--allow-trailing |
Ignore bytes after the first object |
--encoding pem|der|hex |
--reencode output encoding (default der) |
--label <LABEL> |
PEM label with --encoding pem |
--output, -o <file> |
--reencode output file (default stdout) |
--format, -f text|json |
Report format |
asn1 encode |
Description |
|---|---|
--spec <file> |
JSON node spec (- = stdin; or the operand). Types: boolean, integer, enumerated, null, bit-string, named-bits, octet-string, oid, relative-oid, string, time, sequence, set, set-of, explicit, implicit, tlv, der |
--encoding pem|der|hex |
Output encoding (default der) |
--label <LABEL> |
PEM label with --encoding pem |
--output, -o <file> |
Output file (default stdout) |
Certificates #
pkinative cert #
RFC 5280 certificates: read, issue, encode their building blocks, verify one signature, and check names and purposes.
pkinative cert inspect [<file>] [--extension <kind>] [--raw-extensions]
pkinative cert create --spec <spec.json> --key <key> [--issuer <ca.pem>] [--public-key <file>] [-o <file>]
pkinative cert encode <structure> --spec <spec.json>
pkinative cert encode signature-algorithm --key <key> [--rsa-scheme <scheme>]
pkinative cert decode-extension --oid <oid> --value <hex> [--critical]
pkinative cert verify-signature [<file>] [--issuer <file>]
pkinative cert check-name [<file>] --host <name> | --ip <address>
pkinative cert match-name <presented> <reference> [--no-wildcards]
pkinative cert check-purpose [<file>] --purpose <name|oid|any> [--chain <file>]...
cert inspect |
Description |
|---|---|
--input, -i <file> |
The certificate, PEM or DER (default: the operand) |
--extension <kind> |
Print one decoded extension (E_NOT_FOUND if absent). The 19 kinds: basicConstraints, keyUsage, extendedKeyUsage, subjectAltName, issuerAltName, subjectKeyIdentifier, authorityKeyIdentifier, nameConstraints, certificatePolicies, policyMappings, policyConstraints, inhibitAnyPolicy, authorityInfoAccess, subjectInfoAccess, crlDistributionPoints, freshestCRL, signedCertificateTimestampList, ocspNoCheck, subjectDirectoryAttributes |
--raw-extensions |
Keep every extension undecoded |
--format, -f text|json |
Report format |
cert create |
Description |
|---|---|
--spec <file> |
JSON spec (- = stdin): subject, issuer, serialNumber, notBefore, notAfter or validityDays, extensions (pkinative schema cert-spec). Defaults when omitted: a random 127-bit serialNumber, notBefore now, validityDays 365 |
--issuer <file> |
Issuer certificate; omitted = self-signed |
--public-key <file> |
Subject key: a certificate, request or PUBLIC KEY (default: derived from an unencrypted --key when self-signed) |
| signing flags | --key, --p12, --key-type, --hash, --rsa-scheme, --salt-length, --password-file, --password-stdin (below) |
--output, -o <file> |
Output file (default stdout) |
--encoding pem|der|hex |
Output encoding (default pem) |
The new certificate is verified against --issuer (or itself) before it is written: a key that does not belong to the issuer is refused (E_INPUT). The status envelope carries serialNumber, selfSigned and signatureVerified.
cert encode <structure> |
Description |
|---|---|
<structure> |
name, name-attribute, validity, spki, algorithm-identifier, attribute, extension, extensions, basic-constraints, key-usage, extended-key-usage, subject-alt-name, subject-key-identifier, authority-key-identifier, signature-algorithm (from the signing key) |
--spec <file> |
JSON value of the structure (- = stdin); the members of each spec: pkinative schema cert-encode-spec |
| signing flags | signature-algorithm only: the key decides the algorithm |
--output, -o <file> |
Output file (default stdout) |
--encoding pem|der|hex |
Output encoding (default hex) |
--label <LABEL> |
PEM label with --encoding pem |
cert decode-extension |
Description |
|---|---|
--oid <oid> |
The extension OID (required) |
--value <hex> |
The extnValue content |
--input, -i <file> |
… or read it from a file, DER |
--critical |
Decode as a critical extension |
--format, -f text|json |
Report format |
cert verify-signature |
Description |
|---|---|
--input, -i <file> |
The certificate (default: the operand) |
--issuer <file> |
The issuer certificate (default: the self-signature) |
--allow-algorithm-mismatch |
Accept a tbsCertificate and outer signature algorithm that differ |
--format, -f text|json |
Report format. Exit 1 (E_VERIFY_FAILED) when the signature does not verify |
cert check-name |
Description |
|---|---|
--input, -i <file> |
The certificate (default: the operand) |
--host <name> |
DNS reference identity |
--ip <address> |
IPv4 or IPv6 reference identity |
--chain <file> (repeatable) |
Issuers, for name constraints |
--allow-cn-fallback |
Accept a commonName when there is no SAN |
--no-wildcards |
Refuse wildcard names |
--format, -f text|json |
Report format. Exit 1 (E_CHECK_FAILED) on a mismatch |
cert match-name <presented> <reference> |
Description |
|---|---|
--no-wildcards |
Refuse wildcard names |
--format, -f text|json |
Report format. Exit 1 (E_CHECK_FAILED) on a mismatch |
cert check-purpose |
Description |
|---|---|
--input, -i <file> |
The certificate (default: the operand) |
--purpose <name|oid|any> |
serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, ocspSigning, any, or an OID |
--chain <file> (repeatable) |
The issuers, leaf-first order |
--no-restrict-issuers |
Do not require the purpose in issuer EKUs |
--require-explicit-purpose |
Refuse a certificate without an EKU |
--format, -f text|json |
Report format. Exit 1 (E_CHECK_FAILED) when the purpose fails |
Signing flags (cert create, cert encode signature-algorithm, csr create, cms sign). The algorithm follows the key: ECDSA on the key's curve with the curve's hash, Ed25519 and Ed448 without a hash, RSA only with an explicit scheme.
| Flag | Description |
|---|---|
--key <file> |
PKCS#8 private key, plain or PBES2-encrypted |
--p12 <file> |
PKCS#12 file holding one key (PBES2 and PBMAC1) |
--key-type <type> |
Type of an encrypted key when no certificate or public key tells it: ec-p256, ec-p384, ec-p521, ed25519, ed448, rsa, rsa-pss |
--hash SHA-256|SHA-384|SHA-512 |
Digest (default: the curve's, or SHA-256; SHA-1 only with --allow-sha1) |
--rsa-scheme pkcs1|pss |
Required for an RSA key: there is no default |
--salt-length <n> |
RSA-PSS salt length (default: the hash length) |
--password-file <file> |
Key or PKCS#12 password: the first line of the file |
--password-stdin |
… or the first line of stdin (or PKINATIVE_PASSWORD; never on the command line) |
pkinative csr #
PKCS#10 certification requests (RFC 2986).
pkinative csr inspect [<file>] [--raw-extensions]
pkinative csr create --spec <spec.json> --key <key> [--public-key <file>]
pkinative csr verify [<file>]
| Subcommand | Flags |
|---|---|
inspect |
--input, -i <file> (the request, PEM or DER; default the operand); --raw-extensions (keep requested extensions undecoded); --format, -f text|json |
create |
--spec <file> ({ "subject": {...}, "extensions": {...} }, pkinative schema csr-spec; - = stdin); --public-key <file> (the key to certify; default derived from an unencrypted --key); the signing flags; --output, -o <file>; --encoding pem|der|hex (default pem). The proof of possession is verified before the request is written |
verify |
--input, -i <file>; --format, -f. Exit 1 (E_VERIFY_FAILED) when the self-signature does not verify |
Paths & revocation #
pkinative chain #
RFC 5280 §6 certification paths. verify is the one-call verdict: path building, every signature, RFC 5280 validation, the server name (RFC 6125), the purposes and, with CRLs or OCSP responses, revocation. build finds a path without checking signatures: its verdict is the path, so valid is false with PKI_REASON_SIGNATURE_NOT_CHECKED, the envelope says signaturesChecked: false, and it exits 0 whenever a path is found, 1 when none is. validate takes the path in order and verifies each link's signature first. A negative verdict prints the report, then exits 1 (E_VERIFY_FAILED). Offline: CRLs and OCSP responses are files you fetched.
pkinative chain verify [<leaf>] --trust <roots> [--untrusted <file>]... [--host <name> | --ip <address>]
[--purpose <p>]... [--crl <file>]... [--ocsp <file>]... [--require-revocation] [--at <instant>]
pkinative chain build [<leaf>] --trust <roots> [--untrusted <file>]...
pkinative chain validate --path <leaf> --path <ca>... --trust <roots>
| Flag | verify |
build |
validate |
Description |
|---|---|---|---|---|
--input, -i <file> |
✓ | ✓ | The leaf (default: the operand) | |
--path <file> (repeatable) |
✓ | The path, leaf first (or operands) | ||
--trust <file> (repeatable) |
✓ | ✓ | ✓ | Trust anchors, PEM bundle or DER (required) |
--untrusted <file> (repeatable) |
✓ | ✓ | Intermediate candidates | |
--at <instant> |
✓ | ✓ | ✓ | Validation time: ISO 8601 (UTC unless zoned), epoch milliseconds or now (default now) |
--purpose <name|oid> (repeatable) |
✓ | ✓ | ✓ | Required extended key usage: serverAuth, clientAuth, codeSigning, … |
--policy <oid> (repeatable) |
✓ | ✓ | ✓ | Initial policy set |
--require-explicit-policy, --inhibit-policy-mapping, --inhibit-any-policy |
✓ | ✓ | ✓ | RFC 5280 §6.1.1 policy inputs |
--host <name> / --ip <address> |
✓ | The DNS name or IP address the leaf must hold | ||
--crl <file> (repeatable) |
✓ | CRLs, PEM bundle or DER | ||
--ocsp <file> (repeatable) |
✓ | OCSP responses, DER or PEM (a PEM bundle is accepted) | ||
--ocsp-nonce <hex> |
✓ | The nonce the OCSP request carried (1–32 octets) | ||
--require-ocsp-nonce |
✓ | Refuse an OCSP response without that nonce | ||
--require-revocation |
✓ | Fail when a certificate's status is unknown | ||
--no-signatures |
✓ | Skip the link signatures: a structural verdict, headed as such | ||
--format, -f text|json |
✓ | ✓ | ✓ | Report format |
The chain envelopes carry valid, pathLength and signaturesChecked; verify --summary prints valid, the reason codes, the path's subjects, explored and signatureVerifications.
pkinative crl #
RFC 5280 §5 certificate revocation lists. find reports whether a serial is listed and exits 0 either way; check is the verdict — listed, stale, wrong scope or unverified is exit 1 (E_CHECK_FAILED). With --issuer the CRL signature is verified; without it, it counts as unchecked.
pkinative crl inspect [<crl>]
pkinative crl find [<crl>] --serial <hex> | --cert <file>
pkinative crl verify-signature [<crl>] --issuer <ca>
pkinative crl check [<crl>] --cert <file> [--issuer <ca>] [--delta <crl>] [--at <instant>] [--stale-tolerance <ms>]
| Subcommand | Flags |
|---|---|
inspect |
--input, -i <file> (the CRL, PEM or DER; default the operand); --format, -f text|json |
find |
--input, -i; --serial <hex> (the serial number, hexadecimal) or --cert <file> (the certificate to look up); --format, -f |
verify-signature |
--input, -i; --issuer <file> (the CRL issuer, required); --format, -f. Exit 1 (E_VERIFY_FAILED) when it does not verify |
check |
--input, -i; --cert <file> (the certificate to check, required); --issuer <file>; --delta <file> (a delta CRL to apply on top; one without a deltaCRLIndicator is E_INPUT); --at <instant> (default now); --stale-tolerance <ms> (accept a CRL this long past nextUpdate); --format, -f |
pkinative ocsp #
RFC 6960, offline: the CLI writes the request and judges the response; sending the request is yours (curl --data-binary @req.der -H 'Content-Type: application/ocsp-request').
pkinative ocsp request --cert <file> --issuer <ca> [--hash SHA-1|SHA-256] [--nonce <hex>|random] [-o req.der]
pkinative ocsp cert-id --cert <file> --issuer <ca> [--hash SHA-1|SHA-256]
pkinative ocsp inspect [<response>]
pkinative ocsp verify-signature [<response>] [--responder <cert>]
pkinative ocsp check [<response>] --cert <file> --issuer <ca> [--responder <cert>] [--nonce <hex>] [--at <instant>]
| Subcommand | Flags |
|---|---|
request |
--cert <file> (or the operand); --issuer <file> (required); --hash SHA-1|SHA-256 (CertID hash, default SHA-1, as responders expect); --nonce <hex>|random (an RFC 8954 nonce of 1 to 32 octets, printed in the status); --output, -o <file>; --encoding pem|der|hex (default der); --label <LABEL> |
cert-id |
--cert <file> (or the operand); --issuer <file>; --hash SHA-1|SHA-256; --output, -o; --encoding (default hex); --label |
inspect |
--input, -i <file> (the response, DER or PEM; default the operand); --format, -f text|json |
verify-signature |
--input, -i; --responder <file> (default: the certificates the response embeds); --format, -f. Exit 1 (E_VERIFY_FAILED) when no responder certificate verifies it |
check |
--input, -i; --cert <file>, --issuer <file> (both required); --responder <file> (default: the embedded certificates, then the issuer); --responder-trusted (trust the responder out of band; otherwise it must be the CA or a delegate the CA issued with OCSPSigning, RFC 6960 §4.2.2.2); --hash SHA-1|SHA-256 (default: the response's own); --nonce <hex>; --require-nonce; --at <instant>; --stale-tolerance <ms>; --future-tolerance <ms> (accept a thisUpdate this far ahead, default 60000); --format, -f |
ocsp check exits 1 (E_CHECK_FAILED) unless the answer is a clean, signed, authorised good for this certificate.
Signatures & time-stamps #
pkinative cms #
CMS SignedData (RFC 5652, with RFC 5035 signing-certificate-v2 and RFC 6211 algorithm protection).
pkinative cms sign --content <file> --cert <cert> --key <key> [--detached] [--chain <file>]... [-o out.p7s]
pkinative cms verify [<p7s>] [--content <file>] --trust <roots>
pkinative cms inspect [<p7s>]
pkinative cms verify-signer [<p7s>] --cert <cert> [--signer-index <n>]
pkinative cms add-attribute [<p7s>] --attribute <der> [--signer-index <n>]
pkinative cms add-timestamp [<p7s>] --token <tst|tsr> [--signer-index <n>]
cms sign |
Description |
|---|---|
--content <file> |
The content to sign |
--content-digest <hex> |
Sign a precomputed digest instead (needs --detached) |
--cert <file> |
The signer certificate (or the one in --p12) |
--chain <file> (repeatable) |
Certificates to embed |
--crl <file> (repeatable) |
CRLs to embed |
--detached |
Leave the content out of the SignedData |
--content-type <oid> |
eContentType (default id-data) |
--sid issuer-serial|ski |
Signer identifier form (default issuer-serial) |
--signing-time <instant|now|none> |
signingTime attribute (default now) |
--no-signing-certificate |
Omit ESS signing-certificate-v2 |
--no-algorithm-protection |
Omit CMS algorithm protection |
--signed-attribute <file> (repeatable) |
Extra signed attribute, DER |
--unsigned-attribute <file> (repeatable) |
Extra unsigned attribute, DER |
| signing flags | --key, --p12, --key-type, --hash, --rsa-scheme, --salt-length, --password-file, --password-stdin |
--output, -o <file> |
Output file (default stdout) |
--encoding pem|der|hex |
Output encoding (default der; PEM label CMS) |
The new signature is verified under --cert before it is written; a key that does not belong to the certificate is E_INPUT.
cms verify |
Description |
|---|---|
--input, -i <file> |
The SignedData, PEM or DER (default: the operand) |
--content <file> / --content-digest <hex> |
The content of a detached signature, or its digest |
--trust <file> (repeatable) |
Trust anchors (required) |
--untrusted <file> (repeatable) |
Extra certificates for the chains |
--purpose <name|oid> (repeatable) |
Required extended key usage |
--crl <file>, --ocsp <file> (repeatable), --require-revocation |
Revocation evidence |
--at <instant> |
Validation time (default now) |
--at-timestamp |
Validate at the signer's verified timestamp |
--require-signing-certificate, --require-algorithm-protection |
Demand those signed attributes |
--allow-trailing |
Accept bytes after the SignedData |
--format, -f text|json |
Report format. Exit 1 (E_VERIFY_FAILED) unless every signer verifies |
| Subcommand | Flags |
|---|---|
inspect |
--input, -i <file>; --allow-trailing; --format, -f text|json |
verify-signer |
--input, -i; --cert <file> (the signer certificate, required); --signer-index <n> (default 0); --content <file> (of a detached signature); --format, -f. A key check on one signer, not the verdict |
add-attribute |
--input, -i; --signer-index <n>; --attribute <file> (one Attribute, DER, required); --output, -o; --encoding (default der) |
add-timestamp |
--input, -i; --signer-index <n>; --token <file> (a TimeStampToken or TimeStampResp, required); --output, -o; --encoding (default der) |
A counter timestamp stamps the signer's signature value, not the content: take it from cms inspect --fields signerInfos.signature, hash it, and request the timestamp over that digest (tsp request --digest). add-timestamp refuses a token over anything else before writing a byte (E_INPUT, PKI_REASON_TSP_IMPRINT_MISMATCH in error.reasons, the recipe as remedy).
pkinative tsp #
RFC 3161 timestamping, offline: the CLI writes the request and judges the answer; posting it to a TSA is yours (curl --data-binary @req.tsq -H 'Content-Type: application/timestamp-query').
pkinative tsp request --data <file> | --digest <hex> [--hash SHA-256] [--nonce <n>|random] [--policy <oid>] [-o req.tsq]
pkinative tsp inspect [<file>] [--as response|token|tstinfo]
pkinative tsp verify --response <tsr> | --token <tst> --trust <roots> (--request <tsq> | --data <file> | --digest <hex>)
| Subcommand | Flags |
|---|---|
request |
--data <file> (hashed with --hash) or --digest <hex>; --hash SHA-256|SHA-384|SHA-512 (imprint hash, default SHA-256); --nonce <n>|random (decimal, 0x-hex, or 63 random bits; printed in the status); --policy <oid>; --no-cert-req (do not ask the TSA to embed its certificate); --output, -o <file>; --encoding pem|der|hex (default der); --label <LABEL> |
inspect |
--input, -i <file> (default the operand); --as response|token|tstinfo (skip the detection); --format, -f text|json |
verify |
--response <file> (or the operand) or --token <file>; --request <file> (nonce and imprint must match it), --data <file> or --digest <hex>; --trust <file> (repeatable, required); --untrusted <file> (repeatable); --crl <file>, --ocsp <file> (repeatable), --require-revocation; --at <instant>; --allow-noncritical-eku (accept a TSA whose timeStamping EKU is not critical); --format, -f. Exit 1 (E_VERIFY_FAILED) unless the timestamp verifies |
Keys #
pkinative key #
PKCS#8 private keys (RFC 5958, PBES2 per RFC 8018). inspect describes a key — algorithm, curve, attributes, or the PBES2 parameters of an encrypted key — without a password; check imports or decrypts it into a non-extractable Web Crypto key to prove it is usable. No report ever contains a key byte.
pkinative key inspect [<file>]
pkinative key check [<file>] [--key-type <type>] [--rsa-scheme pkcs1|pss]
| Subcommand | Flags |
|---|---|
inspect |
--input, -i <file> (the key, PEM or DER; default the operand); --format, -f text|json |
check |
--input, -i; --key-type <type> (of an encrypted key: ec-p256, ec-p384, ec-p521, ed25519, ed448, rsa, rsa-pss); --hash SHA-256|SHA-384|SHA-512 (the digest the key will sign with); --rsa-scheme pkcs1|pss (required for an RSA key); --salt-length <n>; --password-file <file>; --password-stdin; --format, -f |
pkinative p12 #
PKCS#12 (RFC 7292) under PBES2 and PBMAC1 (RFC 9579) only. inspect needs no password; the others read it from --password-file, --password-stdin or PKINATIVE_PASSWORD. The legacy RC2 and 3DES ciphers are refused by doctrine (E_SECURITY, with the two-step OpenSSL conversion as remedy). A MAC keyed with the RFC 7292 Appendix B KDF is never checked: verify-mac refuses it (E_SECURITY, PKI_KEY_MAC_UNSUPPORTED), and open reports it as unverified integrity (E_VERIFY_FAILED) unless --allow-unverified-integrity is given. No report ever contains a key byte.
pkinative p12 inspect [<file>]
pkinative p12 verify-mac [<file>]
pkinative p12 bags [<file>]
pkinative p12 open [<file>] [--certs-out <bundle.pem>] [--rsa-scheme pkcs1|pss]
| Subcommand | Flags |
|---|---|
inspect |
--input, -i <file> (default the operand); --format, -f text|json |
verify-mac |
--input, -i; --password-file <file>; --password-stdin; --format, -f. Exit 1: E_PASSWORD when the MAC does not match (a wrong password or an altered file), E_VERIFY_FAILED when the file carries no MAC |
bags |
--input, -i; --password-file; --password-stdin; --format, -f. Lists every bag, decrypting the encrypted contents |
open |
--input, -i; --password-file; --password-stdin; --allow-unverified-integrity (accept a file whose MAC cannot be checked); --rsa-scheme pkcs1|pss (import an RSA key for this scheme); --hash SHA-256|SHA-384|SHA-512 (the RSA key's digest, default SHA-256); --certs-out <file> (write the certificates and CRLs as PEM, only from a file that opened); --format, -f. Exit 0 only when everything opened; otherwise exit 1 — E_PASSWORD on a MAC mismatch, E_VERIFY_FAILED when the MAC cannot be checked, E_SECURITY for a legacy cipher — except an RSA key without --rsa-scheme: E_USAGE, exit 2 |
Meta #
| Command | Flags | Does |
|---|---|---|
pkinative doctor |
--format, -f text|json |
Offline preflight: the Node.js security floor, the installed pkinative against the range the CLI was built for, and whether Web Crypto can verify, sign and decrypt. Exit 1 (E_CHECK_FAILED) when a check fails |
pkinative limits |
--format, -f text|json (and any --max-*) |
The 22 pkinative bounds with the flag that raises each, its default, the effective value under the --max-* flags given and its CWE, plus the CLI's --max-content-size; --format json adds what each guards |
pkinative explain [<code>] |
--list; --kind error|reason|diagnostic|cli (with --list); --format, -f text|json |
Explains a code; E_NOT_FOUND for an unknown one |
pkinative schema [<subject>] |
— (up to three operands) | The schema subjects |
pkinative completion <shell> |
— | A completion script for bash, zsh, fish or powershell |
Global options #
| Flag | Description |
|---|---|
--help, -h |
Global or per-command help, on stdout |
--version, -V |
The CLI version; with --json, { name, version, pkinative } |
--json |
Agent mode: compact JSON report on stdout, one JSON envelope on stderr (also PKINATIVE_JSON=1) |
--pretty |
Indent JSON output |
--fields <a,b.c> |
Keep only these dot-paths of a JSON report |
--summary |
Print the command's minimal JSON shape |
--quiet, -q |
No notes or diagnostics on stderr; errors and envelopes still print (also PKINATIVE_QUIET=1) |
--no-color |
No ANSI colour; colour is also off under NO_COLOR, TERM=dumb or a pipe, and forced by FORCE_COLOR |
--dry-run |
Validate everything, write nothing (the status says dryRun: true) |
--strict |
Escalate the first engine warning to E_CHECK_FAILED |
--ber |
Accept BER as well as DER (common in CMS) |
--pem-mode strict|lax |
PEM reading: RFC 7468 strict (default) or lax |
--allow-sha1 |
Accept or produce SHA-1 signatures (legacy material you trust) |
--overwrite |
Replace an existing output file, atomically |
--config <file> |
Use this .pkinativerc.json (default: the nearest one upward) |
--no-config |
Ignore any .pkinativerc.json |
--max-content-size <size> |
The CLI's own bound on content read whole — content to sign, verify or timestamp, and JSON specs (default 1 GiB) |
--max-<limit> <n> |
The 22 pkinative limits (below) |
The 22 --max-* flags are the keys of pkinative's DEFAULT_PKI_LIMITS in kebab case: --max-input-bytes (64 MiB, read before a byte is buffered), --max-depth, --max-nodes, --max-integer-bytes, --max-oid-bytes, --max-ber-segments, --max-pem-blocks, --max-extensions, --max-general-names, --max-name-attributes, --max-policies, --max-chain-length, --max-policy-nodes, --max-revoked-certificates, --max-ocsp-single-responses, --max-paths-explored, --max-signer-infos, --max-attributes, --max-cms-certificates-and-crls, --max-kdf-iterations, --max-pkcs12-bags, --max-pkcs12-kdf-iterations. A byte bound accepts a binary suffix (65536, 64k, 16MiB, 1g); a count bound takes digits only. Every value is a positive integer: pkinative refuses an unbounded limit, and so does the CLI. pkinative limits prints each bound with its default and CWE; the security guide explains what each one guards.
Configuration file #
.pkinativerc.json is found by walking upward from the working directory, the nearest file winning, unless --config <file> names one or --no-config disables the mechanism. It is presentation only: a file planted in a cloned repository must never change what a command reads, trusts, when it judges, or where it writes. So it may set nine keys and nothing else — json, pretty, quiet, no-color, format, encoding, fields, summary, strict (which only tightens) — and any other key, present or future, is E_USAGE: an input, a trust anchor, the validation time, a tolerance, a bound, an output path or a password source can be set on the command line only.
A top-level key applies to every command; a key naming a command ("cert") or a subcommand ("cert inspect") holds a section, and the most specific section wins. A key applies only where the invoked subcommand declares the flag, and an explicit flag always beats the file. Switches take true or false, the three value keys take a string; a wrong type, a section naming no subcommand, __proto__, constructor, prototype, invalid JSON, a non-object or a file above 1 MiB is E_USAGE. The file that supplied defaults is named in the envelope (config) and in a note on stderr.
{
"no-color": true,
"cert inspect": { "format": "json", "fields": "subject,validity" },
"cms sign": { "encoding": "pem" }
}
pkinative schema config prints the file's JSON Schema.
Environment #
| Variable | Effect |
|---|---|
PKINATIVE_JSON=1 |
Same as --json |
PKINATIVE_QUIET=1 |
Same as --quiet |
PKINATIVE_PASSWORD |
The third password source, beside --password-file and --password-stdin; giving two sources is E_USAGE |
PKINATIVE_DEBUG=1 |
Adds the stack trace of a failure to stderr |
NO_COLOR / FORCE_COLOR / TERM=dumb |
Colour of the text report, which is otherwise on when stdout is a terminal (--no-color also turns it off) |
The recommended loop for an agent: run pkinative doctor --json once per environment; run the command with --json (and --summary when only the verdict matters); on exit 1 read error.code, then error.pkiCode and error.reasons, and apply error.remedy only when the input is trusted and the policy allows it; on exit 2 fix the invocation and never retry it unchanged.
Security posture #
- Offline, always. No module of the CLI opens a socket or makes a request;
node:netis imported only to classify an IP address. It never fetches a CRL, an OCSP response, a timestamp, an intermediate or an AIA URL: revocation evidence and timestamps are files you supply, andocsp requestandtsp requestwrite the requests you send yourself. There is no SSRF surface and no verdict that changes with the network. - No key generation, no key export. Bring keys from your HSM, KMS or
openssl genpkey; the CLI imports them into non-extractable Web Crypto keys. There is no PKCS#8 or PKCS#12 writer, andkeyandp12reports are allow-list views that never contain a key byte. - No secret on argv.
--password,--pass,--passinand--key-passwordare refused (exit 2) before any command runs, because argv is readable by other users and lands in shell history. The sources are--password-file <file>(first line),--password-stdinandPKINATIVE_PASSWORD; a password is never written to stdout, stderr or an envelope, and an argument mistakenly typed after--password-stdinis never echoed. - Doctrine kept, never relaxed by configuration. No SHA-1 signature made or accepted without
--allow-sha1; no RC2 or 3DES PKCS#12 (E_SECURITY, and no flag lifts it); an RFC 7292 Appendix B MAC is never checked, andp12 openaccepts such a file only under--allow-unverified-integrity, a decision about its origin; no default RSA signature scheme (--rsa-schemeis required). A created certificate, request or SignedData is verified before it is written. - Every read is bounded. PKI inputs by
--max-input-bytesbefore the file is read, stdin as it streams; content and JSON specs by--max-content-size; theasn1 encodespec is depth-bounded bymaxDepth(--max-depth). Unknown flags are refused, so a misspelt security flag is an error rather than a silent no-op. - Writes are exclusive or atomic. Without
--overwrite, an existing output path — a dangling symlink included — is refused (E_IO); with it, the bytes go to a temporary file in the same directory and a rename replaces the target, never following a link. A file being written when SIGINT or SIGTERM arrives is removed.
The CLI repository's SECURITY.md and threat model hold the full posture; the security guide covers the engine underneath.
Shell completion #
Completion scripts are generated from the command registry: commands and subcommands after global flags, flags, the values of enumerated flags under every spelling, paths after a file flag, and the shell names after completion.
pkinative completion bash > /etc/bash_completion.d/pkinative
pkinative completion zsh > "${fpath[1]}/_pkinative"
pkinative completion fish > ~/.config/fish/completions/pkinative.fish
pkinative completion powershell >> $PROFILE
Conformance #
The CLI computes no verdict of its own, so the engine's conformance — x509-limbo, NIST PKITS, the pinned RFC texts and the OpenSSL and Windows interop matrix — is the CLI's; the conformance guide carries the figures. What the CLI repository adds is proof that nothing is lost on the way through the process boundary:
- Parity with the library. Parity tests compare the CLI's JSON output with the library's own result for
cert inspect,csr inspect,crl inspectandfind,ocsp inspect,cms inspectandverify,tsp inspect,chain verify,asn1 decode,pem decode,oid listandfingerprint. - OpenSSL reads what the CLI writes. An interop suite has OpenSSL accept the certificates and requests the CLI creates (ECDSA, RSA PKCS#1 v1.5, RSA-PSS, Ed25519), verify its CMS signatures and read its OCSP and timestamp requests; it is required in the publish gate.
- Pinned samples. 57 runnable samples, each as a
.shand a.ps1script, cover every invocation butdoctor, and their output is pinned by hash. - No unmapped failure. A seeded fuzz suite holds that no argv or input reaches
E_RUNTIME, and coverage is 100 % on statements, branches, functions and lines.
Not supported #
| Not supported | Instead |
|---|---|
| Network access | None, in any mode: you fetch CRLs, OCSP responses, timestamps and intermediates |
| Key generation, key export | Bring keys from an HSM, a KMS or openssl genpkey |
| Writing a PKCS#8 or PKCS#12 | Use OpenSSL; the CLI opens and verifies them |
| Legacy PKCS#12 ciphers (RC2, 3DES) and PBES1 keys; checking an RFC 7292 Appendix B MAC | Refused by doctrine; the remedy gives the one-time OpenSSL conversion (a file with an Appendix B MAC opens, unverified, under --allow-unverified-integrity) |
| An RSA signature without a named scheme | --rsa-scheme pkcs1|pss is required |
| SHA-1 signatures by default | --allow-sha1, for legacy material you already trust |
| A password on the command line | --password-file, --password-stdin or PKINATIVE_PASSWORD |
| An unbounded limit | Every --max-* value is a positive integer |
| Raising a bound or naming a trust anchor from a config file | Command line only |
The engine's own refusals — DSA verification, ETSI long-term signature formats, names compared by §7.1 preparation — are listed in the choosing guide.
Versioning #
pkinative-cli follows Semantic Versioning, and its agent contract is stable across 1.x: the commands and their flags, the exit codes, the 13 E_* classes and the PKI_* → E_* mapping, the envelope fields, the schema subjects and the configuration keys; fields are only ever added. pkinative-cli 1.0.0, released on 2026-10-08, pins pkinative ^1.0.0; security fixes land on the latest 1.x minor, for as long as the engine's 1.x line is supported.
Further reading #
- CLI command builder — assemble a command line, checked against the CLI's refusals, and copy it.
- CLI use cases — a CI chain gate, offline revocation, a signed and timestamped release, a PKCS#12 audit.
- MCP guide — the same engine for AI assistants.
- Choosing a library and a surface — library, CLI or MCP server, per capability.
- Errors, diagnostics and reasons · Security model · Conformance.
- The CLI's README, its agent contract, its threat model and its llms.txt.