You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用pkijs验证OpenSSL生成的CMS签名时遇缺少分离数据错误

问题:pkijs验证OpenSSL生成的CMS签名报错:Missed detached data input array

我尝试验证通过以下OpenSSL命令生成的CMS签名:

$ openssl cms -sign -signer domain.pem -inkey domain.key -binary -in README.md -outform der -out signature

以下是我使用pkijs编写的验证代码:

import * as pkijs from "../src/shared/vendor/pkijs/index.es.js";
import * as pvtsutils from "../src/shared/vendor/pvtsutils/index.es.js";

function decodePEM(pem: string, tag = "[A-Z0-9 ]+"): ArrayBuffer[] {
  const pattern = new RegExp(
    `-{5}BEGIN ${tag}-{5}([a-zA-Z0-9=+\\/\\n\\r]+)-{5}END ${tag}-{5}`,
    "g",
  );

  const res: ArrayBuffer[] = [];
  let matches: RegExpExecArray | null = null;
  // eslint-disable-next-line no-cond-assign
  while ((matches = pattern.exec(pem))) {
    const base64 = matches[1]
      .replace(/\r/g, "")
      .replace(/\n/g, "");
    res.push(pvtsutils.Convert.FromBase64(base64));
  }

  return res;
}

const buffer = pvtsutils.BufferSourceConverter.toArrayBuffer(await Deno.readFile("./domain.pem"));
const pem = pvtsutils.Convert.ToBinary(buffer);
const certificate = pkijs.Certificate.fromBER(decodePEM(pem, "CERTIFICATE")[0]) as pkijs.Certificate;

const cms = pkijs.ContentInfo.fromBER(await Deno.readFile("./signature"));
if (cms.contentType !== pkijs.ContentInfo.SIGNED_DATA) {
  throw new Error("CMS is not Signed Data");
}

const signedData = new pkijs.SignedData({ schema: cms.content });

// Verify Signed Data signature
const ok = await signedData.verify({
  signer: 0,
  checkChain: true,
  trustedCerts: [certificate],
});

console.log(ok);

证书和SignedData均已正确读取并解析,但调用signedData.verify时出现以下错误:

error: Uncaught (in promise) SignedDataVerifyError: Missed detached data input array
                    throw new SignedDataVerifyError({

请问我哪里操作出错了?


原因与解决方法

你用OpenSSL生成的是分离式(detached)CMS签名——该命令默认不会将原始数据(README.md)嵌入到签名文件中,因此pkijs验证时无法自动获取原始数据,必须手动传入。

只需修改验证代码,在verify方法的参数中添加detached字段,传入原始文件的ArrayBuffer即可:

修改后的关键代码片段

// 新增:读取原始待验证数据
const originalData = pvtsutils.BufferSourceConverter.toArrayBuffer(await Deno.readFile("./README.md"));

// 验证签名时传入detached数据
const ok = await signedData.verify({
  signer: 0,
  checkChain: true,
  trustedCerts: [certificate],
  detached: [originalData] // 添加这一行
});

完整修改后的代码

import * as pkijs from "../src/shared/vendor/pkijs/index.es.js";
import * as pvtsutils from "../src/shared/vendor/pvtsutils/index.es.js";

function decodePEM(pem: string, tag = "[A-Z0-9 ]+"): ArrayBuffer[] {
  const pattern = new RegExp(
    `-{5}BEGIN ${tag}-{5}([a-zA-Z0-9=+\\/\\n\\r]+)-{5}END ${tag}-{5}`,
    "g",
  );

  const res: ArrayBuffer[] = [];
  let matches: RegExpExecArray | null = null;
  // eslint-disable-next-line no-cond-assign
  while ((matches = pattern.exec(pem))) {
    const base64 = matches[1]
      .replace(/\r/g, "")
      .replace(/\n/g, "");
    res.push(pvtsutils.Convert.FromBase64(base64));
  }

  return res;
}

const buffer = pvtsutils.BufferSourceConverter.toArrayBuffer(await Deno.readFile("./domain.pem"));
const pem = pvtsutils.Convert.ToBinary(buffer);
const certificate = pkijs.Certificate.fromBER(decodePEM(pem, "CERTIFICATE")[0]) as pkijs.Certificate;

const cms = pkijs.ContentInfo.fromBER(await Deno.readFile("./signature"));
if (cms.contentType !== pkijs.ContentInfo.SIGNED_DATA) {
  throw new Error("CMS is not Signed Data");
}

const signedData = new pkijs.SignedData({ schema: cms.content });

// 读取原始待验证数据
const originalData = pvtsutils.BufferSourceConverter.toArrayBuffer(await Deno.readFile("./README.md"));

// 验证签名并传入分离式数据
const ok = await signedData.verify({
  signer: 0,
  checkChain: true,
  trustedCerts: [certificate],
  detached: [originalData]
});

console.log(ok);

内容的提问来源于stack exchange,提问作者lviggiani

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 10:01:37