C#使用Spire.Pdf包对PDF文档进行数字签名

背景

  • 对PDF文档进行数字签名的需求
  • 对PDF文档添加水印的需求
  • 网上资料版本不一或不全

本文章提到的Spire.Pdf均是使用的Spire.Pdf for .NET,除此之前还有其他语言的版本,如Spire.Pdf for JAVA;
Spire.Pdf主要用于操作PDF,另外还有Spire.Excel、Spire.Doc等
主要介绍了在C#中使用Spire.Pdf组件包对PDF文档进行数字签名、添加水印功能,旨在引导大家快速、轻松的对PDF文档进行数字签名和添加水印功能;

简介

Spire.PDF for .NET 是一款专业的基于.NET平台的PDF文档控制组件。它能够让开发人员在不使用Adobe Acrobat和其他外部控件的情况下,运用.NET 应用程序创建,阅读,编写和操纵PDF 文档。Spire.PDF for .NET 功能丰富,除了基本的功能比如:绘制多种图形,图片,创建窗体字段,插入页眉页脚,输入数据表,自动对大型表格进行分页外,Spire.PDF for .NET还支持PDF数字签名,将HTML转换成PDF格式,提取PDF文档中的文本信息和图片等,目前Spire.PDF for .NET共有两个版本,一个是免费版本一个是付费版本,免费版本如果只是处理简单的pdf是没问题的,但是如果涉及到输出为pdf则会只显示前10页,第十一页则是预定的购买页介绍,我这里主要是对PDF文档的数字签名和水印,所以不涉及输出pdf;

依赖

本文示例代码依赖于Spire.Pdf,可以在项目中使用NuGet程序包引入。

源码

核心代码

  1 public class DigitalSignature
  2     {
  3         /// <summary>
  4         /// 页顶部红色警告字样覆盖白色图片Base64.
  5         /// </summary>
  6         private const string WatermarkCoverBase64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCABHAycDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z";
  7 
  8         /// <summary>
  9         /// 构造函数.
 10         /// </summary>
 11         /// <param name="waitSignFile">待签名文件.</param>
 12         /// <param name="imageSign">签名图片.</param>
 13         /// <param name="pfx">签名证书.</param>
 14         /// <param name="pfxPwd">签名证书密码.</param>
 15         public DigitalSignature(byte[] waitSignFile, byte[] imageSign, byte[] pfx, string pfxPwd)
 16         {
 17             this.WaitSignFile = waitSignFile;
 18             this.ImageSign = imageSign;
 19             this.Pfx = pfx;
 20             this.PfxPwd = pfxPwd;
 21         }
 22 
 23         /// <summary>
 24         /// 构造函数.
 25         /// </summary>
 26         /// <param name="waitSignFile">待签名文件.</param>
 27         /// <param name="charactersSign">签名文字.</param>
 28         /// <param name="signRightLeftWidth">签名右向左宽度.</param>
 29         /// <param name="signBottomUpHeight">签名低向上高度.</param>
 30         /// <param name="pfx">签名证书.</param>
 31         /// <param name="pfxPwd">签名证书密码.</param>
 32         public DigitalSignature(byte[] waitSignFile, string charactersSign, float signRightLeftWidth, float signBottomUpHeight, byte[] pfx, string pfxPwd)
 33         {
 34             this.WaitSignFile = waitSignFile;
 35             this.CharactersSign = charactersSign;
 36             this.SignRightLeftWidth = signRightLeftWidth;
 37             this.SignBottomUpHeight = signBottomUpHeight;
 38             this.Pfx = pfx;
 39             this.PfxPwd = pfxPwd;
 40         }
 41 
 42         /// <summary>
 43         /// 构造函数.
 44         /// </summary>
 45         /// <param name="waitSignFile">待签名文件.</param>
 46         /// <param name="imageSign">签名图片.</param>
 47         /// <param name="charactersSign">签名文字.</param>
 48         /// <param name="pfx">签名证书.</param>
 49         /// <param name="pfxPwd">签名证书密码.</param>
 50         public DigitalSignature(byte[] waitSignFile, byte[] imageSign, string charactersSign, byte[] pfx, string pfxPwd)
 51         {
 52             this.WaitSignFile = waitSignFile;
 53             this.ImageSign = imageSign;
 54             this.CharactersSign = charactersSign;
 55             this.Pfx = pfx;
 56             this.PfxPwd = pfxPwd;
 57         }
 58 
 59         /// <summary>
 60         /// Gets or sets 待签名文件.
 61         /// </summary>
 62         public byte[] WaitSignFile { get; set; }
 63 
 64         /// <summary>
 65         /// Gets or sets 图签名.
 66         /// </summary>
 67         public byte[] ImageSign { get; set; }
 68 
 69         /// <summary>
 70         /// Gets or sets 文字签名.
 71         /// </summary>
 72         public string CharactersSign { get; set; }
 73 
 74         /// <summary>
 75         /// Gets or sets 签名右向左的宽度.
 76         /// </summary>
 77         public float? SignRightLeftWidth { get; set; }
 78 
 79         /// <summary>
 80         /// Gets or sets 签名顶向上高度.
 81         /// </summary>
 82         public float? SignBottomUpHeight { get; set; }
 83 
 84         /// <summary>
 85         /// Gets or sets 签名索引页面(不指定默认所有页进行签名).
 86         /// </summary>
 87         public int? SignIndexPages { get; set; }
 88 
 89         /// <summary>
 90         /// Gets or sets Pfx证书.
 91         /// </summary>
 92         public byte[] Pfx { get; set; }
 93 
 94         /// <summary>
 95         /// Gets or sets Pfx证书密码.
 96         /// </summary>
 97         public string PfxPwd { get; set; }
 98 
 99         public Stream Signature()
100         {
101             ///加载PDF文档
102             PdfDocument pdf = new PdfDocument();
103             pdf.LoadFromBytes(this.WaitSignFile);
104 
105             if (pdf?.Pages?.Count <= 0)
106             {
107                 throw new Exception("文件有误");
108             }
109 
110             X509Certificate2 x509 = new X509Certificate2(this.Pfx, this.PfxPwd);
111             PdfOrdinarySignatureMaker signatureMaker = new PdfOrdinarySignatureMaker(pdf, x509);
112 
113             var appearance = new PdfCustomSignatureAppearance(this.CharactersSign, this.ImageSign, this.SignRightLeftWidth, this.SignBottomUpHeight);
114             IPdfSignatureAppearance signatureAppearance = appearance;
115 
116             // 绘画白底图片
117             PdfRubberStampAnnotation logoStamp = new PdfRubberStampAnnotation(new RectangleF(new PointF(0, 0), new SizeF(350, 22)));
118             PdfAppearance logoApprearance = new PdfAppearance(logoStamp);
119             //var logoPath = AppDomain.CurrentDomain.BaseDirectory + "\\white.jpg";
120             byte[] byt = Convert.FromBase64String(WatermarkCoverBase64);
121             Stream streamByLogo = new MemoryStream(byt);
122             PdfImage image = PdfImage.FromStream(streamByLogo);
123             PdfTemplate template = new PdfTemplate(350, 22);
124             template.Graphics.DrawImage(image, 0, 0);
125             logoApprearance.Normal = template;
126             logoStamp.Appearance = logoApprearance;
127 
128             if (this.SignIndexPages.HasValue)
129             {
130                 if (this.SignIndexPages.Value < 0 || this.SignIndexPages.Value > pdf?.Pages?.Count)
131                 {
132                     throw new Exception("签名索引页有误");
133                 }
134 
135                 var page = pdf.Pages[this.SignIndexPages.Value];
136 
137                 // 添加白底图片覆盖页面顶部印记
138                 page.AnnotationsWidget.Add(logoStamp);
139 
140                 // 在页面中的指定位置添加可视化签名
141                 signatureMaker.MakeSignature("signName_", page, page.ActualSize.Width - appearance.SignRightLeftWidth, page.ActualSize.Height - appearance.SignBottomUpHeight, appearance.SignRightLeftWidth, appearance.SignBottomUpHeight, signatureAppearance);
142             }
143             else
144             {
145                 foreach (PdfPageBase page in pdf.Pages)
146                 {
147                     // 添加白底图片覆盖页面顶部印记
148                     page.AnnotationsWidget.Add(logoStamp);
149 
150                     // 在页面中的指定位置添加可视化签名
151                     signatureMaker.MakeSignature("signName_", page, page.ActualSize.Width - appearance.SignRightLeftWidth, page.ActualSize.Height - appearance.SignBottomUpHeight, appearance.SignRightLeftWidth, appearance.SignBottomUpHeight, signatureAppearance);
152                 }
153             }
154 
155             MemoryStream stream = new MemoryStream();
156             pdf.SaveToStream(stream, FileFormat.PDF);
157             pdf.Close();
158             return stream;
159         }
160 
161         /// <summary>
162         /// 使用第三方插件 =》 去除  Evaluation Warning : The document was created with Spire.PDF for .NET.
163         /// </summary>
164         /// <param name="sourcePdfs">原文件地址</param>
165         //private static MemoryStream ClearPdfFilesFirstPage(MemoryStream sourcePdf)
166         //{
167         //    iTextSharp.text.pdf.PdfReader reader = null;
168         //    iTextSharp.text.Document document = new iTextSharp.text.Document();
169         //    iTextSharp.text.pdf.PdfImportedPage page = null;
170         //    iTextSharp.text.pdf.PdfCopy pdfCpy = null;
171         //    int n = 0;
172         //    reader = new iTextSharp.text.pdf.PdfReader(sourcePdf);
173         //    reader.ConsolidateNamedDestinations();
174         //    n = reader.NumberOfPages;
175         //    document = new iTextSharp.text.Document(reader.GetPageSizeWithRotation(1));
176         //    MemoryStream memoryStream = new MemoryStream();
177         //    pdfCpy = new iTextSharp.text.pdf.PdfCopy(document, memoryStream);
178         //    document.Open();
179         //    for (int j = 2; j <= n; j++)
180         //    {
181         //        page = pdfCpy.GetImportedPage(reader, j);
182         //        pdfCpy.AddPage(page);
183 
184         //    }
185         //    reader.Close();
186         //    document.Close();
187         //    return memoryStream;
188         //}
189     }
190 
191 
192     public class PdfCustomSignatureAppearance : IPdfSignatureAppearance
193     {
194         public PdfCustomSignatureAppearance(string charactersSign, byte[] sign, float? signRightLeftWidth, float? signBottomUpHeight)
195         {
196             this.CharactersSign = charactersSign;
197 
198             if (sign != null && sign.Length > 0)
199             {
200                 this.Sign = sign;
201                 MemoryStream ms = new MemoryStream(sign);
202                 var image = System.Drawing.Image.FromStream(ms);
203                 if (!signRightLeftWidth.HasValue)
204                 {
205                     signRightLeftWidth = image.Width;
206                 }
207 
208                 if (!signBottomUpHeight.HasValue)
209                 {
210                     signBottomUpHeight = image.Height;
211                 }
212             }
213 
214             this.SignRightLeftWidth = signRightLeftWidth.Value;
215             this.SignBottomUpHeight = signBottomUpHeight.Value;
216         }
217 
218         /// <summary>
219         /// Gets or sets 签名.
220         /// </summary>
221         public byte[] Sign { get; set; }
222 
223         /// <summary>
224         /// Gets or sets 签名右向左的宽度.
225         /// </summary>
226         public float SignRightLeftWidth { get; set; }
227 
228         /// <summary>
229         /// Gets or sets 签名顶向上高度.
230         /// </summary>
231         public float SignBottomUpHeight { get; set; }
232 
233         /// <summary>
234         /// Gets or sets 文字签名.
235         /// </summary>
236         public string CharactersSign { get; set; }
237 
238         public void Generate(PdfCanvas g)
239         {
240             if (!string.IsNullOrWhiteSpace(CharactersSign))
241             {
242                 float fontSize = 15;
243                 var font = new System.Drawing.Font("Arial", fontSize);
244                 PdfTrueTypeFont fontByPdf = new PdfTrueTypeFont(font, true);
245                 g.DrawString(CharactersSign, fontByPdf, PdfBrushes.Black, new PointF(0, 0));
246             }
247 
248             if (this.Sign != null && this.Sign.Length > 0)
249             {
250                 Stream stream = new MemoryStream(this.Sign);
251                 g.DrawImage(Spire.Pdf.Graphics.PdfImage.FromStream(stream), new PointF(20, 20));
252             }
253         }
254     }

 

调用实现

 1 static void Main(string[] args)
 2         {
 3 
 4             /*
 5                 前言:最近有个需求是需要对文档进行数字签名;
 6                 描述:本示例基于Spire.Pdf组件对PDF进行数字签名,演示了
 7                     签名证书使用项目
 8             CreateSelfSignedCertificateByBouncyCastle(https://github.com/daileass/CreateSelfSignedCertificateByBouncyCastle.git)
 9                     生成的自签名证书pfx,解决了数字签名后文档头部有警告
10 
11             */
12 
13             var fileCert = System.Environment.CurrentDirectory + "\\Cert\\";
14             var file = System.Environment.CurrentDirectory + "\\File\\";
15             var filePath = file + "dome.pdf";
16             var newFilePath = file + $"dome_{DateTime.Now.ToString("yyyyMMddHHmmss")}.pdf";
17             var pfxFilePath = fileCert + "edd9386229324d969692dcabf97ac095dpps.fun.pfx";
18             var pfxFilePwd = "ABCD123456";
19             var signFilePath = file + "sign.png";
20 
21             // 数字签名
22             var digitalSignature = new DigitalSignature(
23                 File2Bytes(filePath),
24                 File2Bytes(signFilePath),
25                 "Sign Here:",
26                 File2Bytes(pfxFilePath),
27                 pfxFilePwd
28                 );
29             var stream = digitalSignature.Signature();
30 
31             // 保存签名后的文件
32             using (var fileStream = File.Create(newFilePath))
33             {
34                 stream.Seek(0, SeekOrigin.Begin);
35                 stream.CopyTo(fileStream);
36             }
37 
38             Console.WriteLine("OK");
39             Console.ReadLine();
40         }
41 
42         /// <summary>
43         /// 将文件转换为byte数组
44         /// </summary>
45         /// <param name="path">文件地址</param>
46         /// <returns>转换后的byte数组</returns>
47         public static byte[] File2Bytes(string path)
48         {
49             if (!System.IO.File.Exists(path))
50             {
51                 return new byte[0];
52             }
53 
54             FileInfo fi = new FileInfo(path);
55             byte[] buff = new byte[fi.Length];
56 
57             FileStream fs = fi.OpenRead();
58             fs.Read(buff, 0, Convert.ToInt32(fs.Length));
59             fs.Close();
60 
61             return buff;
62         }

 

源码下载:https://github.com/daileass/PDFDigitalSignatureBySelfSignedCertificate.git

posted @ 2022-08-31 16:37  旅途。  阅读(1694)  评论(1编辑  收藏  举报