C#利用Spire.Pdf包實(shí)現(xiàn)為PDF添加數(shù)字簽名
背景
- 對(duì)PDF文檔進(jìn)行數(shù)字簽名的需求
- 對(duì)PDF文檔添加水印的需求
- 網(wǎng)上資料版本不一或不全
本文章提到的Spire.Pdf均是使用的Spire.Pdf for .NET,除此之前還有其他語(yǔ)言的版本,如Spire.Pdf for JAVA;
Spire.Pdf主要用于操作PDF,另外還有Spire.Excel、Spire.Doc等
主要介紹了在C#中使用Spire.Pdf組件包對(duì)PDF文檔進(jìn)行數(shù)字簽名、添加水印功能,旨在引導(dǎo)大家快速、輕松的對(duì)PDF文檔進(jìn)行數(shù)字簽名和添加水印功能;
簡(jiǎn)介
Spire.PDF for .NET 是一款專業(yè)的基于.NET平臺(tái)的PDF文檔控制組件。它能夠讓開(kāi)發(fā)人員在不使用Adobe Acrobat和其他外部控件的情況下,運(yùn)用.NET 應(yīng)用程序創(chuàng)建,閱讀,編寫和操縱PDF 文檔。Spire.PDF for .NET 功能豐富,除了基本的功能比如:繪制多種圖形,圖片,創(chuàng)建窗體字段,插入頁(yè)眉頁(yè)腳,輸入數(shù)據(jù)表,自動(dòng)對(duì)大型表格進(jìn)行分頁(yè)外,Spire.PDF for .NET還支持PDF數(shù)字簽名,將HTML轉(zhuǎn)換成PDF格式,提取PDF文檔中的文本信息和圖片等,目前Spire.PDF for .NET共有兩個(gè)版本,一個(gè)是免費(fèi)版本一個(gè)是付費(fèi)版本,免費(fèi)版本如果只是處理簡(jiǎn)單的pdf是沒(méi)問(wèn)題的,但是如果涉及到輸出為pdf則會(huì)只顯示前10頁(yè),第十一頁(yè)則是預(yù)定的購(gòu)買頁(yè)介紹,我這里主要是對(duì)PDF文檔的數(shù)字簽名和水印,所以不涉及輸出pdf;
依賴
本文示例代碼依賴于Spire.Pdf,可以在項(xiàng)目中使用NuGet程序包引入。
源碼
核心代碼
public class DigitalSignature
{
/// <summary>
/// 頁(yè)頂部紅色警告字樣覆蓋白色圖片Base64.
/// </summary>
private const string WatermarkCoverBase64 = "/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCABHAycDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD9U6KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA//Z";
/// <summary>
/// 構(gòu)造函數(shù).
/// </summary>
/// <param name="waitSignFile">待簽名文件.</param>
/// <param name="imageSign">簽名圖片.</param>
/// <param name="pfx">簽名證書.</param>
/// <param name="pfxPwd">簽名證書密碼.</param>
public DigitalSignature(byte[] waitSignFile, byte[] imageSign, byte[] pfx, string pfxPwd)
{
this.WaitSignFile = waitSignFile;
this.ImageSign = imageSign;
this.Pfx = pfx;
this.PfxPwd = pfxPwd;
}
/// <summary>
/// 構(gòu)造函數(shù).
/// </summary>
/// <param name="waitSignFile">待簽名文件.</param>
/// <param name="charactersSign">簽名文字.</param>
/// <param name="signRightLeftWidth">簽名右向左寬度.</param>
/// <param name="signBottomUpHeight">簽名低向上高度.</param>
/// <param name="pfx">簽名證書.</param>
/// <param name="pfxPwd">簽名證書密碼.</param>
public DigitalSignature(byte[] waitSignFile, string charactersSign, float signRightLeftWidth, float signBottomUpHeight, byte[] pfx, string pfxPwd)
{
this.WaitSignFile = waitSignFile;
this.CharactersSign = charactersSign;
this.SignRightLeftWidth = signRightLeftWidth;
this.SignBottomUpHeight = signBottomUpHeight;
this.Pfx = pfx;
this.PfxPwd = pfxPwd;
}
/// <summary>
/// 構(gòu)造函數(shù).
/// </summary>
/// <param name="waitSignFile">待簽名文件.</param>
/// <param name="imageSign">簽名圖片.</param>
/// <param name="charactersSign">簽名文字.</param>
/// <param name="pfx">簽名證書.</param>
/// <param name="pfxPwd">簽名證書密碼.</param>
public DigitalSignature(byte[] waitSignFile, byte[] imageSign, string charactersSign, byte[] pfx, string pfxPwd)
{
this.WaitSignFile = waitSignFile;
this.ImageSign = imageSign;
this.CharactersSign = charactersSign;
this.Pfx = pfx;
this.PfxPwd = pfxPwd;
}
/// <summary>
/// Gets or sets 待簽名文件.
/// </summary>
public byte[] WaitSignFile { get; set; }
/// <summary>
/// Gets or sets 圖簽名.
/// </summary>
public byte[] ImageSign { get; set; }
/// <summary>
/// Gets or sets 文字簽名.
/// </summary>
public string CharactersSign { get; set; }
/// <summary>
/// Gets or sets 簽名右向左的寬度.
/// </summary>
public float? SignRightLeftWidth { get; set; }
/// <summary>
/// Gets or sets 簽名頂向上高度.
/// </summary>
public float? SignBottomUpHeight { get; set; }
/// <summary>
/// Gets or sets 簽名索引頁(yè)面(不指定默認(rèn)所有頁(yè)進(jìn)行簽名).
/// </summary>
public int? SignIndexPages { get; set; }
/// <summary>
/// Gets or sets Pfx證書.
/// </summary>
public byte[] Pfx { get; set; }
/// <summary>
/// Gets or sets Pfx證書密碼.
/// </summary>
public string PfxPwd { get; set; }
public Stream Signature()
{
///加載PDF文檔
PdfDocument pdf = new PdfDocument();
pdf.LoadFromBytes(this.WaitSignFile);
if (pdf?.Pages?.Count <= 0)
{
throw new Exception("文件有誤");
}
X509Certificate2 x509 = new X509Certificate2(this.Pfx, this.PfxPwd);
PdfOrdinarySignatureMaker signatureMaker = new PdfOrdinarySignatureMaker(pdf, x509);
var appearance = new PdfCustomSignatureAppearance(this.CharactersSign, this.ImageSign, this.SignRightLeftWidth, this.SignBottomUpHeight);
IPdfSignatureAppearance signatureAppearance = appearance;
// 繪畫白底圖片
PdfRubberStampAnnotation logoStamp = new PdfRubberStampAnnotation(new RectangleF(new PointF(0, 0), new SizeF(350, 22)));
PdfAppearance logoApprearance = new PdfAppearance(logoStamp);
//var logoPath = AppDomain.CurrentDomain.BaseDirectory + "\\white.jpg";
byte[] byt = Convert.FromBase64String(WatermarkCoverBase64);
Stream streamByLogo = new MemoryStream(byt);
PdfImage image = PdfImage.FromStream(streamByLogo);
PdfTemplate template = new PdfTemplate(350, 22);
template.Graphics.DrawImage(image, 0, 0);
logoApprearance.Normal = template;
logoStamp.Appearance = logoApprearance;
if (this.SignIndexPages.HasValue)
{
if (this.SignIndexPages.Value < 0 || this.SignIndexPages.Value > pdf?.Pages?.Count)
{
throw new Exception("簽名索引頁(yè)有誤");
}
var page = pdf.Pages[this.SignIndexPages.Value];
// 添加白底圖片覆蓋頁(yè)面頂部印記
page.AnnotationsWidget.Add(logoStamp);
// 在頁(yè)面中的指定位置添加可視化簽名
signatureMaker.MakeSignature("signName_", page, page.ActualSize.Width - appearance.SignRightLeftWidth, page.ActualSize.Height - appearance.SignBottomUpHeight, appearance.SignRightLeftWidth, appearance.SignBottomUpHeight, signatureAppearance);
}
else
{
foreach (PdfPageBase page in pdf.Pages)
{
// 添加白底圖片覆蓋頁(yè)面頂部印記
page.AnnotationsWidget.Add(logoStamp);
// 在頁(yè)面中的指定位置添加可視化簽名
signatureMaker.MakeSignature("signName_", page, page.ActualSize.Width - appearance.SignRightLeftWidth, page.ActualSize.Height - appearance.SignBottomUpHeight, appearance.SignRightLeftWidth, appearance.SignBottomUpHeight, signatureAppearance);
}
}
MemoryStream stream = new MemoryStream();
pdf.SaveToStream(stream, FileFormat.PDF);
pdf.Close();
return stream;
}
/// <summary>
/// 使用第三方插件 =》 去除 Evaluation Warning : The document was created with Spire.PDF for .NET.
/// </summary>
/// <param name="sourcePdfs">原文件地址</param>
//private static MemoryStream ClearPdfFilesFirstPage(MemoryStream sourcePdf)
//{
// iTextSharp.text.pdf.PdfReader reader = null;
// iTextSharp.text.Document document = new iTextSharp.text.Document();
// iTextSharp.text.pdf.PdfImportedPage page = null;
// iTextSharp.text.pdf.PdfCopy pdfCpy = null;
// int n = 0;
// reader = new iTextSharp.text.pdf.PdfReader(sourcePdf);
// reader.ConsolidateNamedDestinations();
// n = reader.NumberOfPages;
// document = new iTextSharp.text.Document(reader.GetPageSizeWithRotation(1));
// MemoryStream memoryStream = new MemoryStream();
// pdfCpy = new iTextSharp.text.pdf.PdfCopy(document, memoryStream);
// document.Open();
// for (int j = 2; j <= n; j++)
// {
// page = pdfCpy.GetImportedPage(reader, j);
// pdfCpy.AddPage(page);
// }
// reader.Close();
// document.Close();
// return memoryStream;
//}
}
public class PdfCustomSignatureAppearance : IPdfSignatureAppearance
{
public PdfCustomSignatureAppearance(string charactersSign, byte[] sign, float? signRightLeftWidth, float? signBottomUpHeight)
{
this.CharactersSign = charactersSign;
if (sign != null && sign.Length > 0)
{
this.Sign = sign;
MemoryStream ms = new MemoryStream(sign);
var image = System.Drawing.Image.FromStream(ms);
if (!signRightLeftWidth.HasValue)
{
signRightLeftWidth = image.Width;
}
if (!signBottomUpHeight.HasValue)
{
signBottomUpHeight = image.Height;
}
}
this.SignRightLeftWidth = signRightLeftWidth.Value;
this.SignBottomUpHeight = signBottomUpHeight.Value;
}
/// <summary>
/// Gets or sets 簽名.
/// </summary>
public byte[] Sign { get; set; }
/// <summary>
/// Gets or sets 簽名右向左的寬度.
/// </summary>
public float SignRightLeftWidth { get; set; }
/// <summary>
/// Gets or sets 簽名頂向上高度.
/// </summary>
public float SignBottomUpHeight { get; set; }
/// <summary>
/// Gets or sets 文字簽名.
/// </summary>
public string CharactersSign { get; set; }
public void Generate(PdfCanvas g)
{
if (!string.IsNullOrWhiteSpace(CharactersSign))
{
float fontSize = 15;
var font = new System.Drawing.Font("Arial", fontSize);
PdfTrueTypeFont fontByPdf = new PdfTrueTypeFont(font, true);
g.DrawString(CharactersSign, fontByPdf, PdfBrushes.Black, new PointF(0, 0));
}
if (this.Sign != null && this.Sign.Length > 0)
{
Stream stream = new MemoryStream(this.Sign);
g.DrawImage(Spire.Pdf.Graphics.PdfImage.FromStream(stream), new PointF(20, 20));
}
}
}調(diào)用實(shí)現(xiàn)
static void Main(string[] args)
{
/*
前言:最近有個(gè)需求是需要對(duì)文檔進(jìn)行數(shù)字簽名;
描述:本示例基于Spire.Pdf組件對(duì)PDF進(jìn)行數(shù)字簽名,演示了
簽名證書使用項(xiàng)目
CreateSelfSignedCertificateByBouncyCastle(https://github.com/daileass/CreateSelfSignedCertificateByBouncyCastle.git)
生成的自簽名證書pfx,解決了數(shù)字簽名后文檔頭部有警告
*/
var fileCert = System.Environment.CurrentDirectory + "\\Cert\\";
var file = System.Environment.CurrentDirectory + "\\File\\";
var filePath = file + "dome.pdf";
var newFilePath = file + $"dome_{DateTime.Now.ToString("yyyyMMddHHmmss")}.pdf";
var pfxFilePath = fileCert + "edd9386229324d969692dcabf97ac095dpps.fun.pfx";
var pfxFilePwd = "ABCD123456";
var signFilePath = file + "sign.png";
// 數(shù)字簽名
var digitalSignature = new DigitalSignature(
File2Bytes(filePath),
File2Bytes(signFilePath),
"Sign Here:",
File2Bytes(pfxFilePath),
pfxFilePwd
);
var stream = digitalSignature.Signature();
// 保存簽名后的文件
using (var fileStream = File.Create(newFilePath))
{
stream.Seek(0, SeekOrigin.Begin);
stream.CopyTo(fileStream);
}
Console.WriteLine("OK");
Console.ReadLine();
}
/// <summary>
/// 將文件轉(zhuǎn)換為byte數(shù)組
/// </summary>
/// <param name="path">文件地址</param>
/// <returns>轉(zhuǎn)換后的byte數(shù)組</returns>
public static byte[] File2Bytes(string path)
{
if (!System.IO.File.Exists(path))
{
return new byte[0];
}
FileInfo fi = new FileInfo(path);
byte[] buff = new byte[fi.Length];
FileStream fs = fi.OpenRead();
fs.Read(buff, 0, Convert.ToInt32(fs.Length));
fs.Close();
return buff;
}源碼下載:https://github.com/daileass/PDFDigitalSignatureBySelfSignedCertificate
到此這篇關(guān)于C#利用Spire.Pdf包實(shí)現(xiàn)為PDF添加數(shù)字簽名的文章就介紹到這了,更多相關(guān)C# PDF添加數(shù)字簽名內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WPF實(shí)現(xiàn)調(diào)用本機(jī)攝像頭的示例代碼
這篇文章主要介紹了如何利用WPF實(shí)現(xiàn)調(diào)用本機(jī)攝像頭,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下2022-08-08
c#打印預(yù)覽控件中實(shí)現(xiàn)用鼠標(biāo)移動(dòng)頁(yè)面功能代碼分享
項(xiàng)目中需要實(shí)現(xiàn)以下功能:打印預(yù)覽控件中,可以用鼠標(biāo)拖動(dòng)頁(yè)面,以查看超出顯示范圍之外的部分內(nèi)容,下面就是實(shí)現(xiàn)代碼2013-12-12
C#抓取網(wǎng)頁(yè)數(shù)據(jù) 解析標(biāo)題描述圖片等信息 去除HTML標(biāo)簽
本文主要一步一步介紹利用C#抓取頁(yè)面數(shù)據(jù)的過(guò)程,抓取HTML,獲取標(biāo)題、描述、圖片等信息,并去除HTML,希望對(duì)大家有所幫助。2016-04-04
C#實(shí)現(xiàn)遠(yuǎn)程連接ORACLE數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了C#實(shí)現(xiàn)遠(yuǎn)程連接ORACLE數(shù)據(jù)庫(kù)的方法,通過(guò)自定義函數(shù)db_connection_test實(shí)現(xiàn)遠(yuǎn)程連接Oracle數(shù)據(jù)庫(kù)的功能,是非常實(shí)用的技巧,需要的朋友可以參考下2014-12-12
c# Bitmap轉(zhuǎn)bitmapImage高效方法
本文主要介紹了c# Bitmap轉(zhuǎn)bitmapImage高效方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11
C#中comboBox實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)
給大家分享了C#中comboBox實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的全部代碼,代碼經(jīng)過(guò)測(cè)試,有興趣的朋友跟著做一下。2018-03-03

