关于 pkinative MCP — X.509, CMS, OCSP, time-stamps and PKCS#12 without a key byte leaving
pkinative-mcp 是面向需要检查、验证或创建证书相关数据的开发者和安全团队的 PKI 服务。其 TypeScript 引擎不访问网络,也不会返回私钥字节。
主要功能
- 01检查 X.509 证书和 CSR
- 02验证证书路径与吊销状态
- 03创建证书、CSR 和签名
- 04编解码 ASN.1、PEM 和 OID
- 05安全打开 PKCS#8 与 PKCS#12 密钥
适用场景
- 01检查证书链和主机名身份
- 02检查 CRL 与 OCSP 响应
- 03创建证书请求和时间戳请求
- 04排查 PKI 问题时解码 ASN.1 结构
MCP 相关
向 MCP 客户端提供 34 个 PKI 工具和 7 个提示词,用于证书、CSR、CRL、OCSP、CMS、时间戳及编码操作。服务不访问网络,也不返回私钥字节。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y pkinative-mcpPKINATIVE_MCP_OUTPUT_DIRAbsolute path of the one sandbox directory: path inputs are read from it and outputMode='file' outputs are written under it (never overwritten; the real path of every file read and of every parent written must stay inside — a planted symlink or junction is SECURITY_VIOLATION). Key-bearing files (.key .p8 .p12 .pfx) are read by the key tools only, never written, never served as resources. Unset: every tool works on inline PEM / base64 only.PKINATIVE_MCP_CACHE_DIROpt-in content-addressed response cache directory (SHA-256 keyed, 1h TTL, 256 MiB LRU, plaintext at rest). Never caches path inputs, file output, a call carrying a password or a signer, a clock-dependent instant, a random serial or nonce, describe_engine or draft_governance_issue.PKINATIVE_MCP_PORTServe Streamable HTTP on this loopback port (POST /mcp) instead of stdio (MCP 2026-07-28, stateless, legacy fallback). Unauthenticated unless PKINATIVE_MCP_HTTP_TOKEN is set.PKINATIVE_MCP_HTTP_TOKEN密钥Opt-in bearer token for the HTTP transport (>= 16 chars, no whitespace; a weaker value refuses to start). When set, /mcp requires 'Authorization: Bearer <token>' or answers 401. Compared constant-time, never logged.PKINATIVE_MCP_PASSWORD密钥Operator-side password of the encrypted PKCS#8 key or PKCS#12 file the tools open (keeps the secret out of the model transcript). Exclusive with the per-call password input (both at once is VALIDATION_ERROR). Never logged; only its presence is reported at startup.PKINATIVE_MCP_ATPinned instant (ISO 8601 or epoch milliseconds) every omitted at / notBefore / signingTime falls back to, for reproducible verdicts and artefacts. Unset: the wall clock (a clock-dependent result is never cached). An invalid value refuses to start.PKINATIVE_MCP_MAX_INPUT_BYTESOperator ceiling for limits.maxInputBytes and the decoded size of one PKI object (default 67108864 = 64 MiB, the engine default). Integer >= 1024, read once at startup; an invalid value refuses to start. A per-call value above it is refused with LIMIT_CEILING_EXCEEDED.PKINATIVE_MCP_MAX_KDF_ITERATIONSOperator ceiling for limits.maxKdfIterations and limits.maxPkcs12KdfIterations (default 10000000, the engine default): the CPU bound of a hostile PBES2 key or PKCS#12 file. Integer >= 1, read once at startup; an invalid value refuses to start.PKINATIVE_MCP_MAX_PATHS_EXPLOREDOperator ceiling for limits.maxPathsExplored (default 1000, the engine default): the RFC 5280 section 6 path-building work bound. Integer >= 1, read once at startup; an invalid value refuses to start.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
34收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| mode | enum | 是 | 'decode' 会列出 PEM 文本中的块(并通过 extractIndex 提取一个块);'encode' 会将 DER 字节封装为一个 PEM 块。decode | encode |
| input | object | 是 | PEM 文本(decode:pem 或 PEM 文件路径)或 DER 字节(encode:derBase64 或 DER 文件路径):pem / derBase64 / path 中必须且只能指定一个。 |
| input.pem | string | 否 | RFC 7468 PEM 文本(以 -----BEGIN 开头)。 |
| input.path | string | 否 | PKINATIVE_MCP_OUTPUT_DIR 沙盒内 PEM / DER 文件的相对路径(从不缓存)。 |
| input.derBase64 | string | 否 | 以 base64 表示的 DER 字节,只能指定一次(不能是 PEM 文本)。 |
| label | string | 否 | decode:仅列出具有此标签的块。encode:块的标签(必填,例如 'CERTIFICATE')。由大写字母和数字组成,单词之间以单个空格分隔;私钥标签会被拒绝(SECURITY_VIOLATION)。 |
| fields | array | 否 | 在 verbosity 处理后保留的点路径(数组会映射到各元素);不匹配的路径会放入 _meta.unmatchedFields。 |
| limits | object | 否 | 提高本次调用中 pkinative 的 22 项命名安全限制(仅限可信输入;未提供的键保留引擎默认值)。高于操作员上限(PKINATIVE_MCP_MAX_INPUT_BYTES、PKINATIVE_MCP_MAX_KDF_ITERATIONS、PKINATIVE_MCP_MAX_PATHS_EXPLORED)的值会触发 LIMIT_CEILING_EXCEEDED;describe_engine 会列出默认值和上限。 |
| limits.maxDepth | integer | 否 | 构造值的嵌套深度。默认值为 64。 |
| limits.maxNodes | integer | 否 | 从单个输入中解码的值数量。默认值为 200000。 |
| limits.maxOidBytes | integer | 否 | 单个 OBJECT IDENTIFIER 的内容长度。默认值为 256。 |
| limits.maxPolicies | integer | 否 | 单个扩展中的策略或策略映射数量。默认值为 1024。 |
| limits.maxPemBlocks | integer | 否 | 从单个 PEM 文本中读取的块数。默认值为 10000。 |
| limits.maxAttributes | integer | 否 | 一个属性集中的属性。默认值为 256。 |
| limits.maxExtensions | integer | 否 | 一个证书或请求中的扩展。默认值为 256。 |
| limits.maxInputBytes | integer | 否 | 单个 DER 输入的大小,或单个 PEM 文本的长度。默认值为 67108864。 |
| limits.maxPkcs12Bags | integer | 否 | 从单个 PKCS#12 中读取的 SafeBags 数量,涵盖所有 SafeContents。默认值为 4096。 |
| limits.maxBerSegments | integer | 否 | 从单个 BER 构造字符串中拼接的分段数。默认值为 10000。 |
| limits.maxChainLength | integer | 否 | 单条认证路径可包含的证书数。默认值为 10。 |
| limits.maxPolicyNodes | integer | 否 | RFC 5280 valid_policy_tree 中的活动节点数。默认值为 4096。 |
| limits.maxSignerInfos | integer | 否 | 单个 SignedData 中的签名者数。默认值为 64。 |
| limits.maxGeneralNames | integer | 否 | 单个字段中的 GeneralName 条目数。默认值为 10000。 |
| limits.maxIntegerBytes | integer | 否 | 转换为 bigint 的单个 INTEGER 的内容长度。默认值为 8192。 |
| limits.maxKdfIterations | integer | 否 | 单次派生中执行的 PBKDF2 迭代次数。默认值为 10000000。 |
| limits.maxPathsExplored | integer | 否 | 构建单个路径时探索的候选路径数(RFC 5280 §6 的工作量上限)。默认值为 1000。 |
| limits.maxNameAttributes | integer | 否 | 单个专有名称中的属性数。默认值为 1024。 |
| limits.maxOcspSingleResponses | integer | 否 | 单个 OCSP 响应中的 SingleResponse 条目数。默认值为 256。 |
| limits.maxPkcs12KdfIterations | integer | 否 | 单个 PKCS#12 的 PBKDF2 总迭代次数。默认值为 10000000。 |
| limits.maxRevokedCertificates | integer | 否 | 遍历单个 CRL revokedCertificates 列表中的条目数。默认值为 1000000。 |
| limits.maxCmsCertificatesAndCrls | integer | 否 | 单个 SignedData 携带的证书和吊销条目数。默认值为 1024。 |
| strict | boolean | 否 | 将引擎发出的第一条警告升级为 PKI_STRICT_DIAGNOSTIC 错误,而不是将其列入 diagnostics[]。 |
| pemMode | enum | 否 | 'strict'(严格遵循 RFC 7468,默认)或 'lax'(容忍空白、行长度和 RFC 1421 标头)。strict | lax |
| encoding | enum | 否 | 工件编码。decode + extractIndex:'der'(默认,原始字节)、'pem'(严格重新输出的块)或 'hex'(仅内联,≤ 64 KiB)。encode:始终为 'pem'(其他值均为 VALIDATION_ERROR)。pem | der | hex |
| verbosity | enum | 否 | 'summary' 仅保留标量字段(节省 Token)。full | summary |
| outputMode | enum | 否 | 'base64'(默认)以内嵌资源形式直接返回产物(≤ 50 MiB)。'file' 将其写入 PKINATIVE_MCP_OUTPUT_DIR 内(不会覆盖;≤ 1 GiB),并返回 resource_link。base64 | file |
| outputPath | string | 否 | 沙箱内的相对路径(仅当 outputMode='file' 时);扩展名必须与编码匹配(der 使用 .der .crt .cer .csr .crl .p7s .bin …;pem 使用 .pem)。拒绝带密钥的扩展名(.key .p8 .p12 .pfx):此处不会写入私钥。 |
| extractIndex | integer | 否 | decode:还返回文本中列出的、索引位置对应的块,作为 `encoding` 格式的产物。无法提取私钥块(SECURITY_VIOLATION)。 |
| encodingRules | enum | 否 | 'der'(严格 X.690 DER,默认)或 'ber'(允许不定长/非最短长度及构造字符串,适用于旧版 CMS 生成器的输出)。der | ber |
详细介绍
pkinative-mcp
用于 PKI 的 MCP 服务 — 检查并验证 X.509 证书、CSR、CRL、OCSP 响应、CMS SignedData 和 RFC 3161 时间戳;构建并验证证书链(RFC 5280 路径验证、名称和用途检查、离线 CRL 与 OCSP 吊销检查);创建证书、CSR、CMS 签名、OCSP 和时间戳请求;打开 PKCS#8 和 PKCS#12,且绝不返回密钥字节;解码 ASN.1、PEM 和 OID — 基于 pkinative 引擎提供 34 个工具和 7 个提示词(零依赖、纯 TypeScript,所有签名均使用 Web Crypto;CI 中已验证与 OpenSSL 3 的互操作性),适用于 Claude Desktop、Cursor、ChatGPT 及任何 Model Context Protocol 客户端。绝不联网。
✨ 功能
pkinative-mcp 向任何 MCP 宿主提供 34 个工具,分为六组。类型说明结果的形式:读取工具返回事实(structuredContent,可通过 verbosity / fields 投影);判定工具返回 valid + reasons[],且对于否定判定,绝不会设置 isError;产物工具交付 DER 或 PEM,一次性内联返回或写入沙盒。最后一列列出了 Agent 可据此分支处理的拒绝代码(完整表格见 docs/AGENT_CONTRACT.md)。
编码
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
convert_pem | 读取 | mode: 'decode' 会列出 PEM 文本中的每个区块(标签、DER 大小、偏移量、SHA-256),extractIndex 会将其中一个区块输出为 DER / PEM / 十六进制;mode: 'encode' 会将且仅将一个 DER 值封装为一个严格符合 RFC 7468 的区块,并使用 label 作为标签。会列出私钥区块,但绝不提取或生成;如果输入的任何位置包含密钥材料,都会拒绝处理。 | SECURITY_VIOLATION、NOT_FOUND、VALIDATION_ERROR、PKI_PEM_UNEXPECTED_LABEL、PKI_PEM_* |
resolve_oid | 读取 | 一次调用即可处理对象标识符:name(注册名称,每次调用 ≤ 1000 个)、validate(X.660 语法,仅报告错误,不会抛出)、list(引擎注册表,分页大小为 200 / 2000)、encode(点分格式 → 十六进制,内容或完整 TLV,RELATIVE-OID)、decode(十六进制 → 点分格式 + 名称)。 | VALIDATION_ERROR、PKI_OID_INVALID、PKI_ASN1_* |
compute_fingerprint | 读取 | 对精确 DER 计算 fingerprint(默认 SHA-256;也支持 SHA-1 / 384 / 512)、计算 key-id(证书、CSR 或 PUBLIC KEY 中公钥的 RFC 5280 §4.2.1.2 标识符)、计算 shake256(outputLength 字节,即 Ed448 CMS 摘要)。可以计算任意对象的摘要;不会返回任何输入字节。 | VALIDATION_ERROR、PKI_X509_*、PKI_ASN1_* |
decode_asn1 | 读取 | 输出任意 DER 的 X.690 节点树(通过 encodingRules: 'ber' 支持 BER):偏移量、标签、预览、OID 名称,最多 20 000 个节点;nodePath 可缩小到某个子树,read 可应用类型化读取器,reencode: true 可将节点重新编码为工件。拒绝处理私钥或 PKCS#12(依据标签判断,或当 DER 可解析为其中一种时)。 | SECURITY_VIOLATION、NOT_FOUND、PKI_ASN1_UNEXPECTED_TAG、PKI_API_MISUSE、PKI_ASN1_* |
encode_asn1 | 工件 | 将 JSON 节点 spec(boolean、integer、bit-string、octet-string、oid、string、time、sequence / set / set-of、explicit / implicit、tlv、der)通过引擎自身的编码器确定性地编码为 DER,是 decode_asn1 的逆操作。 | VALIDATION_ERROR(指出 JSON 路径)、SECURITY_VIOLATION、PKI_LIMIT_EXCEEDED、PKI_ASN1_*、PKI_OID_INVALID |
证书与请求
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
inspect_certificate | read | 单个证书的 RFC 5280 报告:序列号、算法、颁发者 / 主题(RFC 4514 + RDN)、有效期、公钥、解码后的 19 种扩展类型(rawExtensions: true 会保留原始内容)、指纹、诊断信息;extension 返回一项扩展;issuer / checkSelfSignature 会额外进行一次签名检查——这并不代表信任。 | NOT_FOUND、PKI_PEM_UNEXPECTED_LABEL、PKI_X509_*、PKI_CRYPTO_ALGORITHM_REFUSED |
create_certificate | artefact | 根据 spec(主题、有效期、扩展)签发 X.509 v3 证书,由 signer(PKCS#8 密钥或导入为不可提取 Web Crypto 密钥的 PKCS#12 文件)签名:可创建自签名 CA 证书,也可从证书 / CSR / PUBLIC KEY 签发叶证书。序列号默认是 'random';在返回字节之前会针对颁发者进行验证。 | KEY_MISMATCH、RSA_SCHEME_REQUIRED、KEY_TYPE_REQUIRED、SECRET_REQUIRED、UNSUPPORTED_KEY、VALIDATION_ERROR |
encode_x509_structure | artefact | 对一个 X.509 构件(名称、有效期、spki、algorithm-identifier、扩展、basic-constraints、key-usage、extended-key-usage、subject-alt-name、SKI / AKI、signature-algorithm)根据封闭的 spec 进行确定性编码;默认输出十六进制。此处不会进行签名。 | VALIDATION_ERROR(指出成员名称)、KEY_TYPE_REQUIRED、SECRET_REQUIRED、RSA_SCHEME_REQUIRED |
decode_extension | read | 按照 inspect_certificate 的方式,根据 OID 解码一个 extnValue 内容(十六进制或 DER);会根据 RFC 5280 对该类型的规则检查 critical(仅生成诊断信息,不会拒绝)。 | PKI_X509_EXTENSION_MALFORMED、VALIDATION_ERROR |
check_certificate_name | verdict | RFC 6125 身份判定:对 certificate + host / ip(SAN dNSName / iPAddress、通配符、受链名称约束限制的可选 CN 回退)进行检查;或者使用不带证书的匹配形式 presented + reference。 | PKI_REASON_NAME_MISMATCH(位于 reasons[] 中)、VALIDATION_ERROR |
check_certificate_purpose | verdict | certificate 是否可沿 [certificate, ...chain] 用于 purpose(serverAuth、clientAuth、codeSigning、emailProtection、timeStamping、ocspSigning、any 或 OID)?restrictIssuers(默认 true)会将 CA 自身的 EKU 应用于其签发的证书。 | PKI_REASON_PURPOSE_NOT_PERMITTED(位于 reasons[] 中)、VALIDATION_ERROR |
inspect_csr | read | 单个 PKCS#10 请求的 RFC 2986 报告:主题、密钥、属性、请求的扩展、是否存在 challengePassword(绝不显示其文本),以及默认包含的持有证明(signature.valid)。 | PKI_REASON_SIGNATURE_NOT_CHECKED(位于 reasons[] 中)、PKI_X509_*、PKI_PEM_UNEXPECTED_LABEL |
create_csr | artefact | 根据 spec(主题、要请求的扩展)创建 PKCS#10 请求,并由 signer 签名;公钥从普通密钥派生、取自 PKCS#12 证书,或由 subjectPublicKey 提供;在返回字节之前会验证持有证明。 | KEY_MISMATCH、RSA_SCHEME_REQUIRED、SECRET_REQUIRED、KEY_TYPE_REQUIRED、VALIDATION_ERROR |
路径、CRL 和 OCSP
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
verify_certificate_chain | 判定 | 一次调用即可得出信任判定:从 leaf 出发,经由 candidates 到达你的某个 trustAnchors 的路径;验证每个签名;在 at 时刻按 RFC 5280 §6 进行验证(有效期、约束、密钥用途、名称、策略);检查 serverName(RFC 6125)和 purposes;并根据你提供的 CRL / OCSP 字节检查吊销状态(requireRevocation 要求提供终端实体证书的吊销证据)。 | reasons[] 中的 PKI_REASON_*(NO_TRUST_ANCHOR、EXPIRED、NOT_YET_VALID、REVOKED、REVOCATION_UNKNOWN、NAME_MISMATCH,……)、VALIDATION_ERROR |
build_certificate_path | 判定 | 仅发现路径:根据 at 时刻的结构规则,确定哪些中间证书可以从 leaf 到达某个信任锚。不会检查签名——valid 始终为 false,判定结果为 found 和 structuralReasons。 | PKI_REASON_SIGNATURE_NOT_CHECKED(始终存在)、PKI_REASON_NO_TRUST_ANCHOR |
validate_certificate_path | 判定 | 在 at 时刻,根据你的信任锚,按 RFC 5280 §6.1 验证由你排序的路径(叶证书在前);先验证每个链接的签名(checkSignatures: false 会给出包含 failingReasons 的结构性判定)。此处不检查吊销状态。 | PKI_REASON_SIGNATURE_INVALID、PKI_REASON_SIGNATURE_NOT_CHECKED、VALIDATION_ERROR |
inspect_crl | 读取 | 根据 RFC 5280 §5 生成单个 CRL 的报告:颁发者、更新时间、CRL 编号、增量指示符、IDP、条目数(逐项遍历,绝不会全部解码)和扩展;find 会查找一个序列号;issuer 会额外进行一次签名检查。 | VALIDATION_ERROR、PKI_X509_*、PKI_CRYPTO_ALGORITHM_REFUSED |
check_crl_revocation | 判定 | 在 at 时刻,根据你获取的一个 certificate 和一个基础 crl(以及可选的 delta)检查吊销状态:范围、新鲜度(staleTolerance)、根据 issuer 验证签名,以及查找序列号。如果 delta 的签名无法根据 issuer 验证,则不会应用该 delta:其结果为 valid: false,并在 delta 上给出 PKI_REASON_REVOCATION_UNKNOWN。 | reasons[] 中的 PKI_REASON_REVOKED、PKI_REASON_REVOCATION_UNKNOWN,以及 VALIDATION_ERROR(delta 缺少 deltaCRLIndicator 时) |
create_ocsp_request | 工件 | 为 certificate 和 issuer 生成 DER 格式的 OCSPRequest(RFC 6960):CertID 使用 SHA-1(默认)或 SHA-256,支持 RFC 8954 nonce('random' 或十六进制),以及 certIdOnly。由你将字节 POST 给响应方;summary.nonceHex / certIdHex 与响应相匹配。 | VALIDATION_ERROR |
inspect_ocsp_response | 读取 | 根据 RFC 6960 生成单个响应的报告:状态、响应方 ID、producedAt、每个 SingleResponse(CertID、状态、更新时间)、扩展、嵌入的证书和 nonce 是否存在;responder 会额外进行一次签名检查。 | PKI_*(PKI_ASN1_*、PKI_X509_*)、VALIDATION_ERROR |
check_ocsp_status | 判定 | 检查一个 certificate 对应的一个 response:根据 issuer 重新计算 CertID,验证签名(使用 responder、嵌入的证书或颁发者),确认签名者已获授权(RFC 6960 §4.2.2.2),并检查状态、at 时刻的新鲜度和 nonce(nonce、requireNonce)。 | reasons[] 中的 PKI_REASON_REVOKED 和其他 PKI_REASON_*、VALIDATION_ERROR |
CMS 和时间戳
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
sign_cms | 工件 | 对 content(附带或 detached)或预先计算的 contentDigest 生成 RFC 5652 SignedData;可包含一个 signer(PKCS#8 密钥或 PKCS#12)、certificate、chain 和 crls,这些内容会先解析再嵌入;始终写入签名属性(contentType、messageDigest、signingTime、SigningCertificateV2、CMSAlgorithmProtection);在返回字节之前,会根据签名者证书进行验证。 | KEY_MISMATCH、RSA_SCHEME_REQUIRED、SECRET_REQUIRED、VALIDATION_ERROR、PKI_API_MISUSE、ENGINE_ERROR |
verify_cms | 判定 | 对 SignedData 进行一次调用即可完成判定:针对每位签名者,检查签名属性、内容摘要(附带的,或来自 content / contentDigest)、签名、截至 at 时到你的 trustAnchors 的证书链、所有计数器时间戳,以及你提供的字节和消息所携带字节中的吊销信息;atTimeStamp: true 表示长期验证。 | reasons[] 中的 PKI_REASON_*(也会针对每位签名者;intact 用于区分未被篡改与不受信任),PKI_ASN1_TRAILING_DATA、VALIDATION_ERROR |
inspect_cms | 读取 | RFC 5652 报告:内容类型和长度、摘要算法、嵌入的证书、CRL / OCSP 数量、每位签名者的信息(标识符、算法、作为声明值的签名时间、属性、时间戳数量、计数器时间戳所戳记的签名值);signerCertificate 会针对 signerIndex 指定的签名者执行一次密钥检查。 | NOT_FOUND(signerIndex 超出最后一位签名者,且提供了 signerCertificate)、PKI_ASN1_TRAILING_DATA、PKI_* |
amend_cms | 工件 | 对已签名消息进行事后合法修改的唯一方式:在签名者 signerIndex 上添加 add-unsigned-attribute(以十六进制表示的 DER Attribute)或 add-timestamp(TimeStampToken 或完整的 TimeStampResp),所有已签名字节保持不变;在写入任何内容之前,会根据签名值检查令牌的 imprint。 | VALIDATION_ERROR(data.detail 为 PKI_REASON_TSP_IMPRINT_MISMATCH)、NOT_FOUND、PKI_API_MISUSE |
create_timestamp_request | 工件 | 你自行 POST 到 TSA 的 RFC 3161 TimeStampReq:对 data 或 digestHex(SHA-256 / 384 / 512)生成 imprint;支持 nonce('random'、十进制或十六进制)、policy、certReq。 | VALIDATION_ERROR(摘要长度错误) |
inspect_timestamp | 读取 | TimeStampResp、TimeStampToken 或裸 TSTInfo(自动检测,或通过 as 指定):状态、策略、imprint、序列号、genTime、精度、nonce、声明的 TSA 名称、CMS 封装。解析并不能证明任何内容。 | PKI_*、PKI_STRICT_DIAGNOSTIC(使用 strict: true 时) |
verify_timestamp | 判定 | 根据你所说的戳记内容(request、data 或 digestHex,至少提供一项)验证 token 或 response,并检查 TSA 签名、截至 at 时到你的锚点的证书链(含关键的 timeStamping EKU),以及你提供的字节中的吊销信息。 | PKI_REASON_TSP_IMPRINT_MISMATCH、PKI_REASON_TSP_NOT_GRANTED 及 reasons[] 中的其他 PKI_REASON_*、VALIDATION_ERROR |
私钥和 PKCS#12
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
inspect_private_key | 读取 | 不使用密码描述 PKCS#8 密钥(未设置 PKINATIVE_MCP_PASSWORD 时——该变量会被视为每个接受密码的工具的密码):格式、类型、曲线、算法、加密密钥的 PBES2 参数(或 pkinative 拒绝的旧版方案);tryImport: true 会将其导入为不可提取的 Web Crypto 密钥,并报告句柄。任何结果都不会包含密钥字节。 | SECRET_REQUIRED、KEY_TYPE_REQUIRED、RSA_SCHEME_REQUIRED、PKI_CRYPTO_DECRYPTION_FAILED、PKI_KEY_ENCRYPTION_UNSUPPORTED |
inspect_pkcs12 | 读取 | 不使用密码描述 .p12 / .pfx(操作者的 PKINATIVE_MCP_PASSWORD 会被视为密码,因此必须未设置):MAC 构造(PBMAC1 或旧版 RFC 7292 MAC)、每个 SafeContents 及其方案、未加密 SafeContents 中的各个包;提供密码时,返回 macVerified 和 opened(每个包的允许列表视图)——若加密的 SafeContents 使用了错误密码,则返回 PKI_CRYPTO_DECRYPTION_FAILED。 | PKI_CRYPTO_DECRYPTION_FAILED、PKI_KEY_MAC_UNSUPPORTED、PKI_KEY_ENCRYPTION_UNSUPPORTED |
open_pkcs12 | 判定 | 一次调用即可打开 PKCS#12:验证 MAC、解密 PBES2 SafeContents、将每个密钥直接解包为不可提取的 Web Crypto 密钥(仅作描述,绝不导出)、解析证书并统计 CRL;exportCertificates: true 会附加包含证书和 CRL 的 PEM 包——绝不包含密钥。 | SECRET_REQUIRED;在 reasons[] 中:PKI_REASON_PKCS12_MAC_MISMATCH、PKI_REASON_PKCS12_ENCRYPTION_UNSUPPORTED、PKI_REASON_PKCS12_INTEGRITY_UNVERIFIED、PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED |
元信息
| 工具 | 类型 | 功能 | 关键拒绝代码 |
|---|---|---|---|
describe_engine | 引擎 | 离线预检:版本、主机的 Web Crypto 功能、引擎的 22 项默认限制、操作者上限、每项服务上限、沙盒 / 缓存 / 固定时刻状态、57 个 PKI_* / 43 个 PKI_REASON_* / 97 个 PKI_DIAG_* 词汇、21 个包装器代码、拒绝原则以及刻意不公开的引擎接口。network 始终为 'none'。从不缓存。 | VALIDATION_ERROR |
explain_code | 解释 | 解释此服务可发出的任何代码,依据 vendored 于 pkinative v1.0.0 标签的注册表提供说明(类别、标准、CWE、补救方法、可解除该错误的 MCP 输入);list: true 会枚举一种词汇。 | NOT_FOUND |
draft_governance_issue | 治理 | 为 pkinative、pkinative-cli 或 pkinative-mcp 生成本地 GitHub 问题草稿和合规报告——绝不提交;由人工以自己的身份提交。 | GOVERNANCE_VIOLATION、MISSING_OUTPUT_PATH、INVALID_EXTENSION、OUTPUT_EXISTS、SECURITY_VIOLATION |
每个工具都保证:
- 🌐 绝不联网——不获取 CRL / OCSP / AIA / TSA,不进行遥测,不访问 GitHub,不访问任何参数中的 URL,也没有可由操作员配置的端点。吊销证据和时间戳令牌是输入;
create_ocsp_request和create_timestamp_request生成的字节由你负责传输。每个工具都声明openWorldHint: false。 - 🔑 密钥字节绝不外泄——签名密钥是不可导出的 Web Crypto 句柄;每份密钥 / PKCS#12 报告都只显示允许列表中的内容;密钥文件(
.key .p8 .p12 .pfx)绝不会写入,也绝不会作为资源提供;convert_pem、decode_asn1和encode_asn1会拒绝私钥材料。不生成密钥,也不导出密钥。 - 🧭 判定是报告,错误是拒绝——否定判定为
valid: false,并附带引擎的PKI_REASON_*代码(共 43 个);isError仅用于引擎拒绝的输入(57 个PKI_*代码之一,会原样传递,并附带data.errorClass和解决方法)或封装层拒绝(21 个代码,每个均附带解决方法)。应依据代码分支处理,切勿依据消息处理。 - 📐 明示规范,不藏于幕后——SHA-1 仅在
allowSha1: true时可用;RSA 必须明确指定rsaScheme('pkcs1'|'pss',无默认值);仅支持 PBES2 和 PBMAC1(遇到旧版 RC2 / 3DES PKCS#12 时会拒绝,并提供使用 OpenSSL 3.4 转换的解决方法);可解析 DSA 和 ML-DSA,但绝不验证;诊断信息(97 个PKI_DIAG_*代码)除非设置strict: true,否则不算错误;引擎的 22 项限制可在每次调用时调高,但不会超过操作员设置的上限。 - 🪙 节省令牌——读取和判定工具接受
verbosity: 'summary'和fields: [...];制品仅返回一次,以嵌入式resource块或resource_link的形式提供,不会重复放入structuredContent。 - 🔁 按需实现可复现——固定
at、notBefore、signingTime、serialNumber和nonce(或通过PKINATIVE_MCP_AT让操作员固定时间点),即可使制品或判定成为其输入的纯函数;structuredContent.sha256即为证明(docs/guides/REPRODUCIBLE.md)。 - 🧪 以 OpenSSL 3 为准——服务器写入的每个制品都会在 CI 中由 OpenSSL 3.x 读回验证(23 个符合规范的制品、5 个拒绝用的金丝雀测试、12 组命令系列);Linux 和 Windows 上均设为阻断检查。
所有生成制品的工具都支持两种输出模式和三种编码:
base64(默认)——制品仅返回一次,以嵌入式resource内容块的形式提供(PEM 为text,DER 为blob,并带有其 MIME 类型:application/pkix-cert、application/pkcs10、application/pkcs7-signature、application/pkix-crl、application/ocsp-request、application/timestamp-query……);structuredContent包含{ mode, encoding, sizeBytes, sha256, summary, diagnostics }——若设置encoding: 'hex',字节则放入structuredContent.hex(仅内联,≤ 64 KiB)。file——制品写入由PKINATIVE_MCP_OUTPUT_DIR配置的沙箱目录中(独占创建,绝不覆盖 →OUTPUT_EXISTS;≤ 1 GiB),结果附带resource_link。除非设置此变量,否则禁用文件 I/O;绝对路径、路径遍历、NUL 字节、带密钥内容的文件或未知扩展名,以及真实路径超出沙箱的符号链接或 junction,都会被拒绝。同一沙箱也用于所有path输入,因此create_csr → create_certificate → verify_certificate_chain链式调用无需重复发送字节。
**节省令牌的读取。**读取和判定工具(以及 describe_engine、explain_code)接受两个可选输入:
verbosity: 'summary'— 仅包含标量的精简结论。例如,verify_certificate_chain→{ valid, reasonCodes, pathLength, evaluatedAt, revocationEvidence, diagnosticCount };inspect_certificate→ subject、issuer、serial、validity、key、extension 种类。fields: ['a', 'b.c']— 将结构化结果投影到指定的点路径(≤ 16;数组会映射到各个元素);在verbosity之后应用。所有未匹配到内容的路径(包括嵌套路径)都会被省略,并在_meta.unmatchedFields中报告(同时提供_meta.availableFields);包含__proto__、constructor或prototype段的路径会触发VALIDATION_ERROR。
最精简的“我能信任这个服务端证书吗?”探测:对 verify_certificate_chain 使用 { "leaf": {…}, "trustAnchors": [{…}], "at": "2027-01-01T00:00:00Z", "serverName": { "host": "example.test" }, "verbosity": "summary", "fields": ["valid", "reasonCodes"] }。
该服务还提供 七个 MCP 提示词——governance_contract、draft_issue_workflow、verify_before_trust、issue_certificate、sign_and_timestamp、offline_revocation、open_keys_safely——并将沙盒中的每个非机密文件作为 pkinative://output/{+path} 资源公开。
为什么选择 pkinative?
pkinative-mcp 继承了底层引擎的所有保证:
- 引擎无运行时依赖——纯 TypeScript,不含原生绑定;所有签名均使用 Web Crypto,因此不会在 JavaScript 中执行依赖机密信息的算术运算(本服务仅额外使用 MCP SDK 和 zod:总共三个运行时依赖)。
- RFC 5280 路径验证(构建、§6.1 验证、名称约束、策略)、RFC 6125 名称、RFC 6960 OCSP、RFC 5652 CMS、RFC 3161 时间戳、RFC 2986 CSR、采用 PBES2 的 RFC 5958 / 8018 PKCS#8、采用 PBMAC1 的 RFC 7292 / 9579 PKCS#12,严格遵循 X.690 DER,并可选择启用 BER。
- 22 个具名且带 CWE 标签的限制,应用于每个解析器循环——仅由显式输入触发,不会静默生效。
- 固定的词汇表——57 个
PKI_*错误代码、43 个PKI_REASON_*判定原因和 97 个PKI_DIAG_*诊断项,随 v1.0.0 标签一并引入,并逐字传递。 - **不会执行的操作:**不生成密钥、不导出密钥、不提供 PKCS#8 / PKCS#12 / CRL / OCSP 响应 / 时间戳响应写入器、不验证 DSA / ML-DSA,绝不访问网络。
🚀 安装
# Run directly with npx (recommended for MCP clients)
npx -y pkinative-mcp
# Or install globally
npm install -g pkinative-mcp
pkinative-mcp
要求:Node.js ≥ 22(引擎要求的最低版本为 ^22.22.2 || ^24.14.1 || >=25.8.2;可通过 describe_engine.capabilities.nodeFloorSatisfied 查询)。从源代码安装:git clone https://github.com/Nizoka/pkinative-mcp.git && cd pkinative-mcp && npm ci --ignore-scripts && npm run build && node dist/cli.js。
⚙️ 配置
Claude Desktop
编辑 ~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或 %APPDATA%\Claude\claude_desktop_config.json(Windows):
{
"mcpServers": {
"pkinative": {
"command": "npx",
"args": ["-y", "pkinative-mcp"],
"env": {
"PKINATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-pki"
}
}
}
}
Claude Code
项目根目录中的 .mcp.json(仓库附带的配置文件会从 dist/cli.js 启动构建完成的服务):
{
"mcpServers": {
"pkinative": {
"type": "stdio",
"command": "npx",
"args": ["-y", "pkinative-mcp"],
"env": { "PKINATIVE_MCP_OUTPUT_DIR": "${CLAUDE_PROJECT_DIR}/.mcp-pki" }
}
}
}
Cursor / Continue / Zed / Windsurf / Cline / Roo Code
任何支持 stdio 服务的 MCP 兼容客户端均可使用。使用相同的 command + args + env 三元组。Cursor 示例(~/.cursor/mcp.json):
{
"mcpServers": {
"pkinative": {
"command": "npx",
"args": ["-y", "pkinative-mcp"],
"env": { "PKINATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-pki" }
}
}
}
Windsurf / Cline / Roo Code 在各自的 MCP 配置文件中使用相同的格式。
VS Code
.vscode/mcp.json:
{
"servers": {
"pkinative": {
"type": "stdio",
"command": "npx",
"args": ["-y", "pkinative-mcp"],
"env": { "PKINATIVE_MCP_OUTPUT_DIR": "${workspaceFolder}/.mcp-pki" }
}
}
}
🌐 支持的 AI 生态系统和客户端
pkinative-mcp 专为原生 MCP 环境设计,可与支持通过 stdio 或 Streamable HTTP 使用 MCP 的客户端配合使用:Claude Desktop、Claude Code、Cursor、Continue、Zed、Windsurf、Cline、Roo Code、VS Code、ChatGPT(Streamable HTTP)、MCP Inspector,以及任何基于 MCP TypeScript / Python SDK 构建的客户端。由于服务本身不会打开任何套接字,因此也可以安全地在隔离网络或出站流量受限的主机中运行——PKI 材料通常就存放在这类主机上。
它是“native”系列中的第三个服务,前两个是 pdfnative-mcp(PDF)和 zipnative-mcp(ZIP):采用相同的轻量封装架构、相同的沙箱、缓存、投影和治理约定,以及相同的人在回路规则。同一引擎也可通过命令行工具 pkinative-cli 使用。
🔌 MCP 协议合规性
此服务基于 MCP TypeScript SDK v2(@modelcontextprotocol/server)构建,并使用 MCP 2026-07-28 协议:
- 无状态服务 —
server/discover取代了会话握手;每个结果都包含resultType和_metaserverInfo信封。通过 HTTP 通信时,2026-07-28 客户端会在每个POST /mcp请求中发送Mcp-Method/Mcp-Name标头。 - 缓存提示 —
tools/list和prompts/list的缓存范围为public,ttlMs为 24 h;server/discover的缓存范围为public,有效期为 1 h;resources/list/resources/templates/list/resources/read的缓存范围为private,ttlMs: 0(沙箱文件是每台主机上的用户数据)。 - 协议错误 — 使用未知工具名称调用
tools/call时会返回 JSON-RPC 错误(-32602,[UNKNOWN_TOOL] Unknown tool: …),而不是isError结果;未知资源 URI 会返回-32602(参数无效),符合规范要求;isError: true仅用于执行失败。 - 自动回退到旧版 — 使用
initialize发起连接的客户端,会在 stdio 和 HTTP 上通过 SDK 的旧版路径提供服务,适用于 SDK 协商的所有版本:2024-10-07、2024-11-05、2025-03-26、2025-06-18 和 2025-11-25(旧版),以及 2026-07-28(新版)。现有主机不受影响。 - HTTP — 仅支持
POST /mcp;GET/DELETE /mcp返回 405(不支持 SSE 恢复;服务为无状态)。绑定到127.0.0.1,并设有Host/Origin检查(外部值 → 403);PKINATIVE_MCP_HTTP_TOKEN可启用可选的 bearer 令牌验证(未提供令牌时返回401+WWW-Authenticate);超过 256 MiB 的请求正文返回 413。 - stdio — 帧大小上限为 256 MiB,因此数 MiB 的 base64 CMS 消息或 PKCS#12 文件可在一次
tools/call中传输;与每个 SDK 版本一样,在initialize之前发送的请求会在旧版路径中被丢弃且不返回响应。 - 输出架构 — 每个
structuredContent都会根据工具的outputSchema进行验证(这是 2026-07-28 的强制要求),包括verbosity: 'summary'和fields投影:读取和判定工具声明了支持投影的架构(所有属性均为可选,同时保留additionalProperties: false)。根据策略,输入架构不包含$schema关键字或$ref(某些主机会将inputSchema转发给不接受未知关键字的函数调用 API)。serverInfo包含websiteUrl;资源模板为pkinative://output/{+path}。
tools/call 的载荷(content、structuredContent、isError)在 2026-07-28 路径和旧版路径之间完全一致;tests/http-modern.test.ts 会对此进行断言,而 tests/schema-conformance.test.ts 会使用 SDK 的 JSON Schema 2020-12 验证器验证 structuredContent。
| 客户端 | 传输方式 | 协商的协议 |
|---|---|---|
| Claude Desktop, Cursor, Continue, Zed, Windsurf, Cline | stdio | 旧版 initialize(2024-xx / 2025-xx)——未更改 |
| ChatGPT 和其他 Streamable HTTP 主机 | HTTP POST /mcp | 旧版无状态 Streamable HTTP——未更改 |
MCP 2026-07-28 客户端(SDK v2 Client、当前 MCP Inspector) | stdio / HTTP | server/discover、缓存提示、_meta 封装 |
兼容性矩阵——两种传输方式下的每个协商版本,以及用于验证它的测试:
| 版本 | 时期 | stdio | HTTP POST /mcp | 验证依据 |
|---|---|---|---|---|
| 2024-10-07 | 旧版 initialize | ✓ | ✓ | tests/protocol-revisions.test.ts |
| 2024-11-05 | 旧版 initialize | ✓ | ✓ | tests/protocol-revisions.test.ts |
| 2025-03-26 | 旧版 initialize | ✓ | ✓ | tests/protocol-revisions.test.ts |
| 2025-06-18 | 旧版 initialize | ✓ | ✓ | tests/protocol-revisions.test.ts、tests/http-transport.test.ts |
| 2025-11-25 | 旧版 initialize | ✓ | ✓ | tests/protocol-revisions.test.ts、tests/cli-stdio.test.ts、tests/autonomy.test.ts |
| 2026-07-28 | 现代版(server/discover、_meta 封装、缓存提示) | ✓ | ✓(+ bearer token) | tests/stdio-modern.test.ts、tests/http-modern.test.ts、tests/autonomy.test.ts |
环境变量
所有 9 个环境变量都在 server.json 中声明,并在启动时读取一次;如果值无效(PKINATIVE_MCP_PORT 超出 1–65535、PKINATIVE_MCP_OUTPUT_DIR 为相对路径、时间点格式错误或超过上限),服务将拒绝启动——绝不静默回退。日志中只记录密钥是否存在。
| 变量 | 默认值 | 含义 |
|---|---|---|
PKINATIVE_MCP_OUTPUT_DIR | (未设置) | 唯一沙盒目录的绝对路径:每个 path 输入(PKI 对象、内容、密钥)都从该目录读取,outputMode: 'file' 输出也写入该目录下(以独占方式创建——绝不覆盖)。每个被读取文件的真实路径以及每个写入操作的父目录真实路径都必须位于该目录内:在沙盒中植入符号链接或 junction 时,读写两侧都会触发 SECURITY_VIOLATION。含密钥的文件(.key .p8 .p12 .pfx)只能由密钥工具读取,绝不会写入,也绝不会作为资源提供;resources/* 还会按内容拒绝密钥材料,无论文件名是什么。未设置 ⇒ 所有工具仅支持内联 PEM / base64。 |
PKINATIVE_MCP_CACHE_DIR | (未设置) | 启用后使用基于内容寻址的响应缓存(以 SHA-256 为键,TTL 为 1 小时,LRU 上限为 256 MiB,静态存储时为明文;缓存键按工具 API、软件包和引擎版本划分命名空间)。绝不缓存 path 输入、文件输出、带有 password 或 signer 的调用、'random' 序列号或 nonce、'now' 或未固定的时刻、describe_engine 或 draft_governance_issue——也不缓存 PKINATIVE_MCP_PASSWORD 已设置时任何接受密码的工具;命中缓存时会带有 _meta.cached: true。 |
PKINATIVE_MCP_PORT | (未设置 → stdio) | 设置为有效端口(1–65535)时,通过 http://127.0.0.1:<port>/mcp 提供 Streamable HTTP 服务,而非 stdio;其他任何值都会导致启动失败。接受大小不超过 256 MiB 的请求正文(超过时返回 413)。仅限回环地址,并启用 DNS 重绑定防护(外部 Host / Origin → 403);GET / DELETE → 405。除非设置 PKINATIVE_MCP_HTTP_TOKEN,否则不进行身份验证——其他本地进程可以访问此端点。 |
PKINATIVE_MCP_HTTP_TOKEN | (未设置) | (机密) 启用后为 HTTP 传输使用 Bearer 令牌(至少 16 个字符,不得包含空白字符——强度不足会导致启动失败)。设置后,每个 /mcp 请求都必须携带 Authorization: Bearer <token>;否则返回 401 + WWW-Authenticate: Bearer realm="pkinative-mcp"(仅在发送了凭据时才带有 error="invalid_token"——RFC 6750 §3.1)。使用恒定时间比较,绝不记录日志。 |
PKINATIVE_MCP_PASSWORD | (未设置) | (机密) 操作者一侧的密码,用于打开加密的 PKCS#8 密钥或 PKCS#12 文件——可避免机密出现在模型对话记录中。它会作为所有接受密码的工具(inspect_private_key、inspect_pkcs12、open_pkcs12、签名工具的 signer)的密码:要“在不提供密码的情况下描述”,必须取消设置。绝不记录日志;启动时只报告它是否已设置。 |
PKINATIVE_MCP_AT | (未设置 → 系统时钟) | 固定时刻(ISO 8601,或 epoch 毫秒);所有省略的 at / notBefore / signingTime 都回退到此时刻,以便获得可复现的判定结果和产物。未设置:使用系统时钟——依赖时钟的结果绝不缓存。 |
PKINATIVE_MCP_MAX_INPUT_BYTES | 67108864(64 MiB) | 操作者对 limits.maxInputBytes 以及单个 PKI 对象解码后大小的上限(引擎默认值)。整数,且 ≥ 1024。单次调用设置的值超过此上限时会触发 LIMIT_CEILING_EXCEEDED;若上限低于引擎默认值,则会静默收紧默认值。 |
PKINATIVE_MCP_MAX_KDF_ITERATIONS | 10000000 | 操作者对 limits.maxKdfIterations 和 limits.maxPkcs12KdfIterations 的上限(引擎默认值):这是防范恶意 PBES2 密钥或 PKCS#12 文件的 CPU 使用量限制。整数,且 ≥ 1。 |
PKINATIVE_MCP_MAX_PATHS_EXPLORED | 1000 | 操作者对 limits.maxPathsExplored 的上限(引擎默认值):RFC 5280 §6 路径构建的工作量限制。整数,且 ≥ 1。 |
运算符上限。 引擎的 22 个命名 PkiLimits(maxInputBytes、maxDepth、maxNodes、maxIntegerBytes、maxOidBytes、maxBerSegments、maxPemBlocks、maxExtensions、maxGeneralNames、maxNameAttributes、maxPolicies、maxChainLength、maxPolicyNodes、maxRevokedCertificates、maxOcspSingleResponses、maxPathsExplored、maxSignerInfos、maxAttributes、maxCmsCertificatesAndCrls、maxKdfIterations、maxPkcs12Bags、maxPkcs12KdfIterations)仅适用于可信输入,可通过 limits 片段按调用提高。其中三项受上述三个运算符上限约束:高于上限的值会被拒绝,并返回 LIMIT_CEILING_EXCEEDED;data.env 会指出运算符需要提高哪个变量。不存在“无上限”设置,工具参数也不能提高任何上限。describe_engine 会报告默认值、上限以及当前生效的所有限制。 |
🛠 工具参考
每个 PKI 对象输入都是一个对象,且必须且只能包含以下字段之一:pem(RFC 7468 文本)、derBase64(DER 的 base64 编码,只编码一次——不能是 data: URI,也不能是 PEM)或 path(沙盒内的相对路径;扩展名可为 .pem .crt .cer .der .csr .p10 .crl .p7s .p7b .p7c .p7m .cms .p12 .pfx .key .p8 .pub .ocsp .ors .orq .tsq .tsr .tst .bin .asn1)。每个与引擎交互的工具都接受 PARSE 片段(encodingRules、pemMode、strict、limits);读取和判定工具接受 verbosity 和 fields;制品工具接受 outputMode、outputPath 和 encoding。各片段详见共享输入片段。错误结果:content[0].text = "<tool> failed [CODE]: message"、isError: true、_meta.error = { code, message, data? }。完整的输入/输出架构由 tools/list 提供;使用者契约——目录、决策树、操作示例和完整错误表——见 docs/AGENT_CONTRACT.md。以下每个示例都是可执行调用,来源为 examples/ 或某个工具的 _meta.examples(fixtures/… 路径指向仅供测试使用的 PKI 文件;示例运行器会将其复制到沙盒中;test-only-password 是这些文件公开的密码)。
convert_pem
RFC 7468 层(目录)。输入: mode(decode | encode)、input(PKI_INPUT;encode 恰好接受一个 DER 值)、label、extractIndex、PARSE、PROJECTION、OUTPUT(encoding 为 der / pem / hex)。输出: blocks[](index、label、sizeBytes、offset、sha256)、count、extracted,以及在提取区块或封装 DER 时返回的 artefact。代码: SECURITY_VIOLATION(任何标签下,私钥区块或输入中任意位置的密钥材料——包括前缀之后或其他区块之间的密钥材料)、NOT_FOUND(索引超出最后一个区块)、VALIDATION_ERROR、PKI_PEM_UNEXPECTED_LABEL、PKI_PEM_*。
{ "mode": "decode", "input": { "path": "fixtures/leaf.crt.pem" }, "label": "CERTIFICATE", "extractIndex": 0, "encoding": "hex", "fields": ["extracted", "artefact"] }
resolve_oid
在一次调用中处理对象标识符。输入: mode(name | validate | list | encode | decode)、oids[](≤ 1000)、filter、offset / limit(默认值为 200,最大值为 2000)、form(content | tlv)、relative、hex 或 input(作为 decode 来源的 PKI_INPUT——pem / derBase64 / path)、PARSE、PROJECTION。输出: 按模式返回:包含名称或有效性的 oids[]、entries[] + total / hasMore,或单个 oid + name。encode 会针对每个 OID 返回 hex,而不会生成制品:要封装为 PEM 的 OID,请使用 encode_asn1 { spec: { type: 'oid', value }, encoding: 'pem', label }。代码: VALIDATION_ERROR(成员不适用于所选模式、relative 未配合 form: 'tlv' 使用、同时提供 hex 和 input)、PKI_OID_INVALID(encode / decode;validate 则会报告该问题)、PKI_ASN1_*。
{ "mode": "name", "oids": ["1.2.840.10045.4.3.2", "2.5.29.17", "1.3.6.1.5.5.7.3.1", "1.2.3.4"] }
compute_fingerprint
单个对象的摘要。输入:input(PKI_INPUT,任意标签或 DER;拒绝证书包)、mode(fingerprint | key-id | shake256)、algorithm、webcrypto、outputLength(≤ 1 MiB)、separator、letterCase、PARSE、PROJECTION。输出:hex、formatted、algorithm、sizeBytes、label、from(在 key-id 中表示公钥的来源)。代码:VALIDATION_ERROR(所选算法不适用于该模式,或密钥标识符使用 SHA-384 / 512)、PKI_X509_* / PKI_ASN1_*(对不含公钥的对象使用 key-id)。
{ "input": { "path": "fixtures/leaf.csr.pem" }, "mode": "key-id", "fields": ["formatted", "from"] }
decode_asn1
X.690 节点树。输入:input(PKI_INPUT——不能是私钥或 PKCS#12)、sequence、nodePath('0.2.1')、read(boolean | integer | small-integer | enumerated | null | bit-string | octet-string | oid | relative-oid | string | time)、stringType、timeType、reencode、label(当输入没有 PEM 标签或选中子节点时,reencode 产物的 PEM 标签)、allowTrailingData、maxPreviewBytes(默认值为 32)、PARSE、PROJECTION、OUTPUT。输出:nodes[](path、offset、长度、tag、constructed、children、preview、oid + oidName)、nodeCount、truncated(超过 20 000 个节点时为 true)、value(类型化读取结果)、reencode.identical、artefact。代码:SECURITY_VIOLATION、NOT_FOUND(nodePath 中缺少步骤,并指出范围)、PKI_ASN1_UNEXPECTED_TAG(读取器不匹配)、PKI_API_MISUSE(未提供 stringType / timeType 的 IMPLICIT 字符串 / 时间)、PKI_ASN1_*、ENGINE_ERROR(在调高 limits.maxDepth 后调用栈耗尽——错误消息会指出解决方法:降低该值)。
{ "input": { "path": "fixtures/leaf.crt.der" }, "nodePath": "0.1", "read": "integer", "verbosity": "summary" }
encode_asn1
将 JSON 节点规格编码为 DER。输入:spec(一个节点 { type, … })、label(用于 encoding: 'pem')、PARSE、OUTPUT(encoding 为 der / hex / pem;默认值为 der)。**输出:**产物(sizeBytes、sha256、hex 或资源)和 summary(根标签、节点数)。代码:VALIDATION_ERROR(指出 JSON 路径,例如 $.children[1].value …)、SECURITY_VIOLATION(私钥标签,或解析后属于密钥或 PKCS#12 的值)、PKI_LIMIT_EXCEEDED(嵌套深度超过 limits.maxDepth)、PKI_ASN1_*、PKI_OID_INVALID。
{ "spec": { "type": "sequence", "children": [{ "type": "oid", "value": "1.2.840.10045.4.3.2" }] }, "encoding": "hex" }
inspect_certificate
RFC 5280 报告。输入:certificate(PKI_INPUT)、extension(19 种类型之一)、rawExtensions、issuer(PKI_INPUT)或 checkSelfSignature、requireAlgorithmMatch、fingerprints[]、allowSha1、PARSE、PROJECTION。输出:version、serialNumber、signatureAlgorithm、issuer / subject(以及 issuerString / subjectString)、validity、subjectPublicKeyInfo、extensions[]、selfIssued、fingerprints、signature(检查时包含 valid / algorithm)、diagnostics[]。代码:NOT_FOUND(证书不包含所选 extension)、PKI_PEM_UNEXPECTED_LABEL(将密钥作为证书传入——会在解析任何字节前按标签拒绝)、PKI_X509_*、PKI_CRYPTO_ALGORITHM_REFUSED(未设置 allowSha1 却使用 SHA-1)。
{ "certificate": { "path": "fixtures/leaf.crt.pem" }, "issuer": { "path": "fixtures/inter.crt.pem" }, "fingerprints": ["SHA-256", "SHA-1"], "fields": ["signature", "extensions.kind", "subjectString", "fingerprints"] }
create_certificate
签发证书。输入:spec(serialNumber——整数、十六进制字符串或 'random'、subject、issuer、notBefore、notAfter / validityDays、extensions——basicConstraints、keyUsage、extendedKeyUsage、subjectAltName、issuerAltName、SKI / AKI、raw)、issuerCertificate(PKI_INPUT;省略时为自签名)、subjectPublicKey(证书、CSR 或 PUBLIC KEY)、signer(SIGNER)、allowSha1、PARSE、OUTPUT(默认值为 pem)。**输出:**产物和 summary(serialNumber、randomSerial、subject、issuer、selfSigned、signatureAlgorithm、notBefore / notAfter 及 notBeforeSource、扩展类型、signatureVerified、publicKeyFrom)。代码:KEY_MISMATCH(新证书无法通过颁发者或自身的验证)、RSA_SCHEME_REQUIRED、KEY_TYPE_REQUIRED、SECRET_REQUIRED、UNSUPPORTED_KEY、VALIDATION_ERROR(未知的 spec 成员、未设置 allowSha1 却使用 SHA-1、notAfter 早于 notBefore)。
{
"spec": {
"serialNumber": 1,
"subject": { "C": "FR", "O": "Acme", "CN": "Acme Test CA" },
"notBefore": "2027-01-01T00:00:00Z",
"validityDays": 3650,
"extensions": { "basicConstraints": { "ca": true, "pathLen": 0 }, "keyUsage": ["keyCertSign", "cRLSign"] }
},
"signer": { "key": { "path": "fixtures/ed25519.key.pem" } }
}
encode_x509_structure
将一个构造块编码为 DER。输入: 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)、spec(一个封闭对象)、signer(仅限 signature-algorithm——密钥以不可提取的方式导入,且只读取其算法)、label、allowSha1、PARSE、OUTPUT(默认为 hex)。输出: 产物(默认为 hex)和 summary。代码: VALIDATION_ERROR,指出 spec <member>(未知或缺失的成员绝不会被静默丢弃)、KEY_TYPE_REQUIRED、SECRET_REQUIRED、RSA_SCHEME_REQUIRED。
{ "structure": "subject-alt-name", "spec": { "dns": ["svc.example.test", "*.wild.example.test"], "ip": ["192.0.2.1"] } }
decode_extension
按 OID 解码一个 extnValue。输入: oid、valueHex 或 value(PKI_INPUT——OCTET STRING 内部的 DER)、critical、PARSE、PROJECTION。输出: kind、oid、name、critical、sizeBytes、decoded(该类型的成员)、diagnostics[](关键性检查)。代码: PKI_X509_EXTENSION_MALFORMED(补救方法:decode_asn1)、VALIDATION_ERROR。
{ "oid": "2.5.29.19", "valueHex": "30030101ff", "critical": true }
check_certificate_name
RFC 6125 身份判定。输入: 证书形式——certificate(PKI_INPUT)+ host 或 ip、chain[](PKI_BUNDLE,用于 CN 回退)、allowCommonNameFallback(默认为 false)、allowWildcards(默认为 true);匹配形式——presented + reference;PARSE、PROJECTION。输出: form、valid、reasons[]、reference、subject / presented、match。代码: PKI_REASON_NAME_MISMATCH 等(位于 reasons[] 中;这是判定结果,绝不是 isError);VALIDATION_ERROR(两种形式同时出现)。
{ "certificate": { "path": "fixtures/leaf.crt.pem" }, "host": "a.wild.example.test", "verbosity": "summary" }
check_certificate_purpose
RFC 5280 §4.2.1.12 用途判定。输入: certificate、chain[](先列出叶证书的颁发者)、purpose(serverAuth | clientAuth | codeSigning | emailProtection | timeStamping | ocspSigning | any | 带点分隔的 OID)、restrictIssuers(默认为 true)、requireExplicitPurpose(默认为 false)、PARSE、PROJECTION。输出: valid、reasons[]、purposeOid、purposeName、subject、pathLength。代码: PKI_REASON_PURPOSE_NOT_PERMITTED(包含 path[i] 元素,位于 reasons[] 中);VALIDATION_ERROR(未知用途名称)。
{ "certificate": { "path": "fixtures/leaf.crt.pem" }, "purpose": "codeSigning" }
inspect_csr
RFC 2986 报告。输入: request(PKI_INPUT)、rawExtensions、verify(默认为 true——所有权证明,一次 Web Crypto 验证)、allowSha1、PARSE、PROJECTION。输出: subject(+ subjectString)、subjectPublicKeyInfo、attributes[]、extensions[]、challengePassword(是否存在,但绝不包含其文本)、signatureAlgorithm、signature(valid、reasons[])。代码: PKI_REASON_SIGNATURE_NOT_CHECKED(SHA-1 且未启用 allowSha1 时,位于 signature.reasons[] 中)、PKI_X509_*、PKI_PEM_UNEXPECTED_LABEL。
{ "request": { "path": "fixtures/leaf.csr.pem" }, "verbosity": "summary" }
create_csr
PKCS#10 请求。输入: spec(subject、extensions——subjectAltName、keyUsage、extendedKeyUsage、basicConstraints、subjectKeyIdentifier、raw;不接受颁发者的扩展)、subjectPublicKey(加密的 PKCS#8 密钥必需)、signer(SIGNER)、allowSha1、PARSE、OUTPUT(默认为 pem)。输出: 产物和 summary(主题、signatureAlgorithm、请求的扩展类型、signatureVerified、publicKeyFrom)。代码: KEY_MISMATCH(密钥与指定的公钥不匹配)、RSA_SCHEME_REQUIRED、SECRET_REQUIRED、KEY_TYPE_REQUIRED、VALIDATION_ERROR。
{
"spec": {
"subject": { "C": "FR", "O": "Acme", "CN": "svc.example.test" },
"extensions": { "subjectAltName": { "dns": ["svc.example.test", "www.svc.example.test"] }, "keyUsage": ["digitalSignature"], "extendedKeyUsage": ["serverAuth"] }
},
"signer": { "key": { "path": "fixtures/leaf.key.pem" } }
}
verify_certificate_chain
RFC 5280 §6 的判定结果。输入: leaf(PKI_INPUT)、TRUST(trustAnchors[]、candidates[]、crls[]、ocspResponses[]、at、requireRevocation、purposes[]、policy)、serverName(host | ip)、ocspNonce、requireOcspNonce、allowSha1、PARSE、PROJECTION。输出: valid、reasons[]、path[](从叶证书开始,直到信任锚)、pathLength、explored、signatureVerifications[]、evaluatedAt + evaluatedAtSource、revocationEvidence。代码: reasons[] 中的 PKI_REASON_*——NO_TRUST_ANCHOR、EXPIRED、NOT_YET_VALID、REVOKED、REVOCATION_UNKNOWN、REVOCATION_STALE、NAME_MISMATCH、PURPOSE_NOT_PERMITTED、……(判定结果,永远不是 isError);引擎拒绝输入时使用 VALIDATION_ERROR、PKI_*。
{
"leaf": { "path": "fixtures/revoked.crt.pem" },
"candidates": [{ "path": "fixtures/inter.crt.pem" }],
"trustAnchors": [{ "path": "fixtures/root.crt.pem" }],
"crls": [{ "path": "fixtures/inter.crl.pem" }],
"ocspResponses": [{ "path": "fixtures/revoked.ocsp.der" }],
"requireRevocation": true,
"at": "2027-01-01T00:00:00Z",
"fields": ["valid", "reasons.code", "revocationEvidence", "pathLength"]
}
build_certificate_path
发现证书路径,不检查签名。输入: leaf、candidates[]、trustAnchors[]、at、purposes[]、policy、PARSE、PROJECTION。输出: found、structuralReasons[]、path[]、pathLength、explored、valid(始终为 false)、reasons[]、signaturesChecked: false。代码: PKI_REASON_SIGNATURE_NOT_CHECKED(始终存在);未找到通向信任锚的路径时,structuralReasons[] 中会包含 PKI_REASON_NO_TRUST_ANCHOR。
{ "leaf": { "path": "fixtures/leaf.crt.pem" }, "candidates": [{ "path": "fixtures/inter.crt.pem" }], "trustAnchors": [{ "path": "fixtures/root.crt.pem" }], "at": "2027-01-01T00:00:00Z", "verbosity": "summary" }
validate_certificate_path
对有序路径执行 RFC 5280 §6.1 验证。输入: path[](PKI_BUNDLE,从叶证书开始)、trustAnchors[]、at、checkSignatures(默认值为 true)、policy、allowSha1、PARSE、PROJECTION。输出: valid、reasons[]、failingReasons[]、path[]、signaturesChecked、signatures[](每个链接对应一个判定结果)、evaluatedAt。代码: PKI_REASON_SIGNATURE_INVALID、PKI_REASON_SIGNATURE_NOT_CHECKED(checkSignatures: false)、VALIDATION_ERROR。
{ "path": [{ "path": "fixtures/leaf.crt.pem" }, { "path": "fixtures/inter.crt.pem" }], "trustAnchors": [{ "path": "fixtures/root.crt.pem" }], "at": "2027-01-01T00:00:00Z" }
inspect_crl
RFC 5280 §5 报告。输入: crl(PKI_INPUT)、find(serialHex | certificate)、issuer(PKI_INPUT,进行一次签名检查)、allowSha1、PARSE、PROJECTION。输出: issuer(+ issuerString)、signatureAlgorithm、thisUpdate / nextUpdate、crlNumber、isDelta、baseCrlNumber、issuingDistributionPoint、entryCount、extensions[]、entry(revoked、revocationDate、reason、invalidityDate)、signature。代码: VALIDATION_ERROR、PKI_X509_*、PKI_CRYPTO_ALGORITHM_REFUSED。
{ "crl": { "path": "fixtures/inter.crl.der" }, "find": { "certificate": { "path": "fixtures/revoked.crt.pem" } }, "fields": ["entry", "issuerString", "crlNumber", "nextUpdate.iso"] }
check_crl_revocation
针对一张 CRL 检查一张证书。输入: certificate、crl、delta、issuer(CRL 签发者——若缺少此参数,则不会将 CRL 接受为证据:返回 valid: false 和 PKI_REASON_REVOCATION_UNKNOWN,这是一个判定结果)、at、staleTolerance(毫秒)、allowSha1、PARSE、PROJECTION。输出: valid、reasons[]、signatureVerified、deltaSignatureVerified、crlNumber、isDelta、deltaCrlNumber、evaluatedAt。若 delta 的签名无法通过 issuer 验证,则不会应用该 delta:返回 valid: false 和 PKI_REASON_REVOCATION_UNKNOWN。代码: reasons[] 中的 PKI_REASON_REVOKED(附带原因)、PKI_REASON_REVOCATION_UNKNOWN 及新鲜度 / 范围相关原因;VALIDATION_ERROR(delta 缺少 deltaCRLIndicator 时)。
{ "certificate": { "path": "fixtures/ed25519.crt.pem" }, "crl": { "path": "fixtures/ed25519.crl.der" }, "delta": { "path": "fixtures/ed25519-delta.crl.der" }, "issuer": { "path": "fixtures/ed25519.crt.pem" }, "at": "2027-01-01T00:00:00Z" }
create_ocsp_request
由你自行 POST 的 RFC 6960 请求。输入: certificate、issuer、hashAlgorithm(默认值为 SHA-1 | SHA-256)、nonce('random' 或 1–32 字节的十六进制值)、certIdOnly、label(encoding: 'pem' 时使用的 PEM 标签)、PARSE、OUTPUT(默认格式为 der,扩展名为 .orq)。**输出:**产物和 summary(certIdHex、nonceHex、randomNonce、序列号)。代码: VALIDATION_ERROR。
{ "certificate": { "path": "fixtures/leaf.crt.pem" }, "issuer": { "path": "fixtures/inter.crt.pem" }, "hashAlgorithm": "SHA-256", "nonce": "random", "encoding": "hex" }
inspect_ocsp_response
RFC 6960 报告。输入: response(PKI_INPUT)、responder(PKI_INPUT,进行一次签名检查;省略时会尝试使用内嵌证书)、PARSE、PROJECTION。输出: status、basicResponse(响应者 ID、producedAt、含 CertID / 状态 / 更新时间的 responses[]、扩展、证书、签名算法)、noncePresent、signature(valid、source)。代码: PKI_ASN1_*、PKI_X509_*、VALIDATION_ERROR。
{ "response": { "path": "fixtures/revoked.ocsp.der" }, "responder": { "path": "fixtures/ocsp.crt.pem" }, "fields": ["signature", "basicResponse.responses.status", "basicResponse.responses.certId.serialNumber", "noncePresent"] }
check_ocsp_status
一个证书对应一个响应。输入: response、certificate、issuer、responder、responderTrusted、hashAlgorithm、nonce(十六进制)、requireNonce、at、staleTolerance、futureTolerance、allowSha1、PARSE、PROJECTION。输出: valid、reasons[]、status(good | revoked | unknown | null)、signatureVerified、responderAuthorized、responder、responderSource、hashAlgorithm、certIdHex、evaluatedAt。代码: PKI_REASON_REVOKED,以及 reasons[] 中的新鲜度、nonce 和授权原因(非成功的响应状态属于否定判定,且 status: null);VALIDATION_ERROR。
{ "response": { "path": "fixtures/leaf.ocsp.der" }, "certificate": { "path": "fixtures/leaf.crt.pem" }, "issuer": { "path": "fixtures/inter.crt.pem" }, "nonce": "1538349c433373ed058c291909df5b3e", "requireNonce": true, "at": "2027-01-01T00:00:00Z", "verbosity": "summary" }
sign_cms
一个 RFC 5652 SignedData。输入: content(CONTENT_INPUT)或 contentDigest(HEX,仅适用于 detached)、detached、contentType(OID)、certificate、chain[]、crls[]、sid(默认为 issuerAndSerialNumber | subjectKeyIdentifier)、signingTime(某个时刻、'now'、'none')、signingCertificateV2、algorithmProtection、signedAttributes[] / unsignedAttributes[](十六进制 DER 属性)、signer(SIGNER)、allowSha1、PARSE、OUTPUT(默认为 der,.p7s)。输出: 产物和 summary(detached、contentType、signerSubject / signerSerial、sid、摘要和签名算法、signingTime 及 signingTimeSource、certificatesEmbedded、crlsEmbedded、属性计数、signatureVerified)。代码: KEY_MISMATCH(新签名无法通过签名者证书验证)、RSA_SCHEME_REQUIRED、SECRET_REQUIRED、KEY_TYPE_REQUIRED、VALIDATION_ERROR(detached 为 false 时提供了 contentDigest,或使用 signer.key 时缺少 certificate)、SECURITY_VIOLATION(content.path 指向包含密钥的扩展名,或文件中含有密钥材料——工具从沙箱读取的内容绝不会是密钥)、PKI_API_MISUSE(重复的或由构建器管理的属性类型)、ENGINE_ERROR。
{
"content": { "path": "fixtures/content.txt" },
"detached": true,
"certificate": { "path": "fixtures/leaf.crt.pem" },
"chain": [{ "path": "fixtures/inter.crt.pem" }],
"signingTime": "2027-01-01T00:00:00Z",
"signer": { "key": { "path": "fixtures/leaf.key.pem" } }
}
verify_cms
RFC 5652 判定。输入: signedData(PKI_INPUT)、content(CONTENT_INPUT)或 contentDigest(分离式签名)、TRUST(trustAnchors[]、candidates[]、crls[]、ocspResponses[]、at、requireRevocation、purposes[])、atTimeStamp、requireSigningCertificate、requireAlgorithmProtection、allowTrailingData、allowSha1、PARSE、PROJECTION。输出: valid、reasons[]、signers[](每个签名者包含:valid、intact、reasons[]、证书链、时间戳、吊销状态)、signerCount、signatureVerifications[]、evaluatedAt、atTimeStamp。代码: reasons[] 中的 PKI_REASON_*(NO_TRUST_ANCHOR、SIGNATURE_INVALID、REVOKED、TSP_* 等);PKI_API_MISUSE(为附带内容的消息提供了 content——消息本身包含内容);VALIDATION_ERROR(同时提供了 content 和 contentDigest);PKI_ASN1_TRAILING_DATA(未设置 allowTrailingData 时,PDF /Contents 中的零填充)。
{ "signedData": { "path": "fixtures/attached.p7s" }, "trustAnchors": [{ "path": "fixtures/root.crt.pem" }], "crls": [{ "path": "fixtures/inter.crl.pem" }], "ocspResponses": [{ "path": "fixtures/leaf.ocsp.der" }], "requireRevocation": true, "purposes": ["emailProtection"], "at": "2027-01-01T00:00:00Z" }
inspect_cms
RFC 5652 报告。输入: signedData、allowTrailingData、signerCertificate(检查一个密钥)、signerIndex、content(用于没有已签名属性的签名者)、allowSha1、PARSE、PROJECTION。输出: version、contentType、contentLength(null = 分离式)、digestAlgorithms[]、certificates[]、crlCount、ocspResponseCount、signerInfos[](sid、算法、声明的签名时间、属性、时间戳计数、十六进制格式的 signature)、signerSignature。代码: NOT_FOUND(提供 signerCertificate 时,signerIndex 超出最后一个签名者的索引——该索引用于指定要检查的签名者)、PKI_ASN1_TRAILING_DATA、PKI_*。
{ "signedData": { "path": "fixtures/detached.p7s" }, "signerCertificate": { "path": "fixtures/leaf.crt.pem" }, "content": { "path": "fixtures/content.txt" }, "fields": ["signerInfos.signature", "signerInfos.sid", "signerSignature"] }
amend_cms
唯一合法的编辑操作。输入:signedData、signerIndex、operation(add-unsigned-attribute | add-timestamp)、attribute(HEX)、token(PKI_INPUT — 一个 .tst 或完整的 .tsr)、allowSha1、PARSE、OUTPUT(默认 der)。**输出:**工件和 summary。代码:VALIDATION_ERROR,其 data.detail 为 PKI_REASON_TSP_IMPRINT_MISMATCH(令牌的内容不是签名者的签名值 — 解决方法会指出 inspect_cms → create_timestamp_request → amend_cms 的顺序)、NOT_FOUND、PKI_API_MISUSE(有符号属性类型)、PKI_*。
{ "signedData": { "path": "fixtures/attached.p7s" }, "operation": "add-unsigned-attribute", "attribute": "300d06032a0304310606042a030405" }
create_timestamp_request
由你自行 POST 的 RFC 3161 请求。输入:data(CONTENT_INPUT)或 digestHex(HEX)、hashAlgorithm(默认 SHA-256 | SHA-384 | SHA-512)、nonce('random'、十进制或 0x 十六进制)、policy(OID)、certReq(默认 true)、label(encoding: 'pem' 时使用的 PEM 标签)、PARSE、OUTPUT(默认 der,.tsq)。**输出:**工件和 summary(imprintHex、hashAlgorithm、nonce、randomNonce、policy、certReq)。代码:VALIDATION_ERROR(摘要长度与算法不匹配)、SECURITY_VIOLATION(data.path 指向含密钥的扩展或包含密钥材料)。
{ "data": { "path": "fixtures/content.txt" }, "nonce": "random" }
inspect_timestamp
RFC 3161 报告。输入:input(PKI_INPUT)、as(response | token | tstinfo)、PARSE、PROJECTION。输出:as、status(statusStrings、failInfo)、token(CMS 信封:签名者数量、证书)、tstInfo(policy、messageImprint、serialNumber、genTime、accuracy、ordering、nonce、tsa、extensions)。代码:PKI_*(字节不属于这三种结构中的任何一种)、PKI_STRICT_DIAGNOSTIC(设置 strict: true 时 — OpenSSL 令牌包含 PKI_DIAG_CMS_SET_NOT_SORTED)。
{ "input": { "path": "fixtures/content.tst" }, "as": "token", "fields": ["token.tstInfo.genTime.iso", "token.tstInfo.messageImprint", "token.certificates.subject"] }
verify_timestamp
RFC 3161 验证结果。输入:token 或 response、request(你发送的 .tsq — 会检查 imprint、nonce 和 policy)、data(CONTENT_INPUT)或 digestHex、TRUST(trustAnchors[]、candidates[]、crls[]、ocspResponses[]、at、requireRevocation)、allowNonCriticalTimeStampingEku、allowSha1、PARSE、PROJECTION。输出:valid、reasons[]、genTime、earliest / latest(已应用精度)、tsa、chain({ valid, path[], reasons[] })、signatureVerifications[]、evaluatedAt。代码:PKI_REASON_TSP_IMPRINT_MISMATCH、PKI_REASON_TSP_NOT_GRANTED、PKI_REASON_TSP_REQUEST_MISMATCH、PKI_REASON_TSP_TOKEN_INVALID 以及 reasons[] 中的证书链原因;VALIDATION_ERROR(未提供任何被加盖时间戳的内容 — 在没有时间戳内容的情况下验证令牌,无法证明任何事情)。
{ "response": { "path": "fixtures/content.tsr" }, "request": { "path": "fixtures/content.tsq" }, "data": { "path": "fixtures/content.txt" }, "trustAnchors": [{ "path": "fixtures/root.crt.pem" }], "candidates": [{ "path": "fixtures/inter.crt.pem" }], "at": "2027-01-01T00:00:00Z" }
inspect_private_key
描述 PKCS#8 密钥,但不包含任何密钥字节。输入:key(PKI_INPUT)、tryImport、password(或 PKINATIVE_MCP_PASSWORD)、keyType(ec-p256 | ec-p384 | ec-p521 | ed25519 | ed448 | rsa | rsa-pss)、hash、rsaScheme、saltLength、allowSha1、PARSE、PROJECTION。输出:format(pkcs8 | encrypted-pkcs8)、kind、curve、algorithm + algorithmName、attributes、publicKey(是否嵌入)、encryption(方案、PBKDF2 迭代次数、PRF、AES 密钥大小、盐长度)、encryptedBytes、imported(句柄类型、算法、用途)。代码:SECRET_REQUIRED、KEY_TYPE_REQUIRED、RSA_SCHEME_REQUIRED、PKI_CRYPTO_DECRYPTION_FAILED(密码错误)、PKI_KEY_ENCRYPTION_UNSUPPORTED(旧式密码算法,解决方法是使用 openssl pkcs8)、PKI_*(PUBLIC KEY 或非 PKCS#8 DER)。
{ "key": { "path": "fixtures/leaf.key.enc.pem" }, "tryImport": true, "password": "test-only-password", "keyType": "ec-p256", "fields": ["imported", "encryption.scheme"] }
inspect_pkcs12
描述 PKCS#12,不包含密钥字节。输入:pkcs12(PKI_INPUT;对于由 Windows 写入的 PFX,使用 encodingRules: 'ber' 的 BER)、password(可选)、PARSE、PROJECTION。输出:version、mac(类型为 pbmac1 | pkcs12-kdf | null,含参数)、contents[](路径、加密方案及参数、明文内容的包)、bagCount、macVerified(提供密码时才验证;若没有 SafeContents 加密,仅报告 false;否则,错误密码会在报告 MAC 前触发 PKI_CRYPTO_DECRYPTION_FAILED)、opened[](允许列表视图:按主题 / 颁发者 / 序列号展示 certBags;按算法、曲线和加密参数展示密钥包;按大小展示 crlBags)。设置 PKINATIVE_MCP_PASSWORD 后,该变量的值会被视为密码;若要在不提供密码的情况下描述文件,需取消设置该变量。代码:PKI_CRYPTO_DECRYPTION_FAILED(对加密 SafeContents 使用错误密码时触发)、PKI_KEY_MAC_UNSUPPORTED、PKI_KEY_ENCRYPTION_UNSUPPORTED(每种情况均附有 OpenSSL 3.4 转换补救方法)。
{ "pkcs12": { "path": "fixtures/legacy.p12" }, "fields": ["mac.kind", "contents.path", "contents.encryption.scheme"] }
open_pkcs12
一次调用即可打开 PKCS#12。输入:pkcs12、password(或 PKINATIVE_MCP_PASSWORD)、allowUnverifiedIntegrity、rsaScheme(pkcs1 | pss)、hash、exportCertificates、PARSE、PROJECTION、OUTPUT(仅支持 pem——证书包没有 DER 格式)。输出:valid、integrity(verified | unverified | mismatch)、reasons[]、keys[](算法、句柄类型、用途——绝不包含字节)、certificates[]、keyCount / certificateCount / crlCount、exportSkipped、artefact(PEM 证书包)。代码:SECRET_REQUIRED(两个渠道均未提供密码);reasons[] 中可能包含:PKI_REASON_PKCS12_MAC_MISMATCH(密码错误或文件被篡改——不会解密任何内容)、PKI_REASON_PKCS12_ENCRYPTION_UNSUPPORTED、PKI_REASON_PKCS12_INTEGRITY_UNVERIFIED、PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED;MISSING_OUTPUT_PATH、OUTPUT_TOO_LARGE。
{ "pkcs12": { "path": "fixtures/leaf.p12" }, "password": "test-only-password", "exportCertificates": true }
describe_engine
离线预检。**输入:**仅 PROJECTION。输出:server(名称、版本、toolApiVersion、协议、工具数量)、engine(名称、版本、nodeFloor)、capabilities(canVerify、canSign、canDecrypt、node、nodeFloorSatisfied、平台)、randomness、defaultLimits、ceilings、caps、sandbox(enabled、cacheEnabled、pinnedInstant、扩展允许列表)、network: 'none'、registries(57 / 43 / 97 个代码、包装器代码、用途、密钥用途位、OID 注册表数量)、refusals(原则)、unexposed。代码:VALIDATION_ERROR。
{ "fields": ["defaultLimits", "ceilings", "sandbox", "capabilities"] }
explain_code
解释任意代码。输入:code,或使用 kind(error | reason | diagnostic | wrapper)的 list: true、PROJECTION。输出:code、kind、summary、standard、severity、errorClass、engineClass、cwe、since、remedy——或 codes[] + count。代码:NOT_FOUND(代码未知)。
{ "code": "PKI_REASON_PKCS12_RSA_SCHEME_UNSPECIFIED" }
draft_governance_issue
在本地起草符合治理规范的 GitHub issue,供人工审核和提交——适用于 pkinative(引擎行为:解析、验证、限制、拒绝)、pkinative-cli 或 pkinative-mcp(封装行为:架构、沙箱、传输)。服务绝不会联系 GitHub——完全没有网络代码路径;它会返回草稿 Markdown 和机器可读的合规报告。将两者都呈现给用户,然后停止:由用户审核并以自己的身份提交。输入: title、summary、issueType(bug | feature | security | docs | performance)、targetRepo、reproduction({ command, result })、expectedBehavior、actualBehavior、affectedPackages[]、duplicateSearchPerformed(必须为 true)、outputMode(inline | file)+ outputPath(沙箱中的相对 .md 路径)。输出: draftMarkdown、warnings[]、compliance(.github/AGENT_RULES.md 中的九个键)。错误代码: GOVERNANCE_VIOLATION(拟议运行时依赖、嵌入的私钥或密码、没有复现步骤、duplicateSearchPerformed: false)、MISSING_OUTPUT_PATH、INVALID_EXTENSION、OUTPUT_EXISTS、SECURITY_VIOLATION。
{
"title": "parseCertificateList refuses a delta CRL with an empty revokedCertificates list",
"summary": "A delta CRL (deltaCRLIndicator present) whose revokedCertificates SEQUENCE is absent is refused with PKI_X509_STRUCTURE_INVALID although RFC 5280 makes the field OPTIONAL.",
"issueType": "bug",
"targetRepo": "pkinative",
"reproduction": { "command": "inspect_crl { crl: { path: \"fixtures/empty-delta.crl\" } }", "result": "PKI_X509_STRUCTURE_INVALID at revokedCertificates" },
"expectedBehavior": "The delta CRL parses with entryCount 0.",
"affectedPackages": ["pkinative", "pkinative-mcp"],
"duplicateSearchPerformed": true
}
完整的人机协同约定,请参阅 governance_contract 和 draft_issue_workflow 提示词,以及 docs/guides/AI_GOVERNANCE.md。
🧩 共享输入片段
同样的十个片段会出现在整个目录中;工具描述会列出其包含的片段。每个对象架构均为封闭架构(additionalProperties: false,每一层级都使用 Zod .strict()):遇到未知或拼写错误的键时,会返回标明路径的 VALIDATION_ERROR,绝不静默丢弃。
| 片段 | 形式 | 规则 |
|---|---|---|
PKI_INPUT | { pem } | { derBase64 } | { path } | 必须且只能提供一项。pem 是以 -----BEGIN 开头的 RFC 7468 文本(如果 PEM 文本包含多个区块,但预期只有一个对象,则会被拒绝——请使用 convert_pem extractIndex 或捆绑包);derBase64 是且仅是一次以 base64 编码的 DER(不是 data: URI,也不是 PEM);path 相对于沙盒,格式通过首字节识别为 PEM 或 DER,扩展名必须在允许列表中,且绝不缓存。解析任何字节前都会检查标签(PKI_PEM_UNEXPECTED_LABEL)。 |
PKI_BUNDLE | PKI_INPUT[](1–256 项) | trustAnchors、candidates、crls、ocspResponses、chain、path:按顺序取用每项中的所有 PEM 区块。 |
CONTENT_INPUT | { text } | { base64 } | { path } | 要签名、验证或加时间戳的字节:UTF-8 文本、base64(解码后 ≤ 128 MiB),或沙盒文件(任意扩展名,但不能是包含密钥的扩展名,≤ 1 GiB——这是内容,不是 PKI 对象;包含密钥材料的文件会触发 SECURITY_VIOLATION,因为工具读取的内容绝不会是密钥)。 |
HEX | 字符串 | contentDigest、digestHex、valueHex、attribute、nonce、ocspNonce、serialHex:十六进制;允许使用 0x 前缀、冒号和空格(^(?:0x)?(?:[0-9a-fA-F]{2}[:\s]?)+$)。 |
PARSE | encodingRules、pemMode、strict、limits | encodingRules 为 'der'(严格 X.690,默认值)或 'ber'(允许不定长/非最小长度,如旧版 CMS 生成器所写);pemMode 为 'strict'(严格遵循 RFC 7468)或 'lax'(允许空白、行长限制、RFC 1421 标头);strict: true 会将引擎的首条警告提升为 PKI_STRICT_DIAGNOSTIC;limits 见下文。 |
LIMITS | { maxInputBytes, maxDepth, … }(22 个键) | 仅对可信输入提高本次调用的引擎命名限制;未指定的键沿用引擎默认值;每个值都必须是 ≥ 1 的整数;maxInputBytes、maxKdfIterations/maxPkcs12KdfIterations 和 maxPathsExplored 受操作员上限约束(LIMIT_CEILING_EXCEEDED);在提高后的 maxDepth 下耗尽调用栈时,会返回带代码的 ENGINE_ERROR,并指出补救方法(降低该值)。 |
SIGNER | { key } | { p12 },+ password、keyType、hash、rsaScheme、saltLength | key(PKCS#8 私钥,未加密或使用 PBES2 加密,PEM 或 DER 格式——SEC1/PKCS#1 密钥必须先用 openssl pkcs8 -topk8 转换)和 p12(仅支持 PBES2 + PBMAC1)中必须且只能提供一项。密钥会导入为不可提取的 Web Crypto 密钥,且不会离开引擎。算法由密钥决定:ECDSA 使用密钥对应的曲线(根据曲线使用 SHA-256/384/512)、Ed25519/Ed448(不使用哈希);RSA 必须指定 rsaScheme('pkcs1' | 'pss',saltLength 可选);加密密钥需要提供 password(或 PKINATIVE_MCP_PASSWORD)和 keyType,除非证书或公钥输入中已指定密钥类型。包含签名者的调用绝不缓存。 |
TRUST | trustAnchors、candidates、crls、ocspResponses、at、requireRevocation、purposes、policy | 判定结果取决于你指定的信任锚(至少一个)。at 为 ISO 8601 日期时间(未指定时区则默认为 UTC)、纪元毫秒数或 'now';省略时使用 PKINATIVE_MCP_AT,否则使用系统时钟(绝不缓存)。requireRevocation: true 要求终端实体证书具有新鲜证据(否则返回 PKI_REASON_REVOCATION_UNKNOWN——这是引擎的规则;中间证书则根据你传入的证据判定,其状态会在 revocationEvidence 中报告)。purposes(≤ 16)为 EKU 名称或 OID;policy 包含 RFC 5280 §6.1.1 的输入(initialPolicySet、requireExplicitPolicy、inhibitPolicyMapping、inhibitAnyPolicy)。 |
PROJECTION | verbosity、fields | 'full'(默认值)或 'summary';最多保留 16 个点分隔路径(在 verbosity 处理后保留;数组会映射到各元素);所有未匹配的路径(包括嵌套路径)都会通过 _meta.availableFields 在 _meta.unmatchedFields 中报告;包含 __proto__/constructor/prototype 段的路径会触发 VALIDATION_ERROR。 |
OUTPUT | outputMode、outputPath、encoding | 'base64'(默认值,内联 ≤ 50 MiB)或 'file'(相对路径 outputPath,其扩展名必须与编码匹配——pem 使用 .pem;der 使用 .der .crt .cer .csr .p10 .crl .p7s .p7m .p7b .cms .orq .tsq .bin .asn1 …;拒绝包含密钥的扩展名;绝不覆盖;≤ 1 GiB)。encoding 为 'pem'、'der' 或 'hex'(仅内联,≤ 64 KiB;十六进制内容写入 structuredContent.hex)。 |
每个结果还包含 diagnostics[](按代码和路径去重,上限为 200,并附带 diagnosticsTruncated)以及 diagnosticCounts;如果某个工具需要密码,密码输入就是一个普通字符串(≤ 4096 个字符),绝不会回显、记录或缓存。 |
🔐 安全模型
pkinative-mcp 在宿主进程内部运行,并提供 stdio MCP 服务(或仅限 loopback 的 HTTP 端点)。它将每个证书、请求、列表、响应、消息、令牌、密钥和 PKCS#12 都视为不可信输入,并且除 PKINATIVE_MCP_CACHE_DIR 下的可选响应缓存外,不会在配置的沙箱之外执行任何 I/O。
- 机密信息:两条通道,绝无第三条。 密码通过每次调用的
password输入或操作员侧的PKINATIVE_MCP_PASSWORD传给服务端;每次调用只能使用一个来源(同时提供两者会返回VALIDATION_ERROR);两者都未提供时返回SECRET_REQUIRED。密码绝不会出现在结果或错误文本中,绝不会被记录(启动时只报告该变量是否存在),绝不会写入缓存文件,也绝不会作为资源提供。私钥会导入为不可提取的 Web Crypto 句柄;每个密钥和 PKCS#12 报告都是逐字段构建的允许列表视图(src/key-views.ts),测试套件通过根据测试夹具计算出的针表证明,任何结果、stderr、缓存文件或 stdio 传输记录中都不会打印密钥字节、标量、JWK 成员、加密密钥正文或密码(tests/secret-leak.test.ts)。 - 沙箱(
PKINATIVE_MCP_OUTPUT_DIR)是读取(每个path输入、resources/read)和写入(outputPath、治理草稿)唯一的文件系统边界。路径必须是相对路径;绝对路径、驱动器路径、UNC 路径、..、NUL 字节以及不在允许列表中的扩展名都会被拒绝;读取文件的实际路径和写入文件的父目录都必须位于沙箱内(在沙箱中放置符号链接或联接点,读取和写入都会返回SECURITY_VIOLATION);文件以独占方式打开(返回OUTPUT_EXISTS,绝不覆盖);写入失败时会删除部分文件。包含密钥的扩展名(.key .p8 .p12 .pfx)只能由密钥工具读取,绝不写入、列出或通过resources/*提供;并且resources/list/resources/read还会按内容拒绝密钥材料,不论文件名是什么:前缀之后的 PEM 密钥、多个区块中隐藏的密钥、杂项数据后面的原始 DER 密钥,或文本文件中的 Base64 密钥材料,都会触发SECURITY_VIOLATION。同样的内容检查也会保护通用编码器(convert_pem encode只接受一个 DER 值,并拒绝密钥材料)和内容输入(sign_cms的content.path、create_timestamp_request的data.path):工具从沙箱读取为内容的数据绝不会是密钥。未设置时 ⇒ 仅支持内联 PEM / Base64。 - 上限 — 每个字节数和数量上限都是
src/caps.ts中的常量,并由describe_engine.caps公布:
| 上限 | 值 |
|---|---|
| stdio 帧 / HTTP 正文 | 256 MiB |
每次调用解码后的内联 PKI 输入 / 内联 content | 128 MiB / 128 MiB |
完整读入内存的 content.path | 1 GiB |
| 内联输出(工件、资源读取) | 50 MiB |
| 文件输出 | 1 GiB |
| 内联十六进制工件 | 64 KiB |
| Bundle 项 / 内联 DER 属性 / 每次调用的 OID | 256 / 256 / 1000 |
resolve_oid 列表分页 | 默认 200 / 最大 2000 |
SHAKE256 输出 / 密码 / fields 路径 / decode_asn1 预览 / 树节点 | 1 MiB / 4096 / 16 / 32 字节 / 20 000 |
| 每个结果的诊断信息 | 200(按代码和路径去重) |
resources/list 条目数 / 遍历深度 / resources/read | 1000 / 8 / 50 MiB |
| 响应缓存 TTL / 大小 | 3600 s / 256 MiB |
引擎默认值(limits) | pkinative 1.0.0 的 22 个 PkiLimits — maxInputBytes 64 MiB、maxKdfIterations 10 000 000、maxPathsExplored 1000、……(describe_engine.defaultLimits) |
- 缓存门控(
isCacheable,src/server.ts)——设置PKINATIVE_MCP_CACHE_DIR后,只有当结果完全不依赖环境、秘密、时钟或随机性时,才会从缓存中提供:绝不缓存任何包含path输入的结果(路径是引用,而非内容),绝不缓存outputMode: 'file',绝不缓存携带password或signer的调用,绝不缓存'random'序列号或随机数,绝不缓存'now'或未固定的时间点,绝不缓存describe_engine或draft_governance_issue,并且设置PKINATIVE_MCP_PASSWORD时,绝不缓存任何接受密码的工具(操作员密码可能在输入不含任何秘密的情况下被使用)。缓存键命名空间包含工具 API、软件包和引擎版本,因此引擎升级后绝不会提供由旧版引擎写入的字节。 - 原则性拒绝(
describe_engine.refusals)——服务不会逾越的界限及各自的补救措施:不联网;不生成密钥(请从 HSM、KMS 或openssl genpkey导入密钥);不导出密钥;不提供 PKCS#8 / PKCS#12 / CRL / OCSP 响应 / 时间戳响应的写入器(请使用签发软件或 OpenSSL,并在此验证结果);默认拒绝 SHA-1;不提供默认 RSA 方案;拒绝旧版 PKCS#12 和 PBES1 密钥(使用 OpenSSL 3.4+ 转换一次:openssl pkcs12 -legacy …,然后执行-export -pbmac1_pbkdf2);可解析 DSA / ML-DSA,但绝不验证或签名;诊断信息不属于错误;只有显式提高限制且不超过上限时才会提高限制;随机性由封装层提供并报告(randomSerial,randomNonce)。 - 输入在每个工具的边界处都通过严格的 JSON Schema + Zod 验证——任何深度上的未知键或拼写错误的键都会触发
VALIDATION_ERROR;在运行任何解析器之前,会先对 base64 进行合理性检查(拒绝data:URI、在应为 DER 的位置出现 PEM、双重编码或空载荷,并提供补救措施);规范采用防原型污染的方式构建(tests/prototype-pollution.test.ts)。 - HTTP 传输(
PKINATIVE_MCP_PORT)仅绑定到环回地址,并进行 Host / Origin 检查;除非设置PKINATIVE_MCP_HTTP_TOKEN,否则没有身份验证(设置后,没有有效的 bearer token 即返回401)。
网络与出站连接:无
永远不会有。 服务不存在任何网络代码路径:不会获取 CRL、OCSP、AIA 或 TSA,不会进行遥测,不会访问 GitHub,不会检查更新,不会使用任何工具参数中的 URL,而且——与其 PDF 同类工具不同——不存在任何可由操作员配置的端点。describe_engine 会报告 network: 'none',每个工具都会声明 openWorldHint: false。吊销证据和时间戳令牌都是你通过带外方式获取的输入;create_ocsp_request 和 create_timestamp_request 会生成由你传输的字节(application/ocsp-request、application/timestamp-query)。引擎本身绝不会打开套接字、访问文件系统或执行 eval。
请参阅 SECURITY.md,了解负责任的漏洞披露流程和威胁模型;另请参阅 docs/guides/SECURITY.md,了解带有 CWE 编号的防护措施。
🧪 一致性(OpenSSL 3)
OpenSSL 3.x 是此服务的外部判定工具——它对 pdfnative-mcp 的作用,就像 veraPDF:工具写入的内容,必须能由另一种实现读取并验证。npm run corpus:pki 会在固定时刻驱动构建后的服务(scripts/generate-pki-corpus.ts,语料库表位于 scripts/lib/pki-corpus.ts),将互操作性语料库写入 test-output/pki/,并生成 manifest.json(状态、SHA-256、每个确定性条目的第二个摘要):23 个符合要求的产物——涵盖所有签名系列的自签名 CA 和叶证书(Ed25519、RSA PKCS#1 v1.5、RSA-PSS、ECDSA P-256)、一个 pathLen 为 0 的中间证书及其下属叶证书、三个 CSR、附带和分离式 CMS 消息(包括 Ed25519 签名者)、两个时间戳请求、两个 OCSP 请求,以及 DER 样例的 PEM 重编码版本——以及5 个必须按声明代码拒绝的测试样例(SHA-1 签名、旧式 PKCS#12 签名者、缺少方案的 RSA 密钥、早于 notBefore 的 notAfter、规范中的 URL 成员)。若测试样例写入了字节,则视为 XPASS,并导致运行失败。
随后,npm run validate:pki(scripts/validate-pki.ts)会在两个级别检查每个产物:
- 级别 0——引擎往返测试,在进程内通过
dist/server.js执行:检查每张证书,并在固定时刻验证其是否可追溯至语料库 CA(对叶证书使用verify_certificate_chain,并提供serverName和purposes);读取每个请求并检查其持有证明;使用信任锚验证每条 CMS 消息(以及分离式内容);使用node:crypto独立解码并重新计算每个 OCSP 和时间戳请求(CertID 哈希、序列号、消息摘要、nonce);并对每个确定性条目各生成两次,确认哈希相同。此级别无需外部工具,始终运行。 - 级别 1——OpenSSL,要求
PATH中有 OpenSSL 3.x(可通过OPENSSL_BIN覆盖;拒绝 LibreSSL 和低于 3.0 的 OpenSSL,并说明原因):12 类命令——对每张证书(DER 和 PEM)运行x509 -text;验证每个语料库 CA(叶证书使用-purpose sslserver,中间证书使用-untrusted,固定时刻使用-attime);运行req -verify、crl -text、cms -verify(附带和分离式消息,使用-CAfile指定样例根证书)、asn1parse、ocsp -req_text、ts -query -text——原始命令记录位于test-output/pki/reports/。若未安装 OpenSSL,则跳过并说明原因;若设置了PKINATIVE_INTEROP_REQUIRED=1或 gate 使用了--require-all,则失败。
覆盖率测试样例会将语料库限制在 docs/data/interop.json 中的 declared.conformant / declared.refused,以免静默回归缩小已验证范围。验证器会逐文件报告 PASS / FAIL / XFAIL / XPASS / INFRA / SKIP;退出代码:0 表示所有预期均已满足,1 表示出现 FAIL / XPASS / 测试样例失败,2 表示缺少语料库或 dist/,3 表示要求运行 OpenSSL 但未安装。相同级别 1 检查的 vitest 版本位于 tests/interop/openssl.test.ts(npm run test:interop);tests/interop/lint.test.ts(npm run test:lint-certs,发布配置)会使用 scripts/data/lint-waivers.json 中经审核的豁免项,在语料库上运行 zlint 和 pkilint 证书检查工具(目前没有豁免项)。
此 gate 具有阻断性:ci 工作流会在 Linux(Node 22 和 24)、Windows 和 macOS 上使用 --require-all 运行它;interop 工作流则会在四个 OS 任务中重复运行(ubuntu-22.04、ubuntu-24.04、windows-latest、macos-latest),其中 interop (ubuntu-24.04) 和 interop (windows-latest) 是必需的状态检查。OpenSSL 是运行器工具,而非依赖项。PASS 表示针对特定 OpenSSL 修订版的验证证据(开发主机上为 3.5.5),并非认证。请参阅 docs/guides/CONFORMANCE.md。
自主性证明
tests/autonomy.test.ts 会针对构建好的 dist/cli.js,在四种传输方式与协议时代的组合下(stdio legacy、stdio modern、HTTP legacy、使用 PKINATIVE_MCP_HTTP_TOKEN 的 HTTP modern),重放一个脚本化的 Agent 场景(S0–S18:握手 → 目录 → describe_engine → inspect_certificate 及线路上的缓存命中 → 带有 CRL 和 OCSP 证据的证书链判定 → 名称和用途 → 在文件模式下创建 CA、CSR 并签发叶证书 → CMS 签名、验证后再篡改 → 构建时间戳请求并验证响应 → 打开 PKCS#12,且记录中不含任何密钥字节 → resources/* → 拒绝恶意输入并提供代码和补救办法 → 各项上限 → 协议错误 → draft_governance_issue → prompts/get → explain_code → 正常关闭)。测试运行于一个包含测试夹具和缓存目录的沙盒中,并降低 PKINATIVE_MCP_MAX_PATHS_EXPLORED 上限;每次调用都会固定时间点(场景会断言 pinnedInstant: null)。测试会断言没有交互、没有意外的 isError、每次拒绝都提供代码和补救办法、线路上发生缓存命中、任何位置都没有秘密标记串(stdout、stderr、缓存、沙盒)、stdout 仅包含 JSON,且程序正常退出。若要通过 Claude Code、Claude Desktop 或 MCP Inspector 手动重放,请参阅 docs/guides/LOCAL_TESTING.md(仓库附带了现成的 .mcp.json)。
🧪 本地开发
git clone https://github.com/Nizoka/pkinative-mcp.git
cd pkinative-mcp
npm ci --ignore-scripts # .npmrc sets ignore-scripts=true: nothing builds on install
npm run gate:fast # before every commit: typecheck:all, lint, test, code-tables, server-json, verify:docs
npm run gate # the CI profile: + build, dist checks, stdio smoke test, tool shape, samples, coverage, docs, the OpenSSL corpus and interop
npx tsx scripts/gate.ts --publish --require-all # release branches: everything incl. lint:certs (zlint / pkilint); a skipped step fails
npm run gate (scripts/gate.ts) 是绿色构建的唯一标准——111 个文件中的 2381 项测试、vitest.config.ts 的覆盖率阈值(94 / 90 / 95 / 94),以及通过 stdio 驱动的构建后服务;其 stdout 必须只包含 JSON-RPC 帧,且 dist/ 中不得包含任何测试密钥字节。它每个步骤打印一行,并将日志写入 test-output/.gate/<step>.log;--only <step> 运行单个步骤,--from <step> 从指定步骤继续,--json 输出机器可读内容(要传递标志,请直接调用脚本:PowerShell 会吞掉 npm run 后单独出现的 --)。各个步骤对应普通的 npm 脚本:
| 脚本 | 功能 |
|---|---|
npm run typecheck:all | 对 src/ + tests/ 运行 tsc --noEmit,然后检查 scripts/ |
npm run lint | eslint src --max-warnings 0——出现警告即失败 |
npm test / npm run test:coverage | vitest(点状报告器)/ 使用 v8 覆盖率——阈值见 vitest.config.ts |
npm run build | tsc -p tsconfig.build.json → dist/ |
npm run examples:check | 通过 tools/call 处理程序运行 89 个 examples/*.json 示例(多步示例会串联执行) |
npm run test:generate / npm run verify:samples | 98 个样本基线:在隔离环境中运行构建后的服务 → test-output/samples/,并与 tests/_fixtures/samples.sha256.json 比对(10 个逐字节精确匹配,3 个按其 TBS 结构匹配,85 个语义匹配) |
npm run corpus:pki / npm run validate:pki | 生成 OpenSSL 语料库(23 + 5)到 test-output/pki/,并按级别 0 和 1 验证 |
npm run test:interop / npm run test:lint-certs | 级别 1 的 vitest 实现形式(PKINATIVE_INTEROP_REQUIRED=1 会在跳过时失败)/ zlint + pkilint 检查器 |
npm run verify:tool-shape | 在 448 KiB 目录和 8 KiB 指令预算内,将构建后的 tools/list 结构与 tests/_fixtures/tool-shape.json 比对(仅在有意更改架构后运行 npx tsx scripts/tool-shape.ts --write) |
npm run verify:docs | 根据 docs/assets/ecosystem.json 和源代码树核验文档中的所有计数、版本、工具、代码、运维变量、链接和锚点 |
npm run agents:rules | 根据 .github/instructions/*.instructions.md 重新生成 .claude/rules/*.md |
npm run release:prepare | 执行机械式版本升级(绝不提交、打标签或发布) |
npm run hooks:install / hooks:uninstall | 可选的 git hooks(pre-commit:lint + CRLF 防护;pre-push:快速门禁) |
所有脚本、其标志和退出代码都列在 scripts/README.md 中。tests/fixtures/pki/ 下的 42 个固定样本由 OpenSSL 3.5.5 生成(tests/fixtures/PROVENANCE.md),并由 SHA256SUMS 固定,绝不在原处重新生成:其中每个私钥都是公开的、仅供测试使用的材料(密码为 test-only-password)。
本机上的 OpenSSL。 级别 1 需要 PATH 中有 openssl 3.x(或设置 OPENSSL_BIN)。Linux:安装发行版软件包。macOS:运行 brew install openssl@3(系统自带的 /usr/bin/openssl 是 LibreSSL,程序会拒绝使用并说明原因)。Windows:将 OpenSSL 3 版本加入 PATH,或使用随 Git for Windows 附带的版本(usr/bin/openssl.exe);如果它不在 PATH 首位,请将 OPENSSL_BIN 指向它。缺少该程序时,validate:pki 和 interop 会跳过并说明原因;但在 --require-all 下会失败。
通过 stdio 对服务进行冒烟测试:
node dist/cli.js
# In another terminal, send a JSON-RPC initialize request via stdin (e.g. with the MCP Inspector).
npx @modelcontextprotocol/inspector --cli node dist/cli.js --method tools/list
使用 bearer token 和沙箱通过 HTTP 运行:
PKINATIVE_MCP_PORT=3000 PKINATIVE_MCP_HTTP_TOKEN=change-me-to-a-real-secret PKINATIVE_MCP_OUTPUT_DIR=/tmp/pki npx pkinative-mcp
# POST http://127.0.0.1:3000/mcp with Authorization: Bearer …
贡献者: 请从
AGENTS.md开始(其中包含所有编码 Agent 和贡献者共用的仓库规则),并参阅docs/guides/LOCAL_TESTING.md,了解完整的本地验证工作流——质量门禁、作为测试的示例、样本基线、OpenSSL 语料库,以及如何通过 Claude Code 或 MCP Inspector 驱动构建后的服务。
📣 发布流程
pkinative-mcp 遵循与 pkinative、pdfnative-mcp 和 zipnative-mcp 相同的发布规范:
- 每个标签对应一个发布说明文件,路径为
release-notes/vX.Y.Z.md CHANGELOG.md会同步每个版本的要点列表- GitHub Release 正文会从
release-notes/vX.Y.Z.md复制 npx tsx scripts/release-prepare.ts --version X.Y.Z会完成版本升级中的机械性操作(不会提交、打标签或发布);package.json、src/version.ts、server.json和CITATION.cff(tests/metadata.test.ts)中的版本号会保持同步,docs/assets/ecosystem.json中的版本号也会同步(npm run verify:docs,规则manifest-shape)- 发布分支必须通过
npx tsx scripts/gate.ts --publish --require-all——所有步骤都必须通过,包括 OpenSSL,且不得跳过任何步骤(安装了 zlint 和 pkilint 时会运行证书检查器) - npm 发布通过 GitHub Actions Trusted Publishing(OIDC)处理,无需
NPM_TOKEN,并从受保护的npm-publish环境执行:工作流会检查标签是否与软件包版本一致、运行发布门禁,并使用--provenance发布;第二个作业会为 tarball 附加构建来源证明和 CycloneDX SBOM,并将二者附加到 GitHub Release - 每个 Linux 和 Windows 作业都以
step-security/harden-runner开始(该 action 不支持 macOS;文档中注明 macOS 作业是例外),检出时设置persist-credentials: false,使用npm ci --ignore-scripts安装,并以最小权限运行;actions 均固定到提交 SHA;OpenSSL 互操作性门禁会阻止发布 - 人工参与:编码 Agent 负责准备和验证;维护者负责推送、创建拉取请求、打标签和发布(
.github/AGENT_RULES.md)
规范结构和发布检查清单见 release-notes/TEMPLATE.md;发布流程见 CONTRIBUTING.md。
📚 项目结构
src/
├── cli.ts # entrypoint: stdio (default, 256 MiB frames) or Streamable HTTP (PKINATIVE_MCP_PORT); reads the operator knobs once
├── http.ts # Node http <-> Web Request/Response bridge, 256 MiB body cap, Host/Origin loopback guard
├── auth.ts # opt-in HTTP bearer token (PKINATIVE_MCP_HTTP_TOKEN)
├── server.ts # Server factory, dispatch by tool kind, cache hints, the cache gate, SERVER_INSTRUCTIONS, 7 prompts, resources
├── caps.ts # every byte / count cap + the three operator ceilings
├── limits.ts # the 22 PkiLimits as the `limits` fragment; LIMIT_CEILING_EXCEEDED
├── sandbox.ts # the single filesystem boundary (PKINATIVE_MCP_OUTPUT_DIR): extension allow-lists, realpath checks, wx writes
├── pki-input.ts # pem XOR derBase64 XOR path; PEM sniffing, label discipline, bundles
├── content-input.ts # text XOR base64 XOR path for the bytes to sign / verify / time-stamp; the HEX grammar
├── parse-context.ts # the PARSE fragment (encodingRules, pemMode, strict, limits) and the per-call input budget
├── signer.ts # PKCS#8 / PKCS#12 -> non-extractable SigningKey; the algorithm rules (rsaScheme, hashes)
├── secrets.ts # the two password channels (input XOR PKINATIVE_MCP_PASSWORD), redaction
├── key-views.ts # allow-list views of keys and PKCS#12 bags (never a key byte)
├── trust.ts # the TRUST fragment (anchors, candidates, evidence, at, purposes, policy) and reasons[]
├── time.ts # ISO 8601 instants, PKINATIVE_MCP_AT, clock-dependence
├── random.ts # serialNumber:'random' / nonce:'random' (node:crypto), reported in summaries
├── x509-spec.ts, asn1-spec.ts # the closed JSON specs of create_certificate / create_csr / encode_* (depth-bounded, pollution-safe)
├── names.ts, spki.ts # host / IP identity inputs; public-key kinds and curves
├── pki-errors.ts # PkiError -> ToolError (the 57 PKI_* codes verbatim, classes, remedies), guard()
├── wrapper-codes.ts # the 21 wrapper codes with meaning and remedy (served by explain_code)
├── diagnostics.ts # engine diagnostic sink (de-duplicated, capped at 200) -> diagnostics[]; strict escalation
├── projection.ts # verbosity / fields projection for the read and verdict tools
├── output.ts # artefact emitter: embedded resource (pem / der / hex) or exclusive sandbox file
├── resources.ts # pkinative://output/{+path} over the sandbox (key-bearing files never listed nor served)
├── cache.ts # opt-in SHA-256 response cache (PKINATIVE_MCP_CACHE_DIR)
├── views.ts, wire.ts, base64.ts # wire views (no raw DER echoes), hex helpers, base64 boundary diagnostics
├── engine.ts # engine version and Node floor, the Web Crypto capability probe
├── governance.ts # AI-governance / HITL contract text + draft validation
├── errors.ts, version.ts, index.ts
├── data/pkinative/ # the registries vendored at the pkinative v1.0.0 tag: errors, reasons, diagnostics, limits, core-exports + generated code-tables.ts
└── tools/ # one file per tool (34), _definition.ts (kinds, annotations), _registry.ts + _group-*.ts (catalogue order), _shared.ts
scripts/ # TypeScript run by tsx — the full table is in scripts/README.md
├── gate.ts # THE quality gate (npm run gate / gate:fast; --publish --require-all on release branches)
├── generate-samples.ts # npm run test:generate — the examples through the built server -> test-output/samples/
├── verify-samples.ts # npm run verify:samples — the 98-sample baseline (tests/_fixtures/samples.sha256.json)
├── generate-pki-corpus.ts # npm run corpus:pki — the OpenSSL interoperability corpus (23 + 5)
├── validate-pki.ts # npm run validate:pki — level 0 (engine round trip) + level 1 (OpenSSL 3.x); exit 0 / 1 / 2 / 3
├── tool-shape.ts # structural tools/list fingerprint and size budgets (--write refreshes the fixture)
├── verify-docs.ts # npm run verify:docs — the docs held to docs/assets/ecosystem.json and the source tree
├── release-prepare.ts # mechanical version bump (never commits, tags or publishes)
├── build-claude-rules.ts # npm run agents:rules — .github/instructions/ -> .claude/rules/
├── build-code-tables.mjs # regenerates src/data/pkinative/code-tables.ts from the vendored registries
├── verify-issue.mjs # governance draft checker (npm run verify:issue)
├── lib/ # pki-corpus, interop-tools (OpenSSL), sample-fingerprint, tool-shape, mcp-surface, markdown-anchors, agent-config, prose-language, json-schema-lite
└── helpers/, data/, validators/ # hermetic env, I/O, the lint waivers, the pkilint driver
docs/
├── AGENT_CONTRACT.md # the consumer contract for agents that USE the server (catalogue, decision tree, recipes, error reference)
├── AI_GUIDE.md, KNOWLEDGE_BASE.md, API_STABILITY.md
├── guides/ # AI_GOVERNANCE, LOCAL_TESTING, SECURITY, SIGNING, VERIFICATION, REVOCATION, TIMESTAMPS, KEYS, CONFORMANCE, REPRODUCIBLE
├── data/ # errors, reasons, diagnostics, limits, core-exports (mirrors of src/data/pkinative/), interop.json (the corpus counts)
└── assets/ecosystem.json # the single source of every count and version quoted in the docs
examples/ # 89 executable tools/call sequences (npm run examples:check)
AGENTS.md, CLAUDE.md # repository rules for coding agents; .github/instructions/ holds the per-area rules
.claude/ # shared agent settings, the fail-closed guard hook, generated rules, the release-audit skill
.github/workflows/ # ci (Linux Node 22 / 24, Windows, macOS — the gate with --require-all), interop (4 OS legs, blocking),
# sample-regression, publish (Trusted Publishing + SBOM + attestation), docs, codeql, scorecard,
# dependency-review, audit, node-current
.github/rulesets/ # branch and tag rulesets to import
tests/ # vitest suites (one per tool / module), _fixtures/ (tool shape, sample baseline, the registry schema),
# fixtures/pki/ (42 OpenSSL-made TEST-ONLY files, SHA256SUMS, PROVENANCE.md), interop/ (OpenSSL, linters), tools/ (repository tooling)
🗺 路线图
v1.0.0 是首个版本,基于 pkinative 1.0.0:离线支持引擎的全部运行时接口(通过命名工具调用了 117 / 117 个运行时导出项)。完整计划——已发布的里程碑、上游阻塞项、后续步骤和长期方向——请参阅 ROADMAP.md。
按设计受上游阻塞的功能(引擎没有这些功能,轻量封装也不会伪造):
- 密钥生成 — pkinative 不会生成密钥;密钥来自你的 HSM、KMS 或
openssl genpkey。 - PKCS#8、PKCS#12、CRL、OCSP 响应和时间戳响应的写入器 — 引擎不提供这些功能;请使用 CA 软件或 OpenSSL 生成,并在此处验证。
- DSA 和 ML-DSA 验证 — 可解析并报告,但不会验证或签名(
PKI_REASON_SIGNATURE_NOT_CHECKED/UNSUPPORTED_KEY)。
有功能想法?欢迎提交 issue 或 PR;也可以让 Agent 使用 draft_governance_issue 起草,再由你自行提交。
⭐ 为项目点星
如果 pkinative-mcp 对你有用,请给此仓库点个 ⭐,也欢迎为底层引擎 Nizoka/pkinative 点星。星标有助于更多人发现该项目,也能激励我们持续开发。
🤝 参与贡献
非常欢迎贡献。请阅读 CONTRIBUTING.md,查看未解决的 issues,并遵守行为准则。
📄 许可证
MIT © 2026 Nizoka。第三方材料:THIRD-PARTY-NOTICES.md。
pkinative-mcp 基于 pkinative 和 Model Context Protocol TypeScript SDK 构建。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。