K
Khoa Dinh
Guest
I am learning digital signature and how to sign it in c#.Here is my code:
Signature.cs
public class Signature
{
static readonly string RT_OfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
static readonly string OfficeObjectID = "idOfficeObject";
static readonly string SignatureID = "idPackageSignature";
static readonly string ManifestHashAlgorithm = "XML-Signature Syntax and Processing";
// Entry Point
public static void DigiSign(string tempfile)
{
// Open the Package
using (Package package = Package.Open(tempfile))
{
// Get the certificate
X509Certificate2 certificate = GetCertificate();
SignAllParts(package, certificate);
}
}
private static void SignAllParts(Package package, X509Certificate certificate)
{
if (package == null) throw new ArgumentNullException("SignAllParts(package)");
List<Uri> PartstobeSigned = new List<Uri>();
List<PackageRelationshipSelector> SignableReleationships = new List<PackageRelationshipSelector>();
foreach (PackageRelationship relationship in package.GetRelationshipsByType(RT_OfficeDocument))
{
// Pass the releationship of the root. This is decided based on the RT_OfficeDocument (http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument)
CreateListOfSignableItems(relationship, PartstobeSigned, SignableReleationships);
}
// Create the DigitalSignature Manager
PackageDigitalSignatureManager dsm = new PackageDigitalSignatureManager(package);
dsm.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
string signatureID = SignatureID;
string manifestHashAlgorithm = ManifestHashAlgorithm;
System.Security.Cryptography.Xml.DataObject officeObject = CreateOfficeObject(signatureID, manifestHashAlgorithm);
Reference officeObjectReference = new Reference("#" + OfficeObjectID);
try
{
dsm.Sign(PartstobeSigned, certificate, SignableReleationships, signatureID, new System.Security.Cryptography.Xml.DataObject[] { officeObject }, new Reference[] { officeObjectReference });
}
catch (CryptographicException ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
}// end:SignAllParts()
/**************************SignDocument******************************/
// This function is a helper function. The main role of this function is to
// create two lists, one with Package Parts that you want to sign, the other
// containing PacakgeRelationshipSelector objects which indicate relationships to sign.
/*******************************************************************/
static void CreateListOfSignableItems(PackageRelationship relationship, List<Uri> PartstobeSigned, List<PackageRelationshipSelector> SignableReleationships)
{
// This function adds the releation to SignableReleationships. And then it gets the part based on the releationship. Parts URI gets added to the PartstobeSigned list.
PackageRelationshipSelector selector = new PackageRelationshipSelector(relationship.SourceUri, PackageRelationshipSelectorType.Id, relationship.Id);
SignableReleationships.Add(selector);
if (relationship.TargetMode == TargetMode.Internal)
{
PackagePart part = relationship.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
if (PartstobeSigned.Contains(part.Uri) == false)
{
PartstobeSigned.Add(part.Uri);
// GetRelationships Function: Returns a Collection Of all the releationships that are owned by the part.
foreach (PackageRelationship childRelationship in part.GetRelationships())
{
CreateListOfSignableItems(childRelationship, PartstobeSigned, SignableReleationships);
}
}
}
}
/**************************SignDocument******************************/
// Once you create the list and try to sign it, Office will not validate the Signature.
// To allow Office to validate the signature, it requires a custom object which should be added to the
// signature parts. This function loads the OfficeObject.xml resource.
// Please note that GUID being passed in document.Loadxml.
// Background Information: Once you add a SignatureLine in Word, Word gives a unique GUID to it. Now while loading the
// OfficeObject.xml, we need to make sure that The this GUID should match to the ID of the signature line.
// So if you are generating a SignatureLine programmtically, then mmake sure that you generate the GUID for the
// SignatureLine and for this element.
/*******************************************************************/
static System.Security.Cryptography.Xml.DataObject CreateOfficeObject(
string signatureID, string manifestHashAlgorithm)
{
XmlDocument document = new XmlDocument();
document.LoadXml(String.Format(Properties.Resources.OfficeObject, signatureID, manifestHashAlgorithm, "{3CF6B91E-C5F6-46A4-B036-72597274FCC0}"));
System.Security.Cryptography.Xml.DataObject officeObject = new System.Security.Cryptography.Xml.DataObject();
// do not change the order of the following two lines
officeObject.LoadXml(document.DocumentElement); // resets ID
officeObject.Id = OfficeObjectID; // required ID, do not change
return officeObject;
}
/********************************************************/
static X509Certificate2 GetCertificate()
{
X509Store certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(certStore.Certificates, "Select a certificate", "Please select a certificate",
X509SelectionFlag.SingleSelection);
return certs.Count > 0 ? certs[0] : null;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
Signature.DigiSign(@"D:\abc.docx");
}
}
After signed ,in the Additional Information of the signature , the system date/time(sign time) is diferrent from my local time and the date/time format too.I try to change my local time zone and reset date/time but it still not work. What am i missing?
I can't upload image so i provide a link to it : View: https://i.stack.imgur.com/hPHJk.png
Continue reading...
Signature.cs
public class Signature
{
static readonly string RT_OfficeDocument = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
static readonly string OfficeObjectID = "idOfficeObject";
static readonly string SignatureID = "idPackageSignature";
static readonly string ManifestHashAlgorithm = "XML-Signature Syntax and Processing";
// Entry Point
public static void DigiSign(string tempfile)
{
// Open the Package
using (Package package = Package.Open(tempfile))
{
// Get the certificate
X509Certificate2 certificate = GetCertificate();
SignAllParts(package, certificate);
}
}
private static void SignAllParts(Package package, X509Certificate certificate)
{
if (package == null) throw new ArgumentNullException("SignAllParts(package)");
List<Uri> PartstobeSigned = new List<Uri>();
List<PackageRelationshipSelector> SignableReleationships = new List<PackageRelationshipSelector>();
foreach (PackageRelationship relationship in package.GetRelationshipsByType(RT_OfficeDocument))
{
// Pass the releationship of the root. This is decided based on the RT_OfficeDocument (http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument)
CreateListOfSignableItems(relationship, PartstobeSigned, SignableReleationships);
}
// Create the DigitalSignature Manager
PackageDigitalSignatureManager dsm = new PackageDigitalSignatureManager(package);
dsm.CertificateOption = CertificateEmbeddingOption.InSignaturePart;
string signatureID = SignatureID;
string manifestHashAlgorithm = ManifestHashAlgorithm;
System.Security.Cryptography.Xml.DataObject officeObject = CreateOfficeObject(signatureID, manifestHashAlgorithm);
Reference officeObjectReference = new Reference("#" + OfficeObjectID);
try
{
dsm.Sign(PartstobeSigned, certificate, SignableReleationships, signatureID, new System.Security.Cryptography.Xml.DataObject[] { officeObject }, new Reference[] { officeObjectReference });
}
catch (CryptographicException ex)
{
Console.WriteLine(ex.InnerException.ToString());
}
}// end:SignAllParts()
/**************************SignDocument******************************/
// This function is a helper function. The main role of this function is to
// create two lists, one with Package Parts that you want to sign, the other
// containing PacakgeRelationshipSelector objects which indicate relationships to sign.
/*******************************************************************/
static void CreateListOfSignableItems(PackageRelationship relationship, List<Uri> PartstobeSigned, List<PackageRelationshipSelector> SignableReleationships)
{
// This function adds the releation to SignableReleationships. And then it gets the part based on the releationship. Parts URI gets added to the PartstobeSigned list.
PackageRelationshipSelector selector = new PackageRelationshipSelector(relationship.SourceUri, PackageRelationshipSelectorType.Id, relationship.Id);
SignableReleationships.Add(selector);
if (relationship.TargetMode == TargetMode.Internal)
{
PackagePart part = relationship.Package.GetPart(PackUriHelper.ResolvePartUri(relationship.SourceUri, relationship.TargetUri));
if (PartstobeSigned.Contains(part.Uri) == false)
{
PartstobeSigned.Add(part.Uri);
// GetRelationships Function: Returns a Collection Of all the releationships that are owned by the part.
foreach (PackageRelationship childRelationship in part.GetRelationships())
{
CreateListOfSignableItems(childRelationship, PartstobeSigned, SignableReleationships);
}
}
}
}
/**************************SignDocument******************************/
// Once you create the list and try to sign it, Office will not validate the Signature.
// To allow Office to validate the signature, it requires a custom object which should be added to the
// signature parts. This function loads the OfficeObject.xml resource.
// Please note that GUID being passed in document.Loadxml.
// Background Information: Once you add a SignatureLine in Word, Word gives a unique GUID to it. Now while loading the
// OfficeObject.xml, we need to make sure that The this GUID should match to the ID of the signature line.
// So if you are generating a SignatureLine programmtically, then mmake sure that you generate the GUID for the
// SignatureLine and for this element.
/*******************************************************************/
static System.Security.Cryptography.Xml.DataObject CreateOfficeObject(
string signatureID, string manifestHashAlgorithm)
{
XmlDocument document = new XmlDocument();
document.LoadXml(String.Format(Properties.Resources.OfficeObject, signatureID, manifestHashAlgorithm, "{3CF6B91E-C5F6-46A4-B036-72597274FCC0}"));
System.Security.Cryptography.Xml.DataObject officeObject = new System.Security.Cryptography.Xml.DataObject();
// do not change the order of the following two lines
officeObject.LoadXml(document.DocumentElement); // resets ID
officeObject.Id = OfficeObjectID; // required ID, do not change
return officeObject;
}
/********************************************************/
static X509Certificate2 GetCertificate()
{
X509Store certStore = new X509Store(StoreLocation.CurrentUser);
certStore.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = X509Certificate2UI.SelectFromCollection(certStore.Certificates, "Select a certificate", "Please select a certificate",
X509SelectionFlag.SingleSelection);
return certs.Count > 0 ? certs[0] : null;
}
}
Program.cs
class Program
{
static void Main(string[] args)
{
Signature.DigiSign(@"D:\abc.docx");
}
}
After signed ,in the Additional Information of the signature , the system date/time(sign time) is diferrent from my local time and the date/time format too.I try to change my local time zone and reset date/time but it still not work. What am i missing?
I can't upload image so i provide a link to it : View: https://i.stack.imgur.com/hPHJk.png
Continue reading...