• 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         /// 
      4         /// 页顶部红色警告字样覆盖白色图片Base64.
      5         /// 
      6         private const string WatermarkCoverBase64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCABHAycDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z";
      7 
      8         /// 
      9         /// 构造函数.
     10         /// 
     11         /// 待签名文件.
     12         /// 签名图片.
     13         /// 签名证书.
     14         /// 签名证书密码.
     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         /// 
     24         /// 构造函数.
     25         /// 
     26         /// 待签名文件.
     27         /// 签名文字.
     28         /// 签名右向左宽度.
     29         /// 签名低向上高度.
     30         /// 签名证书.
     31         /// 签名证书密码.
     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         /// 
     43         /// 构造函数.
     44         /// 
     45         /// 待签名文件.
     46         /// 签名图片.
     47         /// 签名文字.
     48         /// 签名证书.
     49         /// 签名证书密码.
     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         /// 
     60         /// Gets or sets 待签名文件.
     61         /// 
     62         public byte[] WaitSignFile { get; set; }
     63 
     64         /// 
     65         /// Gets or sets 图签名.
     66         /// 
     67         public byte[] ImageSign { get; set; }
     68 
     69         /// 
     70         /// Gets or sets 文字签名.
     71         /// 
     72         public string CharactersSign { get; set; }
     73 
     74         /// 
     75         /// Gets or sets 签名右向左的宽度.
     76         /// 
     77         public float? SignRightLeftWidth { get; set; }
     78 
     79         /// 
     80         /// Gets or sets 签名顶向上高度.
     81         /// 
     82         public float? SignBottomUpHeight { get; set; }
     83 
     84         /// 
     85         /// Gets or sets 签名索引页面(不指定默认所有页进行签名).
     86         /// 
     87         public int? SignIndexPages { get; set; }
     88 
     89         /// 
     90         /// Gets or sets Pfx证书.
     91         /// 
     92         public byte[] Pfx { get; set; }
     93 
     94         /// 
     95         /// Gets or sets Pfx证书密码.
     96         /// 
     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         /// 
    162         /// 使用第三方插件 =》 去除  Evaluation Warning : The document was created with Spire.PDF for .NET.
    163         /// 
    164         /// 原文件地址
    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         /// 
    219         /// Gets or sets 签名.
    220         /// 
    221         public byte[] Sign { get; set; }
    222 
    223         /// 
    224         /// Gets or sets 签名右向左的宽度.
    225         /// 
    226         public float SignRightLeftWidth { get; set; }
    227 
    228         /// 
    229         /// Gets or sets 签名顶向上高度.
    230         /// 
    231         public float SignBottomUpHeight { get; set; }
    232 
    233         /// 
    234         /// Gets or sets 文字签名.
    235         /// 
    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         /// 
    43         /// 将文件转换为byte数组
    44         /// 
    45         /// 文件地址
    46         /// 转换后的byte数组
    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

  • 相关阅读:
    6.2.2 基于ZIP文件安装MySQL
    [PHP]ShopXO企业级B2C免费开源商城系统 v2.3.1
    【P1008 [NOIP1998 普及组] 三连击】
    NLP:《ChatGPT: Optimizing Language Models for Dialogue一种优化的对话语言模型》翻译与解读
    MySQL内置函数
    PHP包含读文件写文件
    496. 下一个更大元素 I(javascript)496. Next Greater Element I
    React编写CSS方式
    元器件贸易企业,理应“以快制胜”
    全网独有windows10安装hadoop2.2.0
  • 原文地址:https://www.cnblogs.com/PingleDay/p/16643493.html