通过证书签署PDF文件

问题描述

我正在尝试使用 Syncfusion 对 PDF 文档进行数字签名。 (生成pdf文档的库)
整个文档,而不仅仅是字段。 我对签名或证书一无所知。
我知道证书是HSM的类型,所以我使用了: Syncfusion - Externally sign a pdf document

它适用于我的开发 PC,但不适用于生产。证书已找到,但在签署文件时会导致:

CryptographicException: UnkNown error „-1073741823“ at System.Security.Cryptography.Pkcs.SignedCms.Sign(CmsSigner signer,Boolean silent)

我的代码

using Syncfusion.Licensing;
using Syncfusion.Pdf;
using Syncfusion.Pdf.Graphics;
using Syncfusion.Pdf.Parsing;
using Syncfusion.Pdf.Security;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.Pkcs;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace SyncfusionTest3
{
    class Program
    {
        public static X509Certificate2 infoCertifikat = null;

        static void Main(string[] args)
        {


            var store = new System.Security.Cryptography.X509Certificates.X509Store(StoreLocation.LocalMachine);
            string thumbprint = "9F.."; //Production


            store.Open(OpenFlags.ReadOnly);

            foreach (var mCert in store.Certificates)
            {
                if (mCert.Thumbprint.toupper().Equals(thumbprint.toupper()))
                    infoCertifikat = mCert;
            }

            

            if (infoCertifikat == null)
            {
                Console.WriteLine("404 Certificate not found");
                Console.ReadKey();
                return;
            }


            string licenceKey = "LicenceKey";
            SyncfusionLicenseProvider.RegisterLicense(licenceKey);


            using (var pdfDoc = new PdfLoadedDocument("document.pdf"))
            {

                pdfDoc.Documentinformation.Creator = "Me";
                pdfDoc.Documentinformation.Author = "Naxi";


                PdfCertificate pdfCertificate = new PdfCertificate(infoCertifikat);

                //normal signing
                //Syncfusion.Pdf.Security.PdfSignature signature1 = new Syncfusion.Pdf.Security.PdfSignature(pdfDoc,pdfDoc.Pages[0],pdfCertificate,"DigitalSign");

                //External signing becouse of HSM type of certificate
                Syncfusion.Pdf.Security.PdfSignature signature1 = new Syncfusion.Pdf.Security.PdfSignature(pdfDoc,null,"DigitalSign");
                signature1.ComputeHash += Signature_ComputeHash1;

                


                signature1.Bounds = new System.Drawing.RectangleF((6 * 25.4f / 0.352777778f),(9.3f * 25.4f / 0.352777778f),65,25);
                signature1.ContactInfo = "Contact";
                signature1.LocationInfo = "World";
                signature1.Reason = "I want it";


                PdfStandardFont font = new PdfStandardFont(PdfFontFamily.Helvetica,3.8f);
                float row_height = 4.2f;

                signature1.Appearance.normal.Graphics.DrawString("Digitally Signed by " + signature1.ContactInfo,font,PdfBrushes.Black,row_height * 1);
                signature1.Appearance.normal.Graphics.DrawString("Reason: " + signature1.Reason,row_height * 2);
                signature1.Appearance.normal.Graphics.DrawString("Location: " + signature1.LocationInfo,row_height * 3);
                signature1.Appearance.normal.Graphics.DrawString((DateTime.Now).ToString(),row_height * 4);


                pdfDoc.Save("document_signed.pdf");
                pdfDoc.Close(true);
            }
        }


        

        private static void Signature_ComputeHash1(object sender,PdfSignatureEventArgs ars)
        {
            //Get the document bytes

            byte[] documentBytes = ars.Data;


            SignedCms signedCms = new SignedCms(new ContentInfo(documentBytes),detached: true);


            var cmsSigner = new CmsSigner(infoCertifikat);

            //Set the digest algorithm SHA256

            //cmsSigner.DigestAlgorithm = new Oid("1.3.6.1.4.1.311.10.3.12"); //Document signing – just tried

            //cmsSigner.DigestAlgorithm = new Oid("1.2.840.113549.1.1.11"); //SHA256RSA
            //cmsSigner.DigestAlgorithm = new Oid("1.2.840.113549.1.1.5"); //SHA1RSA
            //cmsSigner.DigestAlgorithm = new Oid("1.3.14.3.2.26"); //SHA1


            cmsSigner.DigestAlgorithm = new Oid("2.16.840.1.101.3.4.2.1"); //SHA256
            


            cmsSigner.IncludeOption = X509IncludeOption.EndCertOnly; //Without this it throws CryptographicException: A certificate chain Could not be bulit to a trusted root authority. (only in production)

            signedCms.ComputeSignature(cmsSigner);
            

            //Embed the encoded digital signature to the PDF document

            ars.SignedData = signedCms.Encode();

        }
    }
}

我尝试使用不同的 DigestAlgorithm。 (在代码中注释)但我不知道我应该使用哪个。
我为平台目标 x64 构建它,因为没有它会导致:

CryptographicException: The keyset is not defined. at System.Security.Cryptography.Pkcs.PkcsUtils.CreateSignerEncodeInfo(CmsSigner signer,Boolean silent,DafeCryptoProvHandle)

生产中使用了经过审查的证书:

Image of certificate


我的开发证书有 Key Usage: Signing document,它在生产中丢失。 或者增强的密钥使用缺少什么?

感谢您的任何建议。

更新: 所以,我以不同的方式迈出了一小步,看起来很有希望,但并不笨拙。

  1. 我尝试了 Syncfusion 的正常签名并设置了 CryptographicStandard.CADES
Syncfusion.Pdf.Security.PdfSignature signature1 = new Syncfusion.Pdf.Security.PdfSignature(pdfDoc,"DigitalSign");
signature1.Settings.CryptographicStandard = CryptographicStandard.CADES;

生成签名文档并使程序崩溃。无一例外。

  1. 我查看了一段时间前使用过的方式,发现它使用 hashSHA1、RSA + PKCS#1 进行签名。

所以,我尝试以外部方式使用 oid:

cmsSigner.DigestAlgorithm = new Oid("1,3,36,1,1"); //RSA + SHA1

这会导致:CryptographicsException:对象标识符格式错误。在 SignedCms.Sign(...)

  1. 我尝试的另一种方式是邀请同事,这样我们就可以集体受苦。

我不知道我在做什么。

更新 2:

因此,正确的方法可能是使用带有 SHA1 的外部。签名文件有效,但当程序结束时,程序不会像平常一样关闭而是停止工作。 微软应用程序certutil也是如此。 列出证书后,它停止工作。

这是关于该证书的新信息。它在由 Luna 提供的 HSM 中。

================ Certificate 0 ================
Serial Number: ...
Issuer: ...
 NotBefore: ...
 NotAfter: ...
Subject: ...
Non-root Certificate
Cert Hash(sha1): 9f
  Key Container = ...
  Provider = Luna enhanced RSA and AES provider for Microsoft Windows
Private key is NOT exportable
ERROR: Could not verify certificate public key against private key
Revocation check skipped -- server offline
Certificate is valid

解决方法

确保证书安装在正确的存储中(您的代码显示 StoreLocation.LocalMachine)。所以我们先检查store中的证书是否正常,key是否关联且可访问。

运行以下命令列出并验证存储中的所有证书:

certutil -verifystore MY

输出应该是这样的:

================ Certificate X ================
Serial Number: ...
Issuer: ...
 NotBefore: ...
 NotAfter: ...
Subject: ...
Non-root Certificate
Cert Hash(sha1): ...
  Key Container = ...
  Unique container name: ...
  Provider = ...
Private key is NOT exportable
Signature test passed
...

这将向您显示私钥关联及其提供者,并将创建一个签名以检查密钥是否可访问。

因此,请确保为运行命令的用户以及您的应用程序授予访问密钥的权限。您可能需要通过阅读 HSM 用户手册或联系负责人(内部或制造商支持)来澄清这一点

您的 SignedCms 代码看起来不错 - 对于测试,请使其尽可能简单(没有摘要算法)并逐步向前推进。

编辑

此外,当然,您需要信任您的证书。这意味着证书信任链已安装到证书存储区。

证书可以在这里找到:http://crt.postsignum.cz/certifikaty_autorit.html