如何使用 AddDigitalSignatureOriginPart(DocumentFormat.OpenXml 库)来保护 Excel 文件?

How to use AddDigitalSignatureOriginPart (DocumentFormat.OpenXml library) to secure an Excel file?

需要创建一个数字签名的 excel 文件,然后在用 C# 上传时验证签名。

SpreadsheetDocument.AddDigitalSignatureOriginPart() 方法本身不能保护 Excel 文件。 WordprocessingDocumentPresentationDocumentclasses对应的方法也是如此。这些方法仅添加一个空 DigitalSignatureOriginPart 作为一个或多个 XmlSignaturePart 实例的来源,每个实例包含一个基于 W3C 推荐标准 XML Signature Syntax and Processing Version 1.1 (XMLDSIG ).

要保护 Excel 文件或任何基于开放打包约定 (OPC) 的文件,最直接的方法是使用 PackageDigitalSignatureManager class,其中包含在 System.IO.Packaging 程序集提供的 System.IO.Packaging 命名空间中。因此,如果您的目标是完整的 .NET Framework(例如 net471),则可以使用它。但是,如果您的目标是 .Net Core,则需要自己实现该功能。

以下代码示例显示了如何使用 PackageDigitalSignatureManager class:

using System;
using System.Collections.Generic;
using System.IO.Packaging;
using System.Linq;

namespace CodeSnippets.Windows.IO.Packaging
{
    public static class DigitalSignatureManager
    {
        public static void Sign(Package package)
        {
            var dsm = new PackageDigitalSignatureManager(package)
            {
                CertificateOption = CertificateEmbeddingOption.InSignaturePart
            };

            List<Uri> parts = package.GetParts()
                .Select(part => part.Uri)
                .Concat(new[]
                {
                    // Include the DigitalSignatureOriginPart and corresponding
                    // relationship part, since those will only be added when
                    // signing.
                    dsm.SignatureOrigin,
                    PackUriHelper.GetRelationshipPartUri(dsm.SignatureOrigin)
                })
                .ToList();

            dsm.Sign(parts);
        }

        public static VerifyResult VerifySignature(Package package)
        {
            var dsm = new PackageDigitalSignatureManager(package);
            return dsm.VerifySignatures(true);
        }
    }
}

如果您需要自己实现该功能,熟悉一些来源会有所帮助:

基于这些资源,我创建了一个适用于 .Net Core 的部分示例实现。以下片段显示了 void Sign(OpenXmlPackage, X509Certificate2) 方法,该方法采用 OpenXmlPackageX509Certificate2 并创建有效签名:

        public static void Sign(OpenXmlPackage openXmlPackage, X509Certificate2 certificate)
        {
            if (openXmlPackage == null) throw new ArgumentNullException(nameof(openXmlPackage));
            if (certificate == null) throw new ArgumentNullException(nameof(certificate));

            RSA privateKey = certificate.GetRSAPrivateKey();
            using SHA256 hashAlgorithm = SHA256.Create();

            // Create KeyInfo.
            var keyInfo = new KeyInfo();
            keyInfo.AddClause(new KeyInfoX509Data(certificate));

            // Create a Signature XmlElement.
            var signedXml = new SignedXml { SigningKey = privateKey, KeyInfo = keyInfo };
            signedXml.Signature.Id = Constants.PackageSignatureId;
            signedXml.SignedInfo.SignatureMethod = Constants.SignatureMethod;
            signedXml.AddReference(CreatePackageObjectReference());
            signedXml.AddObject(CreatePackageObject(openXmlPackage.Package, hashAlgorithm));
            signedXml.ComputeSignature();
            XmlElement signature = signedXml.GetXml();

            // Get or create the DigitalSignatureOriginPart.
            DigitalSignatureOriginPart dsOriginPart =
                openXmlPackage.GetPartsOfType<DigitalSignatureOriginPart>().FirstOrDefault() ??
                openXmlPackage.AddNewPart<DigitalSignatureOriginPart>();

            var xmlSignaturePart = dsOriginPart.AddNewPart<XmlSignaturePart>();

            // Write the Signature XmlElement to the XmlSignaturePart.
            using Stream stream = xmlSignaturePart.GetStream(FileMode.Create, FileAccess.Write);
            using XmlWriter writer = XmlWriter.Create(stream);
            signature.WriteTo(writer);
        }

上面void Sign(OpenXmlPackage, X509Certificate2)方法的完整源码可以在我的CodeSnippets GitHub repository. Look for the DigitalSignatureManagerclassCodeSnippets项目中找到