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

yizhihongxing

让我来详细讲解一下“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日

相关文章

  • aspx 页面弹出窗口代码大全

    下面是详细的攻略: ASPX 页面弹出窗口代码大全 在 ASPX 页面中,弹出窗口是实现一些交互和提示的重要手段。下面是常用的弹出窗口代码集合。 alert 弹出窗口 alert 可以在页面上弹出一个提示信息窗口,用法如下: alert(‘Hello, World!’); 其中,’Hello, World!’ 是想要显示在弹出窗口中的提示信息。 confir…

    C# 2023年5月31日
    00
  • 在Asp.net用C#建立动态Excel

    建立动态Excel是Asp.net应用程序中非常常见的功能需求,通过C#代码动态生成Excel,可以直接展示数据并且有良好的展示效果。 下面是实现“在Asp.net用C#建立动态Excel”的完整攻略: 步骤一:安装相关组件 创建动态Excel需要使用Microsoft Office Excel插件,因此我们需要安装相关组件来支持这一功能。同时,还需要引用M…

    C# 2023年6月7日
    00
  • C# 注册表 操作实现代码

    C# 中操作注册表的方法非常简单,以下是一些基本的操作实现代码: 读取注册表 using Microsoft.Win32; // 打开要读取的注册表键 RegistryKey regKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\…

    C# 2023年6月6日
    00
  • 详解Winform里面的缓存使用

    在Winform中,缓存是一种常用的技术,用于提高应用程序的性能和响应速度。本文将提供详细的“详解Winform里面的缓存使用”的完整攻略,包括什么是缓存、为什么要使用缓存、如何使用缓存以及两个示例。 什么是缓存? 缓存是一种将数据存储在内存中的技术,以便在需要时快速访问数据。缓存可以提高应用程序的性能和响应速度,因为从内存中读取数据比从磁盘或网络中读取数据…

    C# 2023年5月15日
    00
  • C# WinForm程序处理后台繁忙导致前台控件假死现象解决方法

    背景介绍C# WinForm程序处理后台繁忙导致前台控件假死现象,是C#程序员常见的问题之一。为了提升程序的用户体验,我们需要采取措施解决此问题。 解决方法①使用多线程异步处理:在C# WinForm程序中,多线程是解决后台繁忙导致前台假死的最好方法。我们可以使用C#语言内置的多线程操作来实现此功能。例如采用BackgroundWorker组件实现异步处理。…

    C# 2023年5月31日
    00
  • 深入浅析C#中单点登录的原理和使用

    深入浅析C#中单点登录的原理和使用 单点登录 (Single Sign-On, SSO) 是一种常见的身份认证方式,允许用户在多个应用中使用同一套凭据进行登录,而不需要重复输入用户名和密码。本文将详细讲解 C# 中单点登录的原理和使用。 原理 单点登录的实现原理涉及到以下三个方面: 身份认证 凭据共享 会话管理 身份认证 在单点登录过程中,用户只需要认证一次…

    C# 2023年5月31日
    00
  • 浅谈对c# 面向对象的理解

    浅谈对C#面向对象的理解 C# 面向对象编程的基本概念 C# 是一种面向对象的编程语言,对象是构成类的基本单元,一个类包含属性、方法和事件。在面向对象编程中,将程序中的所有事物都看作对象,这些对象之间可以互相传递消息,完成整个程序的任务。一般来说,面向对象编程强调以下概念: 类(Class):类是面向对象程序的基本单元,类定义了一组数据和行为,用于描述某个实…

    C# 2023年6月1日
    00
  • ASP.NET自带对象JSON字符串与实体类的转换

    在ASP.NET项目中,我们常常需要将一个JSON字符串转换成一个实体对象,或者将一个实体对象转换成JSON字符串。ASP.NET提供了方便的自带对象来处理这种转换,下面将详细讲解实现的步骤。 1. 将JSON字符串转换成实体类对象 将JSON字符串转换为实体类对象的基本步骤如下: 引入命名空间 我们需要引入System.Web.Script.Seriali…

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