C#实现PDF签名时添加时间戳的2种方法(附VB.NET代码)

让我来详细讲解一下“C#实现PDF签名时添加时间戳的2种方法(附VB.NET代码)”这篇文章的完整攻略。

一、背景

在使用C#代码实现PDF文件的数字签名时,如果需要添加时间戳的话,可以使用以下两种方法:基于PDF签名规范(PDF Signature Appearances)和基于PDF变量(PDF Variables)。两种方法均需使用第三方的时间戳服务器,比如Entrust TSA、GlobalSign TSA等。

二、基于PDF签名规范(PDF Signature Appearances)

  1. 添加签名域

首先,需要在PDF文档中添加签名域。此处来看一下如何使用C#代码向PDF文档添加签名域:

using System;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

class Program
{
    static void Main()
    {
        string inputFilePath = "input.pdf";
        string outputFilePath = "output.pdf";
        string fieldName = "sigField";

        using (PdfReader reader = new PdfReader(inputFilePath))
        using (FileStream stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
        using (PdfStamper stamper = PdfStamper.CreateSignature(reader, stream, '\0'))
        {
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;
            appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), reader.NumberOfPages, fieldName);
            appearance.CertificationLevel = PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_SIGNING;
            appearance.Reason = "I certify that the information provided above is true and correct.";
            appearance.Location = "New York, NY";
            appearance.Contact = "signature@example.com";
            appearance.Layer2Text = "Digitally signed by John Doe.\nDate: {0}\nTS: {1}";
            appearance.Acro6Layers = true;
            appearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;

            stamper.Close();
        }
    }
}

上述代码中,添加签名域的核心代码是:

PdfSignatureAppearance appearance = stamper.SignatureAppearance;
appearance.SetVisibleSignature(new Rectangle(36, 748, 144, 780), reader.NumberOfPages, fieldName);

该代码指定了签名域的位置和大小,并指定了签名域的名称。其他的代码指定了签名的一些属性,比如“证明级别”、“原因”、“地点”、“联系人”等等。

  1. 签名操作

接下来,需要进行PDF签名操作。签名过程中需要添加时间戳。我们可以使用PDFBox提供的方法来获取时间戳:

using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.X509;
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Xml;

class Program
{
    static void Main()
    {
        string inputFilePath = "input.pdf";
        string outputFilePath = "output.pdf";
        string certFilePath = "cert.pfx";
        string certPassword = "password";
        string tsaURL = "http://timestamp.globalsign.com/scripts/timstamp.dll";
        string fieldName = "sigField";

        using (PdfReader reader = new PdfReader(inputFilePath))
        using (FileStream stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
        using (PdfStamper stamper = PdfStamper.CreateSignature(reader, stream, '\0'))
        {
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;

            // 更改代码:添加时间戳
            byte[] contentHash = SHA256.Create().ComputeHash(reader.GetPageContent(reader.NumberOfPages));
            TimeStampToken timeStampToken = GetTimeStampToken(tsaURL, contentHash);
            byte[] timestampBytes = timeStampToken.GetEncoded();

            PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
            dic.Date = new PdfDate(appearance.SignDate);
            dic.Name = "Digital Signature";
            dic.Reason = "I certify that the information provided above is true and correct.";
            dic.Location = "New York, NY";
            dic.Contact = "signature@example.com";
            dic.Layer2Text = "Digitally signed by John Doe.\nDate: {0}\nTS: {1}";
            dic.Bytes = new PdfStream(timestampBytes);

            MakeSignature.SignDetached(appearance, new IExternalSignature[] { new X509Certificate2Signature(GetCertificate(certFilePath, certPassword), "SHA-256") }, new[] { dic }, null, null, null, 0, CryptoStandard.CMS);

            stamper.Close();
        }
    }

    static TimeStampToken GetTimeStampToken(string url, byte[] contentHash)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/timestamp-query";
        request.ContentLength = contentHash.Length;
        Stream stream = request.GetRequestStream();
        stream.Write(contentHash, 0, contentHash.Length);
        stream.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream responseStream = response.GetResponseStream();

        TimeStampResponse timeStampResponse = new TimeStampResponse(responseStream);
        timeStampResponse.Validate(new SignerInformationVerifier(new JcaContentVerifierProviderBuilder().Build(GetCertificate(timeStampResponse, ResponseType.TimeStampToken).PublicKey)));
        return timeStampResponse.TimeStampToken;
    }

    static X509Certificate2 GetCertificate(string filename, string password)
    {
        return new X509Certificate2(filename, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
    }

    static X509Certificate2 GetCertificate(TimeStampResponse response, ResponseType responseType)
    {
        if (response.Status != 0)
        {
            throw new Exception("TimeStampServer responded with an error : " + response.Status);
        }

        Asn1Sequence sequence = (Asn1Sequence)response.TimeStampToken.GetSignedContent().ToAsn1Object();
        ContentInfo contentInfo = new ContentInfo(sequence);

        if (responseType == ResponseType.IndividualData)
        {
            return new X509Certificate2(contentInfo.Content.GetOctets());
        }
        else if (responseType == ResponseType.TimeStampToken)
        {
            return new X509Certificate2(response.TimeStampToken.GetCertificates()[0].GetEncoded());
        }
        else
        {
            return null;
        }
    }

    enum ResponseType
    {
        Unknown,
        IndividualData,
        TimeStampToken
    }
}

上述代码中,“更改代码”部分指定了获取时间戳的流程,包括向时间戳服务器发送请求、获取响应、解析响应等等。而添加时间戳的核心代码是:

PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
dic.Bytes = new PdfStream(timestampBytes);

MakeSignature.SignDetached(appearance, new IExternalSignature[] { new X509Certificate2Signature(GetCertificate(certFilePath, certPassword), "SHA-256") }, new[] { dic }, null, null, null, 0, CryptoStandard.CMS);

其中,PdfSignature类表示数字签名,并将数字签名的字节数组设置为时间戳字节数组。MakeSignature.SignDetached方法用于进行PDF签名操作,其中包含一个签名字典数组,数组中的一个元素就是我们前面创建的数字签名。通过数字签名中包含的时间戳信息,PDF阅读器就能够确认该PDF文档的签名时间。

三、基于PDF变量(PDF Variables)

  1. 添加变量

首先,需要在PDF文档中添加变量。此处来看一下如何使用C#代码向PDF文档添加变量:

using System.Collections.Generic;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.pdf;

class Program
{
    static void Main()
    {
        string inputFilePath = "input.pdf";
        string outputFilePath = "output.pdf";
        string fieldName = "sigField";

        using (PdfReader reader = new PdfReader(inputFilePath))
        using (FileStream stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
        using (PdfStamper stamper = new PdfStamper(reader, stream))
        {
            stamper.AcroFields.SetField(fieldName, "Digitally signed by John Doe.\nDate: {0}\nTS: {1}");
            stamper.AcroFields.SetFieldProperty(fieldName, "textfont", BaseFont.HELVETICA_BOLD, null);
            stamper.AcroFields.SetFieldProperty(fieldName, "textsize", 12f, null);
            stamper.Close();
        }
    }
}

上述代码中,添加变量的核心代码是:

stamper.AcroFields.SetField(fieldName, "Digitally signed by John Doe.\nDate: {0}\nTS: {1}");
stamper.AcroFields.SetFieldProperty(fieldName, "textfont", BaseFont.HELVETICA_BOLD, null);
stamper.AcroFields.SetFieldProperty(fieldName, "textsize", 12f, null);

该代码指定了变量的名称和内容,并设置了变量的字体、大小等。

  1. 签名操作

接下来,需要进行PDF签名操作。签名过程中需要添加时间戳。我们可以直接在签名过程中添加变量值,从而将时间戳信息添加到PDF中:

using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Tsp;
using Org.BouncyCastle.X509;
using System;
using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates;

class Program
{
    static void Main()
    {
        string inputFilePath = "input.pdf";
        string outputFilePath = "output.pdf";
        string certFilePath = "cert.pfx";
        string certPassword = "password";
        string tsaURL = "http://timestamp.globalsign.com/scripts/timstamp.dll";
        string fieldName = "sigField";

        using (PdfReader reader = new PdfReader(inputFilePath))
        using (FileStream stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.Write))
        using (PdfStamper stamper = PdfStamper.CreateSignature(reader, stream, '\0'))
        {
            PdfSignatureAppearance appearance = stamper.SignatureAppearance;

            // 更改代码:添加时间戳
            byte[] contentHash = SHA256.Create().ComputeHash(reader.GetPageContent(reader.NumberOfPages));
            TimeStampToken timeStampToken = GetTimeStampToken(tsaURL, contentHash);
            string time = timeStampToken.TimeStampInfo.GenTime.ToString("yyyy-MM-dd HH:mm:ss");
            string tsa = timeStampToken.TimeStampInfo.Tsa.ToString();

            stamper.AcroFields.SetField(fieldName, string.Format("Digitally signed by John Doe.\nDate: {0}\nTS: {1}", time, tsa));

            MakeSignature.SignDetached(appearance, new IExternalSignature[] { new X509Certificate2Signature(GetCertificate(certFilePath, certPassword), "SHA-256") }, null, null, null, null, 0, CryptoStandard.CMS);

            stamper.Close();
        }
    }

    static TimeStampToken GetTimeStampToken(string url, byte[] contentHash)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.ContentType = "application/timestamp-query";
        request.ContentLength = contentHash.Length;
        Stream stream = request.GetRequestStream();
        stream.Write(contentHash, 0, contentHash.Length);
        stream.Close();

        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        Stream responseStream = response.GetResponseStream();

        TimeStampResponse timeStampResponse = new TimeStampResponse(responseStream);
        timeStampResponse.Validate(new SignerInformationVerifier(new JcaContentVerifierProviderBuilder().Build(GetCertificate(timeStampResponse, ResponseType.TimeStampToken).PublicKey)));
        return timeStampResponse.TimeStampToken;
    }

    static X509Certificate2 GetCertificate(string filename, string password)
    {
        return new X509Certificate2(filename, password, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet);
    }

    static X509Certificate2 GetCertificate(TimeStampResponse response, ResponseType responseType)
    {
        if (response.Status != 0)
        {
            throw new Exception("TimeStampServer responded with an error : " + response.Status);
        }

        Asn1Sequence sequence = (Asn1Sequence)response.TimeStampToken.GetSignedContent().ToAsn1Object();
        ContentInfo contentInfo = new ContentInfo(sequence);

        if (responseType == ResponseType.IndividualData)
        {
            return new X509Certificate2(contentInfo.Content.GetOctets());
        }
        else if (responseType == ResponseType.TimeStampToken)
        {
            return new X509Certificate2(response.TimeStampToken.GetCertificates()[0].GetEncoded());
        }
        else
        {
            return null;
        }
    }

    enum ResponseType
    {
        Unknown,
        IndividualData,
        TimeStampToken
    }
}

上述代码中,“更改代码”部分指定了获取时间戳的流程,包括向时间戳服务器发送请求、获取响应、解析响应等等。添加变量的核心代码是:

stamper.AcroFields.SetField(fieldName, string.Format("Digitally signed by John Doe.\nDate: {0}\nTS: {1}", time, tsa));

其中,timetsa分别表示签名时间和时间戳服务器地址。通过将时间戳信息添加到PDF文档中,我们就可以在PDF文档中标识出签名的时间。

至此,我们已经介绍了基于PDF签名规范(PDF Signature Appearances)和基于PDF变量(PDF Variables)两种方法,来实现添加时间戳的PDF签名操作。具体实现可以参考本文的示例代码。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C#实现PDF签名时添加时间戳的2种方法(附VB.NET代码) - Python技术站

(0)
上一篇 2023年6月1日
下一篇 2023年6月1日

相关文章

  • C#实现Array,List,Dictionary相互转换

    下面详细讲解一下C#实现Array、List、Dictionary相互转换的完整攻略。 1. Array和List的相互转换 Array转List 使用ToList()方法可以将Array类型的数组转换为List泛型集合类型,具体代码如下所示: string[] array = { "apple", "banana",…

    C# 2023年6月7日
    00
  • 详解.NET Core 3.0 里新的JSON API

    在本攻略中,我们将详细讲解.NET Core 3.0中新的JSON API,并提供两个示例说明。 安装Microsoft.AspNetCore.Mvc.NewtonsoftJson:首先,我们需要安装Microsoft.AspNetCore.Mvc.NewtonsoftJson NuGet包。我们可以使用Visual Studio的Get包管理器来安装Mic…

    C# 2023年5月16日
    00
  • Js 导出table内容到Excel的简单实例

    首先我会讲解如何通过js导出table内容到Excel。以下是完整的攻略: 准备工作 编写html页面,并在页面中创建一个table并填充数据 导入jquery、TableExport等库文件 步骤 加载TableExport插件库文件 <script src="js/FileSaver.min.js"></script…

    C# 2023年6月1日
    00
  • C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例 策略模式介绍 策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。 策略模式的实现方法 在策略模式中,定义一个具体的策略接口(抽象类),接口中定义公共的方法(通用的算法),具体的策略类实现这个接口,实现各自的算法。…

    C# 2023年6月1日
    00
  • Unity实现物体左右移动效果

    Unity是一款流行的游戏开发引擎,它可以实现许多游戏功能包括制作物体左右移动效果。下面将详细讲解Unity实现物体左右移动效果的完整攻略。 实现方式 在Unity中实现物体左右移动的基本方式是通过脚本在Update函数中改变物体的位置。因此,我们需要找到需要移动的对象,创建一个用于移动的脚本,并在脚本的Update函数中修改物体的位置。 1. 创建控制脚本…

    C# 2023年6月3日
    00
  • .NET Core 基于Websocket的在线聊天室实现

    .NET Core 基于 Websocket 的在线聊天室实现攻略 在 .NET Core 中,我们可以使用 Websocket 技术来实现在线聊天室。本攻略将介绍如何使用 .NET Core 实现基于 Websocket 的在线聊天室。 步骤 以下是实现基于 Websocket 的在线聊天室的步骤: 创建项目。 使用 Visual Studio 或者 .N…

    C# 2023年5月17日
    00
  • C#实现简单的字符串加密

    下面我给你详细讲解一下C#实现简单的字符串加密的完整攻略。 一、加密算法的选择 字符串加密可以采用多种算法,这里我们使用最简单的一种——Caesar密码算法。该算法原理是将字符串中的每个字符按照一定数目的偏移量加密,解密时再将字符按照相同的偏移量向相反的方向偏移即可。 二、编写加密函数 接下来我们来编写一个加密函数。假设加密偏移量为3,我们将该函数命名为En…

    C# 2023年6月6日
    00
  • 字符串优化

    C#字符串优化学习总结 内存区域 我们知道一个由C/C++编译的程序占用的内存分为以下几个部分: 1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap) : 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于…

    C# 2023年4月27日
    00
合作推广
合作推广
分享本页
返回顶部