65.9K
CodeProject 正在变化。 阅读更多。
Home

数字签名和 PDF 文档

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2007年4月30日

3分钟阅读

viewsIcon

93389

PDFKit.NET 2.0 是一个 100% .NET (可验证) 组件,用于创建和操作 PDF 文档。在本文中,我将重点介绍其数字签名功能。数字签名可用于验证 PDF 文档的来源,并提供 PDF 文档的完整性。

PDFKit.NET 2.0 是一个 100% .NET (可验证) 组件,用于创建和操作 PDF 文档。在本文中,我将重点介绍其数字签名功能。数字签名可用于验证 PDF 文档的来源(谁签署了它?)以及提供 PDF 文档的完整性(文档在签名后是否被更改过?)。在本文中,我将展示如何应用一个或多个数字签名以及如何验证数字签名。

目录

签名

考虑以下表单

Screenshot - screenshot_form_editmode.png
图 1:带有字段和两个空签名字段的 PDF 表单

此表单有两个部分表单字段;一个用于学生,一个用于教师。通常,学生会先填写他/她的部分并签署文档。这可以通过编程方式实现,如下所示:

using (FileStream sourceFile = new FileStream(@"form.pdf", FileMode.Open, 
   FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   // file out the data fields
   TextField projectNr = document.Fields["projectNumber"] as TextField;
   projectNr.Value = "FF-235";
   
   TextField sName = document.Fields["studentName"] as TextField;
   sName.Value = "Bob Stapleton";
   
   TextField sDate = document.Fields["studentDate"] as TextField;
   sDate.Value = "April 18, 2007";

   // retrieve the signature field
   SignatureField sigField = document.Fields["studentSignature"] as 
      SignatureField;
   
   // open certicate store
   Pkcs12Store store = null;
   using (FileStream storeFile = new FileStream(@"ChrisSharp.pfx", 
      FileMode.Open, FileAccess.Read))
   {
      store = new Pkcs12Store(storeFile, "Sample");
   }
   
   // let the factory decide which type should be used
   SignatureHandler handler = StandardSignatureHandler.Create(store);

   // associate the handler with the signature field
   sigField.SignatureHandler = handler;

   // set optional info
   sigField.ContactInfo = "+31 (0)77 4748677";
   sigField.Location = "The Netherlands";
   sigField.Reason = "I hereby declare!";

   // save the signed document - while saving, the handler 
   // will be called to sign the saved content
   using (FileStream outFile = new FileStream(@"signedByStudent.pdf", 
      FileMode.Create, FileAccess.ReadWrite))
   {
      document.Write(outFile);
   }
}

执行上述代码并在 PDF 阅读器中打开 PDF 文档后,文档的外观如下:

Screenshot - signedByStudent.png
图 2:应用第一个签名后

请注意 PDF 阅读器显示的问号。这意味着证书尚未被客户端计算机信任。这只需将证书添加到信任存储即可。

接下来,教师将审查学生的数据,然后填写最终字段并签名。这可以通过编程方式实现,如下所示:

using (FileStream sourceFile = new FileStream(@"..\..\signedByStudent.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   // file out the data fields
   TextField tName = document.Fields["teacherName"] as TextField;
   tName.Value = "Max Boulton";

   TextField tDate = document.Fields["teacherDate"] as TextField;
   tDate.Value = "April 18, 2007";

   // retrieve the signature field
   SignatureField sigField = document.Fields["teacherSignature"] as 
      SignatureField;

   // open certicate store.
   Pkcs12Store store = null;
   using (FileStream storeFile = new FileStream(@"..\..\MaxBoulton.pfx", 
      FileMode.Open, FileAccess.Read))
   {
      store = new Pkcs12Store(storeFile, "teacherpassword");
   }

   // let the factory decide which type should be used.
   SignatureHandler handler = StandardSignatureHandler.Create(store);

   // associate the handler with the signature field
   sigField.SignatureHandler = handler;

   // set optional info.
   sigField.ContactInfo = "+31 (0)77 4748677";
   sigField.Location = "The Netherlands";
   sigField.Reason = "I hereby declare!";

   // save the signed document - while saving, the handler 
   // will be called to sign the saved content
   using (FileStream outFile = new FileStream(@"..\..\signedByTeacher.pdf", 
      FileMode.Create, FileAccess.ReadWrite))
   {
      document.Write(outFile, DocumentWriteMode.AppendUpdate);
   }
}

如果您查看代码,它与前面的代码示例几乎相同。唯一的显著区别是我向 Document.Write 方法传递了一个额外的参数:DocumentWriteMode.AppendUpdate。这告诉PDFKit.NET 2.0将所有更改保存为所谓的“更新”。我将在下一节讨论这一点。

执行上述代码并在 PDF 阅读器中打开 PDF 文档后,文档的外观如下:

Screenshot - signedByTeacher.png
图 3:应用第二个签名后

请注意,第一个签名的图标已更改为警告标志。这表明“文档自签名以来已被更新”。确实如此。

更新

请注意,当我们保存第二个签名时,我们向 Document.Write 传递了一个额外的参数,即 DocumentWriteMode.AppendUpdate。这指示PDFKit.NET将新字段数据和签名保存为“更新”。这意味着原始 PDF 数据完全保持不变,而更改被连接起来。下图说明了这一点。

Screenshot - saveAsUpdate.png
图 4:PDF 更新

因此,第一个签名仍然有效,因为签名时的数据并未更改;我们只是添加了一个更新。

所以,在保存更新后,实际上有两个版本的文档;一个由学生签名,一个由教师签名。检索已应用特定签名的确切文档非常有用。显然,签名者只对该版本负责,而不对之后创建的版本负责。

给定一个文档,您可以枚举所有更新或版本的文档,并像这样将其保存到磁盘:

using (FileStream sourceFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the PDF document
   Document document = new Document(sourceFile);

   // count the number of updates
   Console.WriteLine("This document has {0} updates.",document.Updates.Count );
   
   // save each update as a new PDF document
   foreach (Update update in document.Updates)
   {
      string name = string.Format( @"..\..\signedByTeacher_{0}.pdf", 
         update.Index );
      using (FileStream updateFile = new FileStream(name, FileMode.Create, 
         FileAccess.Write))
      {
         update.Write(updateFile);
      }
   }
}

但也许更有趣的是,您可以打开一个已签名的文档,并按签名字段检索已签名的更新。以下代码示例枚举所有签名字段并保存已签名的更新。

using (FileStream sourceFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open the form
   Document document = new Document(sourceFile);

   foreach (Field field in document.Fields)
   {
      // is this a signature field?
      SignatureField sigField = field as SignatureField;
      if (null != sigField)
      {
         // has it been signed?
         if (sigField.IsSigned)
         {
            // save the update and name it after the field
            string name = string.Format(@"..\..\{0}.pdf", sigField.FullName);
            using (FileStream updateFile = new FileStream(name, 
               FileMode.Create, FileAccess.Write))
            {
               sigField.SignedUpdate.Write(updateFile);
            }
         }
      }
   }
}

执行此代码后,将保存两个新的 PDF 文档:studentSignature.pdfteacherSignature.pdf。每个文档都显示了由相应字段签名的版本。

验证

到目前为止,我们讨论了文档签名。验证留给了 PDF 阅读器应用程序。但是PDFKit.NET也允许您以编程方式验证签名。这非常简单,如下一个代码示例所示。该示例打开了由学生和教师签名的 PDF 文档,并枚举签名字段。对于每个签名,有关签名状态的信息将被写入控制台。

using (FileStream inFile = new FileStream(@"..\..\signedByTeacher.pdf", 
   FileMode.Open, FileAccess.Read))
{
   // open form
   Document document = new Document(inFile);

   foreach (Field field in document.Fields)
   {
      // is this a signature field?
      SignatureField sigField = field as SignatureField;
      if (null != sigField)
      {
         Console.WriteLine("Field '{0}'", sigField.FullName);

         // has it been signed?
         if (sigField.IsSigned)
         {
            // verify, based on the default handlers.
            bool verified = sigField.Verify();
            Console.WriteLine("  -- {0}", 
               verified ? "Verified" : "Not verified");

            if (verified)
            {
               // has the document been modified after signing?
               bool modified = sigField.DocumentModifiedAfterSigning;
               Console.WriteLine("  -- {0}", modified ? "Modified after 
                  signing" : "Not modified after signing");
            }
         }
         else
         {
            Console.WriteLine("  -- Not signed", sigField.FullName);
         }
      }
   }
}

执行上述代码后,将向控制台写入以下内容:

Screenshot - verify.png

© . All rights reserved.