通读以下参考资料:
哈希代码:
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.unicode
in.NET中)作为输入来创建数字签名。如果长度为奇数,它要么填充或截断接收到的任何数据(取决于前面句子中正确的来源)。即。如果PDFSignatureApparance.GetRangeStream()返回的流
的长度为奇数,则签名创建将始终失败。也许我应该创建一个“我是幸运的”选项:如果ranged stream length是偶数,则使用sign,如果是奇数,则抛出InvalidOperationException
。(可悲的幽默尝试)
下面是测试项目
以供参考。
编辑2015-03-25:
在计算范围流摘要之后、将数据转发到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签名中并由其签名的数据。
这已经不是真正的选择了,因为
我已经玩弄了一段时间的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