当前位置: 首页 > 知识库问答 >
问题:

在现代浏览器中以本机方式签署PDF?

郜琦
2023-03-14
  • 使用cliets证书存储或智能卡在浏览器中签署PDF
  1. 为了访问本地证书存储,我使用FortifyApp
using (var fileStream = new MemoryStream())
                    {
                        using (var stamper = PdfStamper.CreateSignature(reader, fileStream, '0', null, true))
                        {
                            var signatureAppearance = stamper.SignatureAppearance;
                            signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle(15,15,15,15), 1, "A");
                            IExternalSignatureContainer external =
                                new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                            signatureAppearance.Reason = "AsdAsd";
                            signatureAppearance.Layer2Text = "Asd";

                            signatureAppearance.SignatureRenderingMode =
                                iTextSharp.text.pdf.PdfSignatureAppearance.RenderingMode.DESCRIPTION;

                            MakeSignature.SignExternalContainer(signatureAppearance, external, 512);

                            return fileStream.ToArray();
                        }
                    }
let pdfBuffer = Buffer.from(new Uint8Array(pdf));

            const byteRangeString = `/ByteRange `;
            const byteRangePos = pdfBuffer.indexOf(byteRangeString);

            if (byteRangePos === -1)
                throw new Error('asd');

            let len = pdfBuffer.slice(byteRangePos).indexOf(`]`) + 1;

            // Calculate the actual ByteRange that needs to replace the placeholder.
            const byteRangeEnd = byteRangePos + len;
            const contentsTagPos = pdfBuffer.indexOf('/Contents ', byteRangeEnd);
            const placeholderPos = pdfBuffer.indexOf('<', contentsTagPos);
            const placeholderEnd = pdfBuffer.indexOf('>', placeholderPos);
            const placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos;
            const placeholderLength = placeholderLengthWithBrackets - 2;
            const byteRange = [0, 0, 0, 0];
            byteRange[1] = placeholderPos;
            byteRange[2] = byteRange[1] + placeholderLengthWithBrackets;
            byteRange[3] = pdfBuffer.length - byteRange[2];
            let actualByteRange = `/ByteRange [${byteRange.join(' ')}]`;
            actualByteRange += ' '.repeat(len - actualByteRange.length);

            // Replace the /ByteRange placeholder with the actual ByteRange
            pdfBuffer = Buffer.concat([pdfBuffer.slice(0, byteRangePos) as any, Buffer.from(actualByteRange), pdfBuffer.slice(byteRangeEnd)]);

            // Remove the placeholder signature
            pdfBuffer = Buffer.concat([pdfBuffer.slice(0, byteRange[1]) as any, pdfBuffer.slice(byteRange[2], byteRange[2] + byteRange[3])]);

//stringSignature comes from the signature creations below, and is 'hex' encoded
// Pad the signature with zeroes so the it is the same length as the placeholder
            stringSignature += Buffer
                .from(String.fromCharCode(0).repeat((placeholderLength / 2) - len))
                .toString('hex');

            // Place it in the document.
            pdfBuffer = Buffer.concat([
                pdfBuffer.slice(0, byteRange[1]) as any,
                Buffer.from(`<${stringSignature}>`),
                pdfBuffer.slice(byteRange[1])
            ]);

  • 这使用了forge和上传的p12文件如果我能翻译导入的(?)来自Fortify的privateKey(它是==typeof CryptoKey,forge抛出一个错误:TypeError:signer.key.sign不是函数
p7.addCertificate(certificate); //certificate is the Certificate from Fortify CertificateStore.getItem(certId)
p7.addSigner({
                key: privateKey, //this is the CryptoKey from Fortify
                certificate: null/*certificate*/, //also tried certificate from Fortify 
                digestAlgorithm: forge.pki.oids.sha256,
                authenticatedAttributes: [
                    {
                        type: forge.pki.oids.contentType,
                        value: forge.pki.oids.data,
                    }, {
                        type: forge.pki.oids.messageDigest,
                        // value will be auto-populated at signing time
                    }, {
                        type: forge.pki.oids.signingTime,
                        // value can also be auto-populated at signing time
                        // We may also support passing this as an option to sign().
                        // Would be useful to match the creation time of the document for example.
                        value: new Date(),
                    },
                ],
            });

            // Sign in detached mode.
            p7.sign({detached: true});
  • 我还尝试使用pkijs创建签名(引发类似错误:Signing error:TypeError:未能在'sublecrypto'上执行'sign':参数2不是'CryptoKey'类型。
let cmsSigned = new pki.SignedData({
                encapContentInfo: new pki.EncapsulatedContentInfo({
                    eContentType: "1.2.840.113549.1.7.1", // "data" content type
                    eContent: new asn.OctetString({ valueHex: pdfBuffer })
                }),
                signerInfos: [
                    new pki.SignerInfo({
                        sid: new pki.IssuerAndSerialNumber({
                            issuer: certificate.issuer,
                            serialNumber: certificate.serialNumber
                        })
                    })
                ],
                certificates: [certificate]
            });

            let signature = await cmsSigned.sign(privateKey, 0, 'SHA-256');
  • 如果我使用以下代码创建签名,“有效”是什么:
let signature = await provider.subtle.sign(alg, privateKey, new Uint8Array(pdfBuffer).buffer);

“有效”,因为它创建了无效的签名:

签名验证期间出错<阿斯恩。1解析错误:
BER解码时遇到错误:

我尝试了多个证书,没有运气。

  1. 我可以实现我的目标,而不必手动上传p12/pfx文件,甚至可能吗?
  2. 延迟签名的服务器端实现是否正确,我还需要其他东西吗?
  3. javascript中的pdf操作正确吗?
  4. 我可以将本机CrytpoKey转换为伪造或pkijs吗?
  5. 最后一个签名有什么问题?乍一看,这似乎是正确的(至少是html" target="_blank">格式):

谢谢: F

共有1个答案

于高雅
2023-03-14

所以我想出来了。

我能在不需要手动上传p12/pfx文件的情况下实现我的目标吗?

是的。(有关需要更改的内容,请参见下文。)

延迟签名的服务器端实现是否正确,是否需要其他内容?

是的,上面的代码很好。

javascript中的pdf操作正确吗?

也很好。

我可以将本机CrytpoKey转换为伪造或pkijs吗?

是的,见下文。

最后一个签名有什么问题?

@mkl在评论中回答了一下,谢谢。

FortifyApp现在有一个CMS演示。虽然它不适用于我使用的版本,但它适用于版本1.3.4。

所以我选择了pki。js实现。签名成功所需的代码更改如下:

  1. 导出证书:
const cryptoCert = await provider.certStorage.getItem(selectedCertificateId);
const certRawData = await provider.certStorage.exportCert('raw', cryptoCert);

const pkiCert = new pki.Certificate({
    schema: asn.fromBER(certRawData).result,
});

return pkiCert;
let cmsSigned = new pki.SignedData({
    version: 1,
    encapContentInfo: new pki.EncapsulatedContentInfo({
        eContentType: '1.2.840.113549.1.7.1',
    }),
    signerInfos: [
        new pki.SignerInfo({
            version: 1,
            sid: new pki.IssuerAndSerialNumber({
                issuer: certificate.issuer,
                serialNumber: certificate.serialNumber
            })
        })
    ],
    certificates: [certificate]
});

let signature = await cmsSigned.sign(privateKey, 0, 'SHA-256', pdfBuffer);

const cms = new pki.ContentInfo({
    contentType: '1.2.840.113549.1.7.2',
    content: cmsSigned.toSchema(true),
});
const result = cms.toSchema().toBER(false);

return result;
let stringSignature = Array.prototype.map.call(new Uint8Array(signature), x => (`00${x.toString(16)}`).slice(-2)).join('');
let len = signature.byteLength;
 类似资料:
  • 问题内容: 我正在尝试在我的网站上部署Java applet。我还需要签名,因为我需要访问剪贴板。我遵循了所有可以找到但没有成功的签名教程。到目前为止,这是我所做的: 在NetBeans中编写了一个applet。它在applet查看器中运行良好。 从中制作一个.jar文件。 通过执行以下操作创建证书: 像这样用jarsigner签名: 制作了一个包含以下内容的html文件: 当我打开该html文件

  • 现在相信很多团队的代码都是直接用 ES2015+ 语法来编写和维护,然后通过 Babel 将 ES2015+ 语法转成支持老浏览器的 js 代码,经过转换后的 js 代码从体积和解析执行效率上都比转换前有损耗。 兼容性 从 Caniuse 网站的数据来看,现在绝大多数的浏览器已经对 ES2015+有了很好的支持,而经过我们统计百度 APP 的 Webview 浏览器数据来看,国内大概有 74.71

  • 问题内容: 是否仍然可以通过浏览器中的Java小程序在Windows下启动“本机”应用程序?IE在网页上“单击此处以启动notepad.exe”。我为此找到的最新参考文献是2002年。我想知道是否不再支持该模型/概念。 问题答案: 是的,但是必须签署小程序。 签名的小程序将提示用户授予他们权限。一旦指定了applet,它就具有与计算机上运行的任何应用程序相同的权限,包括启动本机应用程序(或链接本机

  • 本文向大家介绍js判断浏览器版本以及浏览器内核的方法,包括了js判断浏览器版本以及浏览器内核的方法的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了js判断浏览器版本以及浏览器内核的方法。分享给大家供大家参考。具体实现方法如下: js判断是否移动端及浏览器内核 代码二 希望本文所述对大家的javascript程序设计有所帮助。

  • 我试图在用户拍照时检测相机方向,以便在画布上绘制时进行调整。问题是我不能使用设备方向,因为即使方向锁定打开,我也需要它才能工作。 摄像机设置 视频流快照 来自David Walsh的参考代码 - 浏览器摄像头

  • 认识到React本机应用程序设计为使用模拟器开发/测试,是否可以使用web浏览器也测试应用程序? 服务,例如https://rnplay.org/ 存在,但我担心的是它是由https://appetize.io/ 它可能受到每月分钟数的限制。与付费的屏幕流媒体服务相比,我还想利用免费/开源技术来实现这一点。 按照这些思路,为了在浏览器中测试应用程序,应用程序是否需要使用一个或多个库,这些库允许应用