XML digital signature functionality for Greeva electronic invoicing documents
$ dotnet add package Greeva.Xml.SignXML digital signature functionality for Greeva electronic invoicing documents.
Greeva.Xml.Sign provides digital signature capabilities for XML documents generated by Greeva, ensuring compliance with SUNAT requirements for electronic invoicing in Peru.
using Greeva.Xml.Sign;
using System.Security.Cryptography.X509Certificates;
// Load certificate from PFX file
var certificate = CertificateLoader.LoadFromPfx("certificate.pfx", "password");
// Create XML signer
var xmlSigner = new XmlSigner(certificate);
// Sign XML document
string xmlContent = "<?xml version='1.0'?>..."; // Your XML content
string signedXml = xmlSigner.SignXml(xmlContent);
// Or sign asynchronously
string signedXmlAsync = await xmlSigner.SignXmlAsync(xmlContent);
var certificate = CertificateLoader.LoadFromPfx("path/to/certificate.pfx", "password");
string pemContent = "-----BEGIN CERTIFICATE-----...";
var certificate = CertificateLoader.LoadFromPem(pemContent);
string certPem = "-----BEGIN CERTIFICATE-----...";
string keyPem = "-----BEGIN PRIVATE KEY-----...";
var certificate = CertificateLoader.LoadFromPem(certPem, keyPem);
// Register in DI container
services.AddScoped<IXmlSigner>(provider =>
{
var certificate = CertificateLoader.LoadFromPfx("certificate.pfx", "password");
return new XmlSigner(certificate);
});
// Use in service
public class DocumentService
{
private readonly IXmlSigner _xmlSigner;
public DocumentService(IXmlSigner xmlSigner)
{
_xmlSigner = xmlSigner;
}
public async Task<string> ProcessDocumentAsync(string xmlContent)
{
return await _xmlSigner.SignXmlAsync(xmlContent);
}
}
// Load from Windows Certificate Store
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certificate = store.Certificates
.Find(X509FindType.FindBySubjectName, "Your Certificate Subject", false)
.Cast<X509Certificate2>()
.FirstOrDefault();
store.Close();
if (certificate != null)
{
var xmlSigner = new XmlSigner(certificate);
}
// Verify certificate has private key
if (!certificate.HasPrivateKey)
{
throw new InvalidOperationException("Certificate must have a private key for signing");
}
// Check certificate validity
if (DateTime.Now < certificate.NotBefore || DateTime.Now > certificate.NotAfter)
{
throw new InvalidOperationException("Certificate is not valid for current date");
}
// Verify signed XML
bool isValid = xmlSigner.VerifySignature(signedXmlContent);
if (isValid)
{
Console.WriteLine("Signature is valid");
}
else
{
Console.WriteLine("Signature is invalid or corrupted");
}
The XML signatures generated comply with SUNAT requirements:
Generated signatures follow this structure:
<ext:ExtensionContent>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="GREEVA-SIGN">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue>...</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>...</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
</ext:ExtensionContent>
try
{
var signedXml = xmlSigner.SignXml(xmlContent);
}
catch (ArgumentException ex)
{
// Invalid XML content
Console.WriteLine($"Invalid XML: {ex.Message}");
}
catch (InvalidOperationException ex)
{
// Certificate or signing issues
Console.WriteLine($"Signing error: {ex.Message}");
}
catch (CryptographicException ex)
{
// Cryptographic operations failed
Console.WriteLine($"Cryptographic error: {ex.Message}");
}
MIT License - see LICENSE file for details.
For issues and questions, please visit: https://github.com/augustoteles/greeva/issues