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

如何使用iText在web上下文中用智能卡签署PDF?

慎建本
2023-03-14

通读以下参考资料:

    null

哈希代码:

BouncyCastle.X509Certificate[] chain = Utils.GetSignerCertChain();
reader = Utils.GetReader();
MemoryStream stream = new MemoryStream();
using (var stamper = PdfStamper.CreateSignature(reader, stream, '\0'))
{
    PdfSignatureAppearance sap = stamper.SignatureAppearance;
    sap.SetVisibleSignature(
        new Rectangle(36, 740, 144, 770),
        reader.NumberOfPages,
        "SignatureField"
    );
    sap.Certificate = chain[0];
    sap.SignDate = DateTime.Now;
    sap.Reason = "testing web context signatures";

    PdfSignature pdfSignature = new PdfSignature(
        PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED
    );
    pdfSignature.Date = new PdfDate(sap.SignDate);
    pdfSignature.Reason = sap.Reason;
    sap.CryptoDictionary = pdfSignature;

    Dictionary<PdfName, int> exclusionSizes = new Dictionary<PdfName, int>();
    exclusionSizes.Add(PdfName.CONTENTS, SIG_BUFFER * 2 + 2);
    sap.PreClose(exclusionSizes);

    Stream sapStream = sap.GetRangeStream();
    byte[] hash = DigestAlgorithms.Digest(
        sapStream,
        DigestAlgorithms.SHA256
    );

// is this needed?
    PdfPKCS7 sgn = new PdfPKCS7(
        null, chain, DigestAlgorithms.SHA256, true
    );
    byte[] preSigned = sgn.getAuthenticatedAttributeBytes(
        hash, sap.SignDate, null, null, CryptoStandard.CMS
    );

    var hashedValue = Convert.ToBase64String(preSigned);
}

只是一个简单的测试--在初始页面请求上创建一个伪Pdf文档,计算散列,并将其放入一个隐藏的输入字段Base64编码。(上面的hashedvalue)

然后在客户端使用CAPICOM发布表单并获得用户签名的响应:

PdfSignatureAppearance sap = (PdfSignatureAppearance)TempData[TEMPDATA_SAP];
PdfPKCS7 sgn = (PdfPKCS7)TempData[TEMPDATA_PKCS7];
stream = (MemoryStream)TempData[TEMPDATA_STREAM];
byte[] hash = (byte[])TempData[TEMPDATA_HASH];

byte[] originalText = (Encoding.Unicode.GetBytes(hashValue));
// Oid algorithm verified on client side
ContentInfo content = new ContentInfo(new Oid("RSA"), originalText);

SignedCms cms = new SignedCms(content, true);
cms.Decode(Convert.FromBase64String(signedValue));
// CheckSignature does not throw exception
cms.CheckSignature(true);
var encodedSignature = cms.Encode();

/* tried this too, but no effect on result
sgn.SetExternalDigest(
    Convert.FromBase64String(signedValue),
    null,
    "RSA"
);
byte[] encodedSignature = sgn.GetEncodedPKCS7(
    hash, sap.SignDate, null, null, null, CryptoStandard.CMS
);
*/
byte[] paddedSignature = new byte[SIG_BUFFER];
Array.Copy(encodedSignature, 0, paddedSignature, 0, encodedSignature.Length);
var pdfDictionary = new PdfDictionary();
pdfDictionary.Put(
    PdfName.CONTENTS,
    new PdfString(paddedSignature).SetHexWriting(true)
);
sap.Close(pdfDictionary);

所以现在我不确定我是搞砸了散列部分,签名部分,还是两者都搞砸了。在上面的签名代码片段和客户端代码(未显示)中,我把我认为是签名验证代码的代码称为签名验证代码,但这也可能是错误的,因为这对我来说是第一次。打开PDF文件时,会收到臭名昭著的“文件签名后已被更改或损坏”无效签名消息。

客户端代码(不是我编写的)可以在这里找到。源有变量命名错误,已更正。作为参考,CAPICOM文档说signed response是PKCS#7格式的。

编辑2015-03-12:

在@mkl提供了一些很好的指示和更多的研究之后,CAPICOM在这个场景中似乎实际上是无法使用的。虽然没有明确的文档化,但(还有什么新的?)根据这里和这里,CAPICOM期望一个utf16字符串(encoding.unicodein.NET中)作为输入来创建数字签名。如果长度为奇数,它要么填充或截断接收到的任何数据(取决于前面句子中正确的来源)。即。如果PDFSignatureApparance.GetRangeStream()返回的的长度为奇数,则签名创建将始终失败。也许我应该创建一个“我是幸运的”选项:如果ranged stream length是偶数,则使用sign,如果是奇数,则抛出InvalidOperationException。(可悲的幽默尝试)

下面是测试项目 以供参考。

编辑2015-03-25:

共有1个答案

麹飞航
2023-03-14

在计算范围流摘要之后、将数据转发到web页面之前,服务器端代码包含此块:

PdfPKCS7 sgn = new PdfPKCS7(
    null, chain, DigestAlgorithms.SHA256, true
);
byte[] preSigned = sgn.getAuthenticatedAttributeBytes(
    hash, sap.SignDate, null, null, CryptoStandard.CMS
);

var hashedValue = Convert.ToBase64String(preSigned);

在目前的情况下,这是不必要的。只有当您使用的外部签名API仅仅返回一个签名摘要时才需要它;在这种情况下,pdfpkcs7实例构建CMS/PKCS#7签名容器。另一方面,您使用一个您知道的API

CAPICOM文档说签名响应是PKCS#7格式的。

因此,您不需要(更重要的是)也不能使用pdfpkcs7实例。

服务器端hash变量的内容已经是要签名的数据的哈希摘要值。因此,前端(即在那里使用的sign.js)不得再次对其进行哈希以获得要放入签名中的消息摘要属性值。

但最终执行IE的sign.js签名方法

var signedData = new ActiveXObject("CAPICOM.SignedData");

// Set the data that we want to sign
signedData.Content = src;

所以来自后端的哈希被用作要签名的数据,而不是要签名的数据的哈希,你确实哈希了两次,所以那里有错误的哈希值。

因此,看起来你必须传输整个范围的流,这并不是真正的实用...

确实,一些旧的iTextSharp(4.x版)签名示例使用了Capicom。但是,该代码之所以起作用,是因为它创建了PDF签名类型adbe.pkcs7.SHA1的签名,其中范围流的SHA1散列实际上是嵌入在pkcs#7签名中并由其签名的数据。

这已经不是真正的选择了,因为

  • 它需要使用SHA1,而SHA1在严重情况下是无效的,并且
  • 至少从ISO 32000-1(2008年)起就不鼓励使用它,并且ISO 32000-2(正在开发中)将正式不推荐使用。
 类似资料:
  • 我已经玩弄了一段时间的iTextSharp 5.5.7,找不到正确的方法从智能卡为PDF制作一个有效的数字签名-Adobe Reader总是说它的签名人和未知,并且不能解码签名的DER数据。 我查看了Makesignature.cs代码以供参考,以及is的功能: 然后,根据iExternalSignature.cs中的“Sign”方法 “@param message要进行散列和签名的邮件” 所以我

  • 我们已经编写了一个文档管理系统,并希望使用web客户端对文档进行数字签名。我们的Java客户机应用程序已经能够应用和检查数字签名,但我们希望在web客户机中也能进行签名。这是用GWT编写的,因此在客户端运行时,它是一个JavaScript应用程序。 我们不希望创建一个Java applet并将其下载到客户端并执行它。我们希望使用browser安全设备或browser API来签署文档。我们还希望保

  • 我确实使用IText通过延迟签名(SignDeferred)将签名应用于pdf文档。该过程包含以下步骤: 为siging准备pdf文档 为pdf文档中的签名预留空间 使用自签名证书 整个过程工作,我以一个pdf文档结束,其中签名被设置并有效。 原始pdf是pdf-A1a,但生成的pdf不再是有效的pdf-A1a。我知道有一个关于IText PDF-a支持的文档(https://kb.itextpd

  • 我需要使用外部服务签署pdf文档,该服务将返回PKCS1签名。这意味着我必须在IExternalSignatureContainer实例中添加公钥。我使用iText 7进行整个签名过程。 在iText网站上有一个关于这种方法的很好的例子 我在一个文件中创建了整个演唱过程的示例。必需的引用: null 示例代码(为控制台应用程序准备好的一个文件中的两个类):

  • 按照上一个问题中给出的答案:在Itext 7中,如何让范围流对pdf进行签名?,我尝试重新实现在Itext 5中工作的两步签名方法,但在尝试重新打开第一步的文档结果时遇到问题(使用PdfReader或pdf阅读器)。(无效文档) 以下是已包含名为“认证”的空签名字段的文档的预签名部分...为什么此步骤的结果无效? 这是PreSignatureContainer类: }

  • 我正在尝试将小程序(.cap文件)安装到智能卡中。我读到可以使用APDU完成。我使用Netbeans创建了我的小程序,它的助手是//aid/9AE9BE4D27/53。 首先构建apdu,选择安装程序小程序: ; 然后构建将创建我的小程序的apdu(遵循此结构): 我开发了一个应用程序,可以使用。我想如果我发送正确的安装命令,我的。cap文件应安装在卡中。 有了这些信息,谁能帮我建立正确的apdu