能够对pdf进行图片签名的,网上找来找去,就一个iText是免费的,但这个免费是AGPL协议,当然它也有商业协议,iText现在有5和7两个版本,这里我采用的是版本5,nuget下载地址为:https://www.nuget.org/packages/iTextSharp/,然后为了多做比较,我这里还找了Spire.PDF进行测试,其免费版本Free.Spire.PDF还是3.2版本,而其收费版本目前为4.2版本,这里先直接说测试结果,免费版本可以进行数字签名,但图片签名不行,只要将图片赋给对应参数,保存时必定报转化异常,而4.2版本代码与3.2的相比需要稍作调整(只是建议性质,不调整也能运行),可以成功保存,另外收费版本的我还找了PDF-Writer,因为它的例子本身就包含了图片签名部分,所以这里不做描述,以下是iText和Spire代码:
using iTextSharp.text.pdf.security;
using Org.BouncyCastle.Pkcs;//这是itext自带的,不需要单独添加BouncyCastle包
using System.Drawing;
public class PdfDigstSign
{
static string pfxPath = "001180530510644.pfx";
static string pfxPwd = "111111";
static string sourcePdf = "template.pdf";
static string imgPath = "password_1.png";
public static void PDFSignWithIText5Demo()
{
string outPutPdf = "ImageStampWithIText5.pdf";
var reader = new iTextSharp.text.pdf.PdfReader(sourcePdf);
using (FileStream fs = new FileStream(outPutPdf, FileMode.Create))
{
var stamper = iTextSharp.text.pdf.PdfStamper.CreateSignature(reader, fs, '\0', null, true);
var appearance = stamper.SignatureAppearance;
appearance.Reason = "签名测试原因";
appearance.Location = "签名位置";
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(200, 200, 300, 300), 1, "itext");
//itext会自动对图片进行等比例缩放
appearance.SignatureGraphic = iTextSharp.text.Image.GetInstance(imgPath);
appearance.CertificationLevel = iTextSharp.text.pdf.PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED;
appearance.SignatureRenderingMode = iTextSharp.text.pdf.PdfSignatureAppearance.RenderingMode.GRAPHIC;
using (var stream = File.OpenRead(pfxPath))
{
Pkcs12Store store = new Pkcs12Store(stream, pfxPwd.ToCharArray());
string keyAlias = null;
foreach (string alias in store.Aliases)
{
keyAlias = alias;
break;
}
var chains = store.GetCertificateChain(keyAlias).Select(c => c.Certificate).ToList();
var signature = new PrivateKeySignature(store.GetKey(keyAlias).Key, DigestAlgorithms.SHA1);
MakeSignature.SignDetached(appearance, signature, chains, null, null, null, 0, CryptoStandard.CADES);
}
}
}
public static void PDFSignWithSpirePDF4_2Demo()
{
//必须要商业版才行
var doc = new Spire.Pdf.PdfDocument();
doc.LoadFromFile(sourcePdf);
var page = doc.Pages[0];
var cert = new Spire.Pdf.Security.PdfCertificate(pfxPath, pfxPwd);
var signature = new Spire.Pdf.Security.PdfSignature(doc, page, cert, "spire");
//
var loApprearance = signature.Appearence;
var image = Spire.Pdf.Graphics.PdfImage.FromFile(imgPath);
var template = new Spire.Pdf.Graphics.PdfTemplate(image.Width, image.Height);
template.Graphics.DrawImage(image, 0, 0,100,100);//spire由代码指定缩放
loApprearance.Normal = template;
signature.Bounds = new RectangleF(new PointF(100, 100), new SizeF(100, 100));
signature.Reason = "签名原因";
signature.LocationInfo = "签名位置";
signature.GraphicsMode = Spire.Pdf.Security.GraphicMode.SignNameOnly;
signature.DocumentPermissions = Spire.Pdf.Security.PdfCertificationFlags.ForbidChanges;
string output = "ImageStampWithSpirePDF4_2.pdf";
doc.SaveToFile(output);
}
}
上述代码运行完后,两者的签名面板部分稍有不同,具体如下:
之所以生成的略有差异,主要是方法参数不同,在iText中,将PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED修改为PdfSignatureAppearance.NOT_CERTIFIED,两者将显示一致
2018-04-11补充
Spire.Pdf低商业版本(3.x版本)图片签名有bug,只能通过ConfiguerGraphicPath属性指定图片的物理地址来进行图片签名,但这样就不能利用上面例子中template可以为图片指定压缩比来减小PDF大小的好处,所以折中的方式是设置签名方式为如下方式
signature.DigitalSigner = "";
signature.ConfigGraphicType = ConfiguerGraphicType.Text;
利用空字符串来间接的达到只输出显示图片的效果
另外补充 Spire.Pdf免费版不能进行并发操作,商业版才可以