.NET 8中为XPath转换添加XML命名空间以解决签名异常
问题
使用.NET 8的SignedXml对XML文件签名时,调用ComputeSignature()抛出以下异常:
Unhandled exception. System.Xml.XPath.XPathException: Namespace prefix 'ext' is not defined. at MS.Internal.Xml.XPath.CompiledXpathExpr.UndefinedXsltContext.LookupNamespace(String prefix) at MS.Internal.Xml.XPath.BaseAxisQuery.SetXsltContext(XsltContext context) at MS.Internal.Xml.XPath.CompiledXpathExpr.SetContext(IXmlNamespaceResolver nsResolver) at System.Security.Cryptography.Xml.XmlDsigXPathTransform.GetOutput() at System.Security.Cryptography.Xml.TransformChain.TransformToOctetStream(Object inputObject, XmlResolver resolver, String baseUri) at System.Security.Cryptography.Xml.Reference.CalculateHashValue(XmlDocument document, CanonicalXmlNodeList refList) at System.Security.Cryptography.Xml.Reference.UpdateHashValue(XmlDocument document, CanonicalXmlNodeList refList) at System.Security.Cryptography.Xml.SignedXml.BuildDigestedReferences() at System.Security.Cryptography.Xml.SignedXml.ComputeSignature()
对应的XPath转换片段如下:
<?xml version="1.0" encoding="UTF-8"?> <Invoice xmlns="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:ext="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"> <ext:UBLExtensions> <ext:UBLExtension> <ext:ExtensionURI>urn:oasis:names:specification:ubl:dsig:enveloped:xades</ext:ExtensionURI> <ext:ExtensionContent> <sig:UBLDocumentSignatures xmlns:sig="urn:oasis:names:specification:ubl:schema:xsd:CommonSignatureComponents-2" xmlns:sac="urn:oasis:names:specification:ubl:schema:xsd:SignatureAggregateComponents-2" xmlns:sbc="urn:oasis:names:specification:ubl:schema:xsd:SignatureBasicComponents-2"> <sac:SignatureInformation> <cbc:ID>urn:oasis:names:specification:ubl:signature:1</cbc:ID> <sbc:ReferencedSignatureID>urn:oasis:names:specification:ubl:signature:Invoice</sbc:ReferencedSignatureID> <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="signature"> <ds:SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2006/12/xml-c14n11"/> <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"/> <ds:Reference Id="invoiceSignedData" URI=""> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::ext:UBLExtensions)</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::cac:Signature)</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/1999/REC-xpath-19991116"> <ds:XPath>not(//ancestor-or-self::cac:AdditionalDocumentReference[cbc:ID='QR'])</ds:XPath> </ds:Transform> <ds:Transform Algorithm="http://www.w3.org/2006/12/xml-c14n11"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>f+0WCqnPkInI+eL9G3LAry12fTPf+toC9UX07F4fI+s=</ds:DigestValue> </ds:Reference>
原本以为SignedXml会自动识别XPath表达式中的命名空间,但实际需要手动添加,请问如何操作?
解决方案
SignedXml不会自动将XML文档中的命名空间继承到XPath转换的上下文,必须显式为每个XPath转换提供包含所需前缀的命名空间解析器,步骤如下:
创建命名空间管理器并添加前缀映射
基于XML文档的NameTable创建XmlNamespaceManager,添加XPath中用到的所有前缀和对应的URI:XmlDocument doc = new XmlDocument(); doc.Load("你的XML文件路径"); var nsManager = new XmlNamespaceManager(doc.NameTable); nsManager.AddNamespace("ext", "urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"); nsManager.AddNamespace("cac", "urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"); nsManager.AddNamespace("cbc", "urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2");为XPath转换设置上下文
遍历SignedXml中所有Reference的转换集合,找到XmlDsigXPathTransform类型的实例,将其Context属性设置为刚才创建的命名空间管理器:SignedXml signedXml = new SignedXml(doc); // 此处省略设置签名密钥、Reference等初始化代码 foreach (Reference reference in signedXml.SignedInfo.References) { foreach (Transform transform in reference.Transforms) { if (transform is XmlDsigXPathTransform xpathTransform) { xpathTransform.Context = nsManager; } } }执行签名操作
确保在调用ComputeSignature()之前完成上述上下文设置,之后即可正常执行签名:signedXml.ComputeSignature();
内容的提问来源于stack exchange,提问作者links77
相关产品推荐
相关产品推荐

