文章目录
- 前言一、数字签名是什么? 二、数字签名的原理三、PDF的签章1.引入Nuget包 iTextSharp2.核心代码3.客户端调用
4.文档准备效果
前言
最近看到互联网上越来越多的合同都是以PDF的方式发送合同,并让用户感受到真切的法律效用,PDF合同文件都会有签章。
他有两方面好处,一个是让用户看到合同时有个正式的公司签章;另一个是PDF文件的签名能够防止篡改,具有一定的法律效应。
一、数字签名是什么?
数字签名基于哈希算法和公钥加密算法,对明文报文先用哈希算法计算摘要,然后用私钥对摘要进行加密,得到的值就是原文的数字签名。
数字签名(又称公钥数字签名、电子签章)是一种类似写在纸上的普通的物理签名,但是使用了公钥加密领域的技术实现,用于鉴别数字信息的方法。
一套数字签名通常定义两种互补的运算,一个用于签名,另一个用于验证。
二、数字签名的原理
- 发件人生成或取得独一无二的加密密码组,包括私钥和公钥。
- 发件人书写电子邮件
- 发件人用安全的摘要算法获取电子邮件的信息摘要
- 发件人再使用私钥对信息摘要进行加密,即可得到数字签名。
- 发件人将数字签名附在信息之后.
- 发件人将数字签名和信息(加密或未加密)发送给电子收件人.
- 收件人使用发件人的公共密码(公钥)确认发件人的电子签名,即将发件人的数字签名通过公钥进行解密,得到信息摘要
- 收件人使用同样安全的摘要算法,获取信息(加密或未加密)的"信息摘要".
- 收件人比较两个信息摘要.假如两者相同,则收件人可以确信信息在签发后并未作任何改变
- 收件人从证明机构处获得认证证书(或者是通过信息发件人获得),这一证书用以确认发件人发出信息上的数字签名的真实性.证明机构在数字签名系统中是一个典型的受委托管理证明业务的第三方.该证书包含发件人的公共密码和姓名(以及其他可能的附加信息),由证明机构在其上进行数字签名.
三、PDF的签章
1.引入Nuget包 iTextSharp
代码如下(示例):
Install-Package iTextSharp
iTextSharp开源地址:
2.核心代码
代码如下(示例):
/// <summary>
/// 为PDF文档签名,并附加公章、附带信息
/// </summary>
/// <param name="fileName">PDF源文件</param>
/// <param name="SignedFileName">签名后的输出文件</param>
/// <param name="SignerName">签署者</param>
/// <param name="SignatureImg">公章图片</param>
/// <param name="Pkcs">证书</param>
/// <param name="Password">密钥</param>
/// <param name="SignReason">原因</param>
/// <param name="SignLocation">位置</param>
private static void PdfSignWithStamp(string fileName,
string SignedFileName,
string SignerName,
string SignatureImg,
string Pkcs,
string Password,
string SignReason= null,
string SignLocation = null)
{
//PKCS概念:.p12是PKCS#12文件的文件扩展名,也称为“PFX文件”的备用扩展,它是保存私钥和证书的组合格式,是最新的签名实用程序使用的格式。
//一般从规范的证书颁发机构获得有效的认证证书;
//或者不考虑法律效用等问题,可以直接通过OpenSSL生成PKCS。
//具体生成方法:搜索“OpenSSL P12”
var p12 = new Pkcs12Store(new FileStream(Pkcs, FileMode.Open, FileAccess.Read), Password.ToCharArray());
var alias = "";
foreach (string al in p12.Aliases)
{
if (p12.IsKeyEntry(al) && p12.GetKey(al).Key.IsPrivate)
{
alias = al; //获得签名中的别名
break;
}
}
//获得签名实体
var pk = p12.GetKey(alias);
var rsaKey = pk.Key as RsaPrivateCrtKeyParameters;
//公司盖章的起始位置,在指定时需要设定页码
int xPos = 450;
int yPos = 100; //相对底部
int width = 100;
int heith = 100;
//原始文件
using (PdfReader reader = new PdfReader(fileName))
using (FileStream fout = new FileStream(SignedFileName, FileMode.Create, FileAccess.ReadWrite))
using (PdfStamper stamper = PdfStamper.CreateSignature(reader, fout, '\0', "temp", true))
{
//公章图片构造,需要注意的是,默认调试会报错。
//需要在报错的提示信息下边,选择:调试->异常->CLI的所有复选框均勾掉
PdfSignatureAppearance appearance = stamper.SignatureAppearance;
if (!string.IsNullOrEmpty(SignReason))
appearance.Reason = SignReason;
if (!string.IsNullOrEmpty(SignLocation))
appearance.Location = SignLocation;
appearance.SignDate = DateTime.Now;
//将公章附加到指定页码的具体位置中
appearance.SetVisibleSignature(new iTextSharp.text.Rectangle(xPos, yPos, xPos + width, yPos + heith), 10, null);
//公章图片
appearance.Image = iTextSharp.text.Image.GetInstance(SignatureImg);
appearance.ImageScale = 0.3f;
appearance.Image.Alignment = 300;
appearance.Acro6Layers = true;
//公章上层附加的文字
StringBuilder buf = new StringBuilder();
buf.Append($"Signed:{SignerName}{Environment.NewLine}");
buf.Append($"Date: {appearance.SignDate.ToString(@"yyyy/MM/dd HH:mm:ss")}");
appearance.Layer2Text = buf.ToString();
//签名SHA-256可选SHA-1
IExternalSignature es = new PrivateKeySignature(rsaKey, "SHA-256");
var certificat = new Org.BouncyCastle.X509.X509Certificate[] { p12.GetCertificate(alias).Certificate };
MakeSignature.SignDetached(appearance, es, certificat, null, null, null, 0, CryptoStandard.CMS);
}
}
3.客户端调用
static void Main(string[] args)
{
string KeyStore = @"doc\pkcs12";
string Password = "password";
string Src = @"doc\合同样本(来自网络).pdf";
string Out = @"doc\合同签名与印章(样例).pdf";
string Img = @"doc\stamp.jpg";
string Company = @"company";
Console.WriteLine("开始PDF签名...");
//PDF签名,且附加图片
PdfSignWithStamp(Src, Out, Company, Img, KeyStore,Password);
Console.WriteLine("签名完成!");
Console.ReadLine();
}
4.文档准备
1.干净的PDF源文件:“合同样本(来自网络).pdf”
2.测试用的公章图片:“stamp.jpg”
3.OpenSSL生成的p12证书,可以直接从iTextSharp的源码中拷贝一个。