Reputation: 23
Need to create a digital signed excel file and then validate the signature when uploaded in C#.
Upvotes: 2
Views: 1302
Reputation: 2259
On its own, the SpreadsheetDocument.AddDigitalSignatureOriginPart()
method does not secure an Excel file. The same is true for the corresponding methods of the WordprocessingDocument
and PresentationDocument
classes. Those methods only add an empty DigitalSignatureOriginPart
that serves as the origin of one or more XmlSignaturePart
instances, each of which contains a ds:Signature
element based on the W3C Recommendation XML Signature Syntax and Processing Version 1.1 (XMLDSIG).
To secure an Excel file, or any file based on the Open Packaging Conventions (OPC), the most straightforward approach is to use the PackageDigitalSignatureManager class, which is contained in the System.IO.Packaging
namespace as provided by the WindowsBase.dll
assembly. Thus, if you are targeting the full .NET Framework (e.g., net471), you can use it. However, if you are targeting .Net Core, you need to implement that functionality yourself.
The following code example shows how you can use the 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);
}
}
}
In case you need to implement that functionality yourself, it helps to make yourself familiar with a number of sources:
Based on those sources, I created a partial sample implementation that works with .Net Core. The following snippet shows the void Sign(OpenXmlPackage, X509Certificate2)
method that takes an OpenXmlPackage
and an X509Certificate2
and creates a valid signature:
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);
}
The full source code of the above void Sign(OpenXmlPackage, X509Certificate2)
method can be found in my CodeSnippets GitHub repository. Look for the DigitalSignatureManager class in the CodeSnippets project.
Upvotes: 6