我尝试多次使用itext 5.5.13.1模拟不同用户的签名对文档进行签名,PdfStamper位于AppendMode上。如果文档没有签名,则证书级别为CERTIFIED_NO_CHANGES_ALLOWED或CERTIFIED_FORM_FILLING_AND_ANNOTATIONS,否则我不会为PdfSignatureAppearnce设置此参数。第二次签名后,第一次签名无效,因为文档已更改。有什么办法解决这个问题吗?这是我的代码:
public void Sign(string Thumbprint, string document, string logoPath) {
X509Certificate2 certificate = FindCertificate(Thumbprint);
PdfReader reader = new PdfReader(document);
//Append mode
PdfStamper st = PdfStamper.CreateSignature(reader, new FileStream(SignedDocumentPath(document), FileMode.Create, FileAccess.Write), '\0', null, true);
int signatureWidth = 250;
int signatureHeight = 100;
int NewXPos = 0;
int NewYPos = 0;
SetStampCoordinates(reader, st, ref NewXPos, ref NewYPos, signatureWidth, signatureHeight);
PdfSignatureAppearance sap = st.SignatureAppearance;
if (reader.AcroFields.GetSignatureNames().Count == 0)
{
SetSignatureFieldOptions(certificate, sap, reader, "1", 1, NewXPos, NewYPos, signatureWidth, signatureHeight);
}
else {
SetSignatureFieldOptions(certificate, sap, reader, "2", NewXPos, NewYPos, signatureWidth, signatureHeight);
}
Image image = Image.GetInstance(logoPath);
image.ScaleAbsolute(50, 50);
Font font1 = SetFont("TIMES.TTF", BaseColor.BLUE, 10, 0);
Font font2 = SetFont("TIMES.TTF", BaseColor.BLUE, 8, 0);
PdfTemplate layer = sap.GetLayer(2);
Chunk chunk1 = new Chunk($"\r\nДОКУМЕНТ ПОДПИСАН\r\nЭЛЕКТРОННОЙ ПОДПИСЬЮ\r\n", font1);
Chunk chunk2 = new Chunk($"Сертификат {certificate.Thumbprint}\r\n" +
$"Владелец {certificate.GetNameInfo(X509NameType.SimpleName, false)}\r\n" +
$"Действителен с {Convert.ToDateTime(certificate.GetEffectiveDateString()).Date.ToShortDateString()} " +
$"по {Convert.ToDateTime(certificate.GetExpirationDateString()).Date.ToShortDateString()}\r\n", font2);
PdfTemplate layer0 = sap.GetLayer(0);
image.SetAbsolutePosition(5, 50);
layer0.AddImage(image);
Paragraph para1 = SetParagraphOptions(chunk1, 1, 50, 0, 2, 1.1f);
Paragraph para2 = SetParagraphOptions(chunk2, 0, 5, 15, 0.5f, 1.1f);
ColumnText ct = new ColumnText(layer);
ct.SetSimpleColumn(3f, 3f, layer.BoundingBox.Width - 3f, layer.BoundingBox.Height);
ct.AddElement(para1);
ct.AddElement(para2);
ct.Go();
layer.SetLineWidth(3);
layer.SetRGBColorStroke(0, 0, 255);
layer.Rectangle(0, 0, layer.BoundingBox.Right, layer.BoundingBox.Top);
layer.Stroke();
EncryptDocument(certificate, sap);
}
public X509Certificate2 FindCertificate(string Thumbprint) {
X509Store store = new X509Store("My", StoreLocation.CurrentUser);
store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
X509Certificate2Collection found = store.Certificates.Find(
X509FindType.FindByThumbprint, Thumbprint, true);
X509Certificate2 certificate = found[0];
if (certificate.PrivateKey is Gost3410_2012_256CryptoServiceProvider cert_key)
{
var cspParameters = new CspParameters
{
KeyContainerName = cert_key.CspKeyContainerInfo.KeyContainerName,
ProviderType = cert_key.CspKeyContainerInfo.ProviderType,
ProviderName = cert_key.CspKeyContainerInfo.ProviderName,
Flags = cert_key.CspKeyContainerInfo.MachineKeyStore
? (CspProviderFlags.UseExistingKey | CspProviderFlags.UseMachineKeyStore)
: (CspProviderFlags.UseExistingKey),
KeyPassword = new SecureString()
};
certificate = new X509Certificate2(certificate.RawData)
{
PrivateKey = new Gost3410_2012_256CryptoServiceProvider(cspParameters)
};
}
return certificate;
}
public Font SetFont(string TTFFontName, BaseColor color, float size, int style) {
string ttf = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Fonts), TTFFontName);
BaseFont baseFont = BaseFont.CreateFont(ttf, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
Font font = new Font(baseFont, size, style);
font.Color = color;
return font;
}
public Paragraph SetParagraphOptions(Chunk chunk, int ParagraphAligment, float marginLeft, float marginTop, float fixedLeading, float multipledLeading) {
Paragraph paragraph = new Paragraph();
paragraph.Alignment = ParagraphAligment;
paragraph.IndentationLeft = marginLeft;
paragraph.SpacingBefore = marginTop;
paragraph.SetLeading(fixedLeading, multipledLeading);
paragraph.Add(chunk);
return paragraph;
}
public string SignedDocumentPath(string document) {
string filename = String.Concat(Path.GetFileNameWithoutExtension(document), "_signed.pdf");
string path = Path.Combine(Path.GetDirectoryName(document), filename);
return path;
}
public void SetSignatureFieldOptions(X509Certificate2 certificate, PdfSignatureAppearance sap, PdfReader reader, string field, int level, int XPos, int YPos, int width, int height)
{
X509CertificateParser parser = new X509CertificateParser();
try
{
Rectangle rectangle = new Rectangle(XPos, YPos, XPos + width, YPos + height);
sap.SetVisibleSignature(rectangle, reader.NumberOfPages, field);
sap.Certificate = parser.ReadCertificate(certificate.RawData);
sap.Reason = "I agree";
sap.Location = "Location";
sap.Acro6Layers = true;
sap.SignDate = DateTime.Now;
sap.CertificationLevel = level;
}
catch (Exception ex)
{
if (ex.Message == $"The field {certificate.Thumbprint} already exists.")
throw new Exception("Вы уже подписали данный документ");
}
}
public void SetSignatureFieldOptions(X509Certificate2 certificate, PdfSignatureAppearance sap, PdfReader reader,string field, int XPos, int YPos, int width, int height) {
X509CertificateParser parser = new X509CertificateParser();
try
{
Rectangle rectangle = new Rectangle(XPos, YPos, XPos + width, YPos + height);
sap.SetVisibleSignature(rectangle, reader.NumberOfPages, field);
sap.Certificate = parser.ReadCertificate(certificate.RawData);
sap.Reason = "I agree";
sap.Location = "Location";
sap.Acro6Layers = true;
sap.SignDate = DateTime.Now;
}
catch (Exception ex) {
if (ex.Message == $"The field {certificate.Thumbprint} already exists.")
throw new Exception("Вы уже подписали данный документ");
}
}
public void EncryptDocument(X509Certificate2 certificate, PdfSignatureAppearance sap) {
PdfName filterName;
if (certificate.PrivateKey is Gost3410CryptoServiceProvider)
filterName = new PdfName("CryptoPro#20PDF");
else
filterName = PdfName.ADOBE_PPKLITE;
PdfSignature dic = new PdfSignature(filterName, PdfName.ADBE_PKCS7_DETACHED);
dic.Date = new PdfDate(sap.SignDate);
dic.Name = certificate.GetNameInfo(X509NameType.SimpleName, false);
if (sap.Reason != null)
dic.Reason = sap.Reason;
if (sap.Location != null)
dic.Location = sap.Location;
sap.CryptoDictionary = dic;
int intCSize = 4000;
Dictionary<PdfName, int> hashtable = new Dictionary<PdfName, int>();
hashtable[PdfName.CONTENTS] = intCSize * 2 + 2;
sap.PreClose(hashtable);
Stream s = sap.GetRangeStream();
MemoryStream ss = new MemoryStream();
int read = 0;
byte[] buff = new byte[8192];
while ((read = s.Read(buff, 0, 8192)) > 0)
{
ss.Write(buff, 0, read);
}
ContentInfo contentInfo = new ContentInfo(ss.ToArray());
SignedCms signedCms = new SignedCms(contentInfo, true);
CmsSigner cmsSigner = new CmsSigner(certificate);
signedCms.ComputeSignature(cmsSigner, false);
byte[] pk = signedCms.Encode();
byte[] outc = new byte[intCSize];
PdfDictionary dic2 = new PdfDictionary();
Array.Copy(pk, 0, outc, 0, pk.Length);
dic2.Put(PdfName.CONTENTS, new PdfString(outc).SetHexWriting(true));
sap.Close(dic2);
}
屏幕截图中最重要的部分
是签名面板上签名之间的文本“1页已修改”。这告诉我们,除了添加和填充签名字段之外,您还可以进行其他更改。通过检查文件本身,可以快速识别更改:
>
在原来的sample.pdf
只有一个内容流。
使用一个签名sample_signed.pdf
有三个内容流,原始内容流被新内容流包围。
在带有两个签名的sample_signed_signed.pdf中
有五个内容流,前三个被两个新内容流所包围。
因此,在每次签名过程中,您都要更改页面内容。正如您在本答案中所读到的,始终不允许更改已签名文档的页面内容。添加的流的内容很琐碎,这甚至于事无补,前面添加的每个流都包含:
q
每个添加到末尾的流都包含
Q
q
Q
即,只发生一些保存和恢复图形状态的情况。
上述更改是PdfStamper
方法完成的典型准备步骤,将原始内容包装在q…q中(保存
我在您发布的代码中找不到这样的调用,但在您的代码中缺少
SetStamp 坐标
方法。对于该方法中的 PdfStamper
参数,您是否可能将“获取内容
”称为“获取内容”?
由比特币的签名机制可知,如果丢失了私钥,没有任何办法可以花费对应地址的资金。 这样就使得因为丢失私钥导致资金丢失的风险会很高。为了避免一个私钥的丢失导致地址的资金丢失,比特币引入了多重签名机制,可以实现分散风险的功能。 具体来说,就是假设N个人分别持有N个私钥,只要其中M个人同意签名就可以动用某个“联合地址”的资金。 多重签名地址实际上是一个Script Hash,以2-3类型的多重签名为例,它的
前言 随着区块链等相关技术的创新和突破,很多有形或无形资产都将实现去中心化,数字资产将无处不在。比如我们这里分享的 亿书 就是要把数字出版物版权进行保护,实现去中心化,解决业界多年来版权保护不力的难题。 无论数字资产,还是数字出版版权,都是有明确所有权的,当前实现数字资产所属的技术手段就是本篇要介绍的签名。而多重签名是对签名的扩展使用,给数字资产转移提供了安全保障和技术手段。本篇,从基本概念入手,
PDF创建步骤: 通过添加空签名字段名称创建pdf:suhasb@gmail.com和nikhil.courser@gmail.com,使用原始的hello.pdf输出文件名hello_tag.pdf运行程序>tagpdfsignaturefields.java 从hello_tag.pdf文件中提取签名字段suhasb@gmail.com进行首次签名,输出文件名为hello_signd.pdf
问题内容: 我有三个类:,和。 继承自和(按此顺序)。的构造特征和不同。如何调用两个父类的方法? 我在代码中的努力: 产生错误: 我发现此资源用不同的参数集解释了多重继承,但他们建议对所有参数使用和。我觉得这很丑陋,因为我无法从子类的构造函数调用中看到将哪些参数传递给父类。 问题答案: 千万 不能* 使用,除非你知道自己在做什么。第一个参数告诉它寻找下一个要使用的方法时要 跳过的 类。例如,将查看
我试图学习WP小部件创建。在教程网站上,这是发布- 标题 我对这部分感到困惑-for="消息" 这意味着什么“message”只是一个类似的类或ID,将在CSS中设置样式?还是我没有得到正确的信息?
我正在使用Tika来自动检测被推入DMS的文档的内容类型。几乎所有的工作都很好,除了电子邮件。 我引用了核心库和tika解析器来检测pdf和msword文档。我是不是还漏掉了什么?