我正在开发制作PAdES签名的网络应用程序。我已经成功地实现了PAdES基线-B。然而,当我试图通过添加这里描述的所有必要信息来创建PAdES基线-LT时,包括OCSP响应、CRL和证书,文件似乎被损坏了,Adobe显示了以下错误:签名验证期间的错误: PKCS7解析错误
如果你想看一下,这里是签名的PDF:https://easyupload.io/fxkzvs
我在对PDF签名后附加DSS,因此我附加到get LT subtype的那些附加对象不会影响签名本身,所以我不确定为什么会出现PKCS7错误,如果我创建的同一个签名(创建Baseline-B时)看起来不错的话。
以下是创建和插入这些附加数据的代码部分:
public appendVri(pdfRaw, pdfToSign, vri: VRI) {
if (pdfRaw instanceof ArrayBuffer) {
pdfRaw = new Uint8Array(pdfRaw);
}
const pdf = this.loadPdf(pdfRaw);
const root = this.findRootEntry(pdf.xref);
const rootSuccessor = this.findSuccessorEntry(pdf.xref.entries, root);
const certsEntry = [];
const xObjects = [];
let offsetDss;
let offsetVri;
// let offsetCerts[];
// let offsetOcsp[];
// let offsetCrls[];
const dummy = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(dummy);
const dssEntry = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(dssEntry);
const vriEntry = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(vriEntry);
for (let i = 0; i < vri.certs.length; i++) {
certsEntry[i] = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(certsEntry[i]);
}
const ocspEntry = [];
for (let i = 0; i < vri.OCSPs.length; i++) {
ocspEntry[i] = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(ocspEntry[i]);
}
const crlsEntry = [];
for (let i = 0; i < vri.CRLs.length; i++) {
crlsEntry[i] = this.findFreeXrefNr(pdf.xref.entries, xObjects);
xObjects.push(crlsEntry[i]);
}
let certsReference = '/Certs [';
for (let i = 0; i < vri.certs.length; i++) {
certsReference = certsReference + certsEntry[i] + ' 0 R';
if (i === vri.certs.length - 1) {
certsReference = certsReference + '] \n';
} else {
certsReference = certsReference + ' ';
}
}
let ocspReference = '/OCSPs [';
for (let i = 0; i < vri.OCSPs.length; i++) {
ocspReference = ocspReference + ocspEntry[i] + ' 0 R';
if (i === vri.OCSPs.length - 1) {
ocspReference = ocspReference + '] \n';
} else {
ocspReference = ocspReference + ' ';
}
}
let crlsReference = '/CRLs [';
for (let i = 0; i < vri.CRLs.length; i++) {
crlsReference = crlsReference + crlsEntry[i] + ' 0 R';
if (i === vri.CRLs.length - 1) {
crlsReference = crlsReference + '] \n';
} else {
crlsReference = crlsReference + ' ';
}
}
const offsets = [];
const appendDss = '\n' + pdfToSign.dssEntry + ' 0 obj\n' +
'<< \n' +
'/VRI ' + vriEntry + ' 0 R \n' +
certsReference +
ocspReference +
crlsReference +
'>>';
offsetDss = (pdf.stream.bytes.length);
offsets.push(offsetDss);
let array = this.insertIntoArray(pdf.stream.bytes, offsetDss, appendDss);
const sigHash = this.strHex(this.digest(forge.util.decode64(pdfToSign.sig), 'SHA1')).toUpperCase();
const appendVri = '\n' + vriEntry + ' 0 obj\n' +
'<< \n' +
'/' + sigHash + ' << \n' +
certsReference +
ocspReference +
crlsReference +
'>> \n' +
'>>\n';
offsetVri = offsetDss + appendDss.length;
offsets.push(offsetVri);
array = this.insertIntoArray(array, offsetVri, appendVri);
let lastOffset = offsetVri + appendVri.length;
const appendCerts = [];
const appendOcsp = [];
const appendCrls = [];
let offsetDelta = 0;
appendCerts[-1] = '';
for (let i = 0; i < vri.certs.length; i++) {
appendCerts[i] = certsEntry[i] + ' 0 obj\n' +
'<< \n' +
'/Length ' + vri.certs[i].length + ' \n' +
'>>\n' +
'stream\n' +
atob(vri.certs[i]) + '\n' +
'endstream\n' +
'endobj\n';
const currentOffset = lastOffset + appendCerts[i - 1].length;
offsets.push(currentOffset);
array = this.insertIntoArray(array, currentOffset, appendCerts[i]);
lastOffset = currentOffset;
offsetDelta += currentOffset;
}
lastOffset = lastOffset + appendCerts[appendCerts.length - 1].length;
appendOcsp[-1] = '';
for (let i = 0; i < vri.OCSPs.length; i++) {
appendOcsp[i] = ocspEntry[i] + ' 0 obj\n' +
'<< \n' +
'/Length ' + vri.OCSPs[i].length + ' \n' +
'>>\n' +
'stream\n' +
atob(vri.OCSPs[i]) + '\n' +
'endstream\n' +
'endobj\n';
const currentOffset = lastOffset + appendOcsp[i - 1].length;
offsets.push(currentOffset);
array = this.insertIntoArray(array, currentOffset, appendOcsp[i]);
lastOffset = currentOffset;
offsetDelta += currentOffset;
}
lastOffset = lastOffset + appendOcsp[appendOcsp.length - 1].length;
appendCrls[-1] = '';
for (let i = 0; i < vri.CRLs.length; i++) {
appendCrls[i] = crlsEntry[i] + ' 0 obj\n' +
'<< \n' +
'/Length ' + vri.CRLs[i].length + ' \n' +
'>>\n' +
'stream\n' +
atob(vri.CRLs[i]) + '\n' +
'endstream\n' +
'endobj\n';
const currentOffset = lastOffset + appendCrls[i - 1].length;
offsets.push(currentOffset);
array = this.insertIntoArray(array, currentOffset, appendCrls[i]);
lastOffset = currentOffset;
offsetDelta += currentOffset;
}
offsetDelta += appendDss.length + appendVri.length;
let middle = '';
offsets.forEach(offset => {
offset = offset + '';
middle += offset.padStart(10, '0') + ' ' + '00000' + ' ' + ' n' + '\n';
});
let xref = '\nxref\n' +
pdfToSign.dssEntry + ' ' + (2 + vri.certs.length + vri.CRLs.length + vri.OCSPs.length) + '\n' +
middle;
const sha256Hex = sha256(array, false);
xref += this.createTrailer(pdf.xref.topDict, array.length, sha256Hex, pdf.xref.entries.length);
array = this.insertIntoArray(array, array.length, xref);
return array;
}
编辑:我按照@mkl的建议修复了一切。Adobe不再抛出这个错误,但是我的签名仍然被视为基线-T,而不是基线-LTA,可以在这里检查:https://ec.europa.eu/cefdigital/DSS/webapp-demo/validation
以下是新版本的签名pdf:https://easyupload.io/i5bs9k
PDF中有多个问题,包括最初签署的PDF和您添加的内容。
您暗示在使用appendVri
扩展您的签名PDF之前,可以很好地验证。我不能重现这个。
我通过截短到67127字节,从你的共享PDF中提取了最初签名的PDF,而对于该文件,我在签名验证期间已经得到了错误。PKCS7分析错误:版本不正确。因此,在扩展之前,这个问题已经在您的PDF中。
实际问题在错误消息中也变得很清楚:版本不正确。让我们看看嵌入式CMS容器的ASN.1转储的开始:
0 15733: SEQUENCE {
4 9: . OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2)
: . . (PKCS #7)
15 15718: . [0] {
19 15714: . . SEQUENCE {
23 1: . . . INTEGER 5
...
这里的INTEGER 5
就是CMSVersion
,这就是问题所在。
创建子过滤器值为ETSI的签名。凯德斯。分离,即(非遗留)PAdES签名。
根据当前的PAdES标准(ETSI EN 319 142-1 v1.1.1),这意味着嵌入式签名容器是CAdES(ETSI EN 319 142-1第4.1节)中规定的DER编码的SignedData对象。CAdES要求将CMS版本设置为1或3(ETSI EN 319 122-1 V1.1.1第4.4节)。
因此,签名中的INTEGER 5
是不正确的,值必须是1
或3
(取决于签名的其他细节)。
当我在您的原始PDF中修补签名容器以声明3
而不是5
的版本时,Adobe Acrobat立即对PKCS7解析感到满意。
有趣的是,根据普通CMS标准(RFC 5652),值5
是正确的,因为该签名容器中的crls
集合有一个带有其他类型的条目,即OCSP响应。仅仅在CAdES(以及相应的PAdES)的上下文中,该值必须减小。
在PAdES的上下文中,这实际上是可以理解的,这里毕竟没有使用crls
集合。另一方面,普通的CAdES需要在那里添加OCSP响应,所以我不确定将版本限制为1
和3
的理由。可能有人只是不想在crls
中的CRL和/或OCSP响应更新/组织/...
appendVri
引入了以下额外错误:
>
您的交叉引用表项不正确,它们看起来像这样:
0000067127 00000 n\n
但他们需要这样:
0000067127 00000 n \n
也就是说,00000
代数和n
文本之间必须只有一个空格。由于条目长度必须为20字节,并且使用单字节eol标记,因此额外的空格必须放在n
文本后面。
在预告片中,您只需复制原始尺寸条目:
/Size 18
但是您添加了对象编号最多为28的对象,因此大小条目必须是
/Size 29
在预告片中,您不会链接到上一个原始交叉引用表。但对于增量更新,您必须这样做。因此,您需要添加一个
/Prev 66604
去你的拖车。
有了这些变化,Adobe Reader不再抱怨结构错误。
当准备签署的PDF时,您似乎在其目录中添加了一个DSS条目,该条目指向准备好的PDF中尚未定义的对象:
1 0 obj
<</AcroForm<</Fields[11 0 R] /SigFlags 3>>
/DSS 19 0 R
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
...
trailer <<
/Size 18
/Root 1 0 R
/Info 10 0 R
/ID [<1f7703d1f61b41d20c76b866132baa8b><6a44acaeb3052d4c807f6782f2eed88c>]
>>
然后,在您的方法appendVri
中,您创建了一个对象19,其中的DSS将由该引用引用,该引用最初不指向任何地方。
虽然这可能不是无效的,但这有点可疑。特别是在《不安全阴影攻击》出版后,将这种悬而未决的引用作为签署准备的一部分可能被认为是可疑的。
此外,如果其他一些PDF处理器最终正在处理您的签名(但不是扩展)PDF,它可能会使用对象19来处理其他内容,结果是生成一个带有无效数字安全存储的PDF。
在你的评论中
我的签名仍然被验证为Baseline-T,而不是LT,尽管我修复了您发现的所有问题
实际上,在前面的部分中,我只检查了CMS容器和PDF的结构完整性,没有检查您的精确验证相关信息。
因此,在您更新、更正的PDF示例中,我查看了您添加到文档安全存储中的吊销数据,这里确实出现了一个问题:当OCSP响应时,您只嵌入了基本OCSPResponse
对象,而不是完整的OCSPResponse
对象,这些对象包装了基本OCSP响应对象。
然而,PAdES规范要求您嵌入完整的OCSP响应对象
OCSPs数组(可选)对流的间接引用数组,每个包含DER编码的在线证书状态协议(OCSP)响应(应符合IETF RFC 6960[5]中的定义)。
(ETSI EN 319 142-1 V1.1.1第5.4.2.2节)
因此,请使用完整的OCSP响应,而不仅仅是内部的基本OCSP响应。如果您无法再访问它们,可以根据基本响应重新构建它们,方法是根据规范包装它们:
OCSPResponse ::= SEQUENCE {
responseStatus OCSPResponseStatus,
responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }
OCSPResponseStatus ::= ENUMERATED {
successful (0), -- Response has valid confirmations
malformedRequest (1), -- Illegal confirmation request
internalError (2), -- Internal error in issuer
tryLater (3), -- Try again later
-- (4) is not used
sigRequired (5), -- Must sign the request
unauthorized (6) -- Request unauthorized
}
ResponseBytes ::= SEQUENCE {
responseType OBJECT IDENTIFIER,
response OCTET STRING }
对于基本的OCSP应答器,响应类型将是id-pkix-ocsp-Basic。
id-pkix-ocsp OBJECT IDENTIFIER ::= { id-ad-ocsp }
id-pkix-ocsp-basic OBJECT IDENTIFIER ::= { id-pkix-ocsp 1 }
(RFC 6960第4.2.1节)
只需假设成功
状态。
进程 c:在函数“主要”中: prog.c:35:20:警告:格式“%d”需要类型为“int”的参数,但参数3的类型为“void * ”[-w format =]printf(" \ n % c \ t % d \ t identifier \ n ",c,p);^ prog. c: 47:24:警告:格式'%d'需要类型'int'的参数,但参数3的类型'void*'[-Wformat=]prin
错误 *--unhandled-rejections=strict`(请参见https://nodejs.org/api/CLI.html#CLI_unhandled_rejections_mode)。(拒绝ID:1)(节点:1616)[DEP0018]拒绝警告:不推荐未处理得承诺拒绝.将来,未处理的承诺拒绝将以非零退出代码终止node.js进程。 app.js文件 database.js文件
问题内容: 测试代码的图片 / 工作签名的图片您好,我正在将behat与与貂皮集成的selenium驱动程序一起使用,我正在尝试编写一个输入假签名的测试。我们使用鼠标在屏幕上绘制签名,因此我希望selenium能够为我做到这一点。我尝试获取该字段的ID并使用dragTo(’我页面上的另一个元素’),但它仅在签名框中单击,而没有执行任何其他操作。 我正在使用的框架是laravel for php。
您将自动执行著名的歌曲“墙上的99瓶XXX”。你将打印这首歌所有99个诗句的歌词。用循环!如果你不知道歌词,用谷歌查一下。 该方案应: a.如果他们不到21岁,或者他们喜欢苏打水,那么歌词是“墙上有99瓶苏打水” B.如果他们超过21岁,那么是“99瓶啤酒” 您必须使用WHILE循环,并且counter变量必须是print语句的一部分! 所以第一节是: 99瓶苏打水挂在墙上 墙上有98瓶苏打水 最
我正在用PyCharm做一个项目。项目已打开并配置了解释器,可以成功运行。远程解释器路径已正确映射。这似乎是正确的配置,但PyCharm用“未解决的引用”错误突出显示了我的有效代码,即使对于内置Python函数也是如此。为什么即使代码在运行,它们似乎也没有被检测到?有没有办法让PyCharm正确识别这些? 这个问题的具体实例是远程解释器,但是这个问题也出现在本地解释器上。
我尝试在JSP页面上显示一些数据库记录。在我的项目中,我必须使用index.html和studentdeatils.jsp这样的页面。我将requestDispather设置为从索引页转到studentDetails页。 当我试图显示JSP时,我的浏览器页面中出现了以下错误。 请分享你的想法。