Skip to content

Instantly share code, notes, and snippets.

@Oskang09
Created June 24, 2024 12:23
Show Gist options
  • Save Oskang09/f7796554e4e91cbb1f583f6f2b6e83f3 to your computer and use it in GitHub Desktop.
Save Oskang09/f7796554e4e91cbb1f583f6f2b6e83f3 to your computer and use it in GitHub Desktop.
public static class UblSignature
{
private const string SignatureID = "signature";
private const string SignaturePropertiesID = "id-xades-signed-props";
public static XmlElement SignWithXAdES(X509Certificate2 signingCertificate, XmlDocument xmlDocument)
{
var signedXml = new XadesSignedXml(xmlDocument);
signedXml.Signature.Id = SignatureID;
signedXml.SigningKey = signingCertificate.GetRSAPrivateKey();
var signedReference = new Reference { Id = "id-doc-signed-data", Uri = "" };
signedReference.AddTransform(new XmlDsigC14NTransform());
signedXml.AddReference(signedReference);
var keyInfo = new KeyInfo();
keyInfo.AddClause(new KeyInfoX509Data(signingCertificate));
signedXml.KeyInfo = keyInfo;
AddXAdESProperties(xmlDocument, signedXml, signingCertificate);
signedXml.ComputeSignature();
var signed = signedXml.GetXml();
XNamespace nsSig = "urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2";
XNamespace nsSac = "urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2";
XNamespace nsSbc = "urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2";
XNamespace nsCbc = "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2";
var udsWrapper = new XElement(nsSig + "UBLDocumentSignatures",
new XAttribute(XNamespace.Xmlns + "sig", nsSig),
new XAttribute(XNamespace.Xmlns + "sac", nsSac),
new XAttribute(XNamespace.Xmlns + "sbc", nsSbc),
new XAttribute(XNamespace.Xmlns + "cbc", nsCbc),
new XElement(nsSac + "SignatureInformation",
new XElement(nsCbc + "ID", "urn:oasis:names:specification:ubl:signature:1"),
new XElement(nsSbc + "ReferencedSignatureID", "urn:oasis:names:specification:ubl:signature:Invoice"),
XElement.Parse(signed.OuterXml)
)
);
var signDoc = new XmlDocument();
using (var udsReader = udsWrapper.CreateReader())
{
signDoc.Load(udsWrapper.CreateReader());
}
return signDoc.DocumentElement;
}
private static void AddXAdESProperties(XmlDocument document, XadesSignedXml xadesSignedXml, X509Certificate2 signingCertificate)
{
var parametersSignature = new Reference
{
Uri = $"#{SignaturePropertiesID}",
Type = XadesSignedXml.XmlDsigSignatureProperties,
};
xadesSignedXml.AddReference(parametersSignature);
// <Object>
var objectNode = document.CreateElement("Object", SignedXml.XmlDsigNamespaceUrl);
// <Object><QualifyingProperties>
var qualifyingPropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "QualifyingProperties", XadesSignedXml.XadesNamespaceUrl);
qualifyingPropertiesNode.SetAttribute("Target", $"#{SignatureID}");
objectNode.AppendChild(qualifyingPropertiesNode);
// <Object><QualifyingProperties><SignedProperties>
var signedPropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SignedProperties", XadesSignedXml.XadesNamespaceUrl);
signedPropertiesNode.SetAttribute("Id", SignaturePropertiesID);
qualifyingPropertiesNode.AppendChild(signedPropertiesNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties>
var signedSignaturePropertiesNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SignedSignatureProperties", XadesSignedXml.XadesNamespaceUrl);
signedPropertiesNode.AppendChild(signedSignaturePropertiesNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties> </SigningTime>
var signingTime = document.CreateElement(XadesSignedXml.XadesPrefix, "SigningTime", XadesSignedXml.XadesNamespaceUrl);
signingTime.InnerText = $"{DateTime.UtcNow:s}Z";
signedSignaturePropertiesNode.AppendChild(signingTime);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate>
var signingCertificateNode = document.CreateElement(XadesSignedXml.XadesPrefix, "SigningCertificate", XadesSignedXml.XadesNamespaceUrl);
signedSignaturePropertiesNode.AppendChild(signingCertificateNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert>
var certNode = document.CreateElement(XadesSignedXml.XadesPrefix, "Cert", XadesSignedXml.XadesNamespaceUrl);
signingCertificateNode.AppendChild(certNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest>
var certDigestNode = document.CreateElement(XadesSignedXml.XadesPrefix, "CertDigest", XadesSignedXml.XadesNamespaceUrl);
certNode.AppendChild(certDigestNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
var digestMethod = document.CreateElement("DigestMethod", SignedXml.XmlDsigNamespaceUrl);
var digestMethodAlgorithmAtribute = document.CreateAttribute("Algorithm");
digestMethodAlgorithmAtribute.InnerText = SignedXml.XmlDsigSHA256Url;
digestMethod.Attributes.Append(digestMethodAlgorithmAtribute);
certDigestNode.AppendChild(digestMethod);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><CertDigest> </DigestMethod>
var digestValue = document.CreateElement("DigestValue", SignedXml.XmlDsigNamespaceUrl);
digestValue.InnerText = Convert.ToBase64String(signingCertificate.GetCertHash(HashAlgorithmName.SHA256));
certDigestNode.AppendChild(digestValue);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial>
var issuerSerialNode = document.CreateElement(XadesSignedXml.XadesPrefix, "IssuerSerial", XadesSignedXml.XadesNamespaceUrl);
certNode.AppendChild(issuerSerialNode);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509IssuerName>
var x509IssuerName = document.CreateElement("X509IssuerName", SignedXml.XmlDsigNamespaceUrl);
x509IssuerName.InnerText = signingCertificate.Issuer;
issuerSerialNode.AppendChild(x509IssuerName);
// <Object><QualifyingProperties><SignedProperties><SignedSignatureProperties><SigningCertificate><Cert><IssuerSerial> </X509SerialNumber>
var x509SerialNumber = document.CreateElement("X509SerialNumber", SignedXml.XmlDsigNamespaceUrl);
x509SerialNumber.InnerText = ToDecimalString(signingCertificate.SerialNumber);
issuerSerialNode.AppendChild(x509SerialNumber);
var dataObject = new DataObject();
dataObject.Data = qualifyingPropertiesNode.SelectNodes(".");
xadesSignedXml.AddObject(dataObject);
}
private static string ToDecimalString(string serialNumber)
{
BigInteger bi;
if (BigInteger.TryParse(serialNumber, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out bi))
{
return bi.ToString(CultureInfo.InvariantCulture);
}
else
{
return serialNumber;
}
}
}
public class XadesSignedXml : SignedXml
{
public const string XadesPrefix = "xades";
public const string DsigPrefix = "ds";
public const string XmlDsigSignatureProperties = "http://www.w3.org/2000/09/xmldsig#SignatureProperties";
public const string XadesNamespaceUrl = "http://uri.etsi.org/01903/v1.3.2#";
public XmlElement PropertiesNode { get; set; }
private readonly List<DataObject> _dataObjects = new List<DataObject>();
public XadesSignedXml(XmlDocument document) : base(document) { }
public override XmlElement GetIdElement(XmlDocument document, string idValue)
{
if (string.IsNullOrEmpty(idValue))
return null;
var xmlElement = base.GetIdElement(document, idValue);
if (xmlElement != null)
return xmlElement;
if (_dataObjects.Count == 0)
return null;
foreach (var dataObject in _dataObjects)
{
var nodeWithSameID = FindNodeWithPrefix(dataObject.Data, "Id", idValue);
if (nodeWithSameID != null)
return nodeWithSameID;
}
return null;
}
public new void AddObject(DataObject dataObject)
{
base.AddObject(dataObject);
_dataObjects.Add(dataObject);
}
private XmlElement FindNodeWithPrefix(XmlNodeList nodeList, string idKey, string idValue)
{
foreach (XmlNode node in nodeList)
{
var attribute = node.Attributes[idKey];
if (attribute != null && attribute.Value == idValue)
return (XmlElement)node;
if (!node.HasChildNodes) continue;
var foundNode = FindNodeWithPrefix(node.ChildNodes, idKey, idValue);
if (foundNode != null) return foundNode;
}
return null;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment