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#影院售票系统毕业设计(1)

    下面逐步讲解“C#影院售票系统毕业设计(1)”的完整攻略。 1. 确定项目需求 要开发一个影院售票系统,需要满足以下需求: 用户端 用户可以通过图形化操作,实现选座、支付等功能; 可以查看影片排片信息、影院信息、个人信息等; 可以对已完成订单进行评价和投诉; 实现基本的安全性,如密码保护等。 管理员端 管理员可以通过图形化界面添加、修改、删除影片和影院信息;…

    C# 2023年6月1日
    00
  • C# Winfrom实现Skyline画直线功能的示例代码

    让我为您详细讲解“C# Winfrom实现Skyline画直线功能的完整攻略”: 1. 确定需求 在开始编写代码前,首先要确定我们的需求就是实现Skyline功能,即在Windows窗体中画直线。 2. 创建Windows窗体应用程序 根据需求,我们需要创建一个Windows窗体应用程序。可以在Visual Studio中选择新建项目,选择“WindowsF…

    C# 2023年6月6日
    00
  • 实例代码讲解c# 线程(上)

    让我来详细讲解一下“实例代码讲解c# 线程(上)”的完整攻略。 标题 首先,我们需要为文章设置标题。根据内容来判断,可以设置成如下格式: 实例代码讲解c# 线程(上) 介绍 在本篇文章中,我们将会介绍c#编程语言中线程的概念和使用方法。 线程是什么? 线程是程序执行的一条路径。在c#中,线程是一个轻量级的操作系统对象,它能够并发地执行代码。c#中的线程可以与…

    C# 2023年5月31日
    00
  • C# 设置Chart的X轴为时间轴​​​​​​​详情

    下面我为您详细讲解一下“C# 设置Chart的X轴为时间轴”的完整攻略,过程中包含两条示例。 前置知识 在了解如何设置Chart的X轴为时间轴之前,您需要掌握以下知识: C#语言基础 Chart控件使用基础 时间格式化 设定X轴为时间轴 Chart控件中的轴(Axis)类物件,其中有多种轴如X轴、Y轴以及二级轴等等,而控制X轴显示类型的属性有AxisType…

    C# 2023年5月15日
    00
  • SpringBoot与velocity的结合的示例代码

    下面是关于“SpringBoot与velocity的结合的示例代码”的完整攻略及示例说明: 1. 环境准备 在开始之前,需要确保以下环境已经准备完整: JDK 1.8或以上 Maven SpringBoot Velocity 如果您还没安装或搭建好以上环境,请先进行安装和配置。 2. 引入依赖 在SpringBoot项目的pom.xml文件中,加入以下依赖:…

    C# 2023年5月31日
    00
  • c#在sql中存取图片image示例

    下面我将为您详细讲解如何使用C#在SQL中存取图片的完整攻略。 1. 创建存储图片的表 首先,需要在SQL Server中创建一个表来存储图片。以下是一个简单的示例表: CREATE TABLE Images( ImageID INT IDENTITY(1,1) PRIMARY KEY, ImageName VARCHAR(100), ImageData V…

    C# 2023年6月2日
    00
  • C#中四步轻松使用log4net记录本地日志的方法

    C#中四步轻松使用log4net记录本地日志的方法 前言 在软件开发中,日志是一种不可或缺的手段来帮助开发人员了解程序运行情况以及查找问题。log4net是一个强大的日志工具,能够轻松地记录日志信息并提供良好的输出格式。在本文中,我们将会演示如何使用log4net记录本地日志。 步骤 以下步骤将详细介绍如何在C#中使用log4net记录本地日志。 1. 添加…

    C# 2023年6月1日
    00
  • ASP.NET MVC获取多级类别组合下的产品

    以下是ASP.NET MVC获取多级类别组合下的产品的完整攻略: 简介 在ASP.NET MVC应用程序中,我们可能需要获取多级类别组合下的产品,例如,我们可能需要获取所有属于“电子产品”类别及其子类别的产品。在这种情况下,我们可以使用递归查询或LINQ查询获取多级类别组合下的产品。 步骤 ASP.NET MVC获取多级类别组合下的产品的步骤如下: 创建类别…

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