XAdES与XMLDsig签名验证问题排查及新异常咨询
XAdES与XMLDSig签名验证问题及解决方案
一、XAdES签名验证初始问题
验证代码
NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature"); FileSystemDirectoryCertStore certStore = new FileSystemDirectoryCertStore("cert/store/"); KeyStore ks; try (FileInputStream fis = new FileInputStream("cert/store/myStore")) { ks = KeyStore.getInstance("jks"); ks.load(fis, "password".toCharArray()); } CertificateValidationProvider provider = new PKIXCertificateValidationProvider( ks, false,certStore.getStore() ); XadesVerificationProfile profile = new XadesVerificationProfile(provider); org.w3c.dom.Element sigElem = (org.w3c.dom.Element) nl.item(0); XAdESVerificationResult r = profile.newVerifier().verify(sigElem, null);
异常信息
xades4j.verification.ReferenceValueException: Reference '#xmldsig-xxxx-abc-object11' cannot be validated
对应XML签名片段(模拟数据)
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="xmldsig-abc"> <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 Id="xmldsig-aee6fb07-8566-47eb-acb7-abc-ref0" Type="http://www.w3.org/2000/09/xmldsig#Object" URI="#xmldsig-xxx-abc-object11"> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>XXXX</ds:DigestValue> </ds:Reference> <ds:Reference Type="http://uri.etsi.org/01903#SignedProperties" URI="#xmldsig-xxx-abc-signedprops"> <ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> </ds:Transforms> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>XXXXX</ds:DigestValue> </ds:Reference> </ds:SignedInfo> <ds:SignatureValue Id="xmldsig-xxxx-sigvalue">XXXXX </ds:SignatureValue> <ds:KeyInfo> <ds:X509Data> <ds:X509Certificate>xXxxxxxx </ds:X509Certificate> <ds:X509IssuerSerial> <ds:X509IssuerName>xxxxx</ds:X509IssuerName> <ds:X509SerialNumber>xxxx</ds:X509SerialNumber> </ds:X509IssuerSerial> <ds:X509SubjectName>xxxxx </ds:X509SubjectName> </ds:X509Data> </ds:KeyInfo> <ds:Object Id="xmldsig-xxx-abc-object11"> <ns2:Vehicle xmlns:ns2="xx:xx" xmlns="xx:xx"> ................................. </ns2:Vehicle> </ds:Object> <ds:Object> <xades:QualifyingProperties xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xades141="http://uri.etsi.org/01903/v1.4.1#" Target="#xmldsig-xxx-abc"> <xades:SignedProperties Id="xmldsig-xxx-abc-signedprops"> <xades:SignedSignatureProperties> <xades:SigningTime>xxxxxxx</xades:SigningTime> <xades:SigningCertificate> <xades:Cert> <xades:CertDigest> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>xxxxxxxxxx</ds:DigestValue> </xades:CertDigest> <xades:IssuerSerial> <ds:X509IssuerName>xxxxxxxxxxx </ds:X509IssuerName> <ds:X509SerialNumber>xxxxxxxxxxx</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> <xades:Cert> <xades:CertDigest> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>xxxxxxxxx</ds:DigestValue> </xades:CertDigest> <xades:IssuerSerial> <ds:X509IssuerName>xxxxxxxxxx </ds:X509IssuerName> <ds:X509SerialNumber>xxxxxxxxxxxx</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> <xades:Cert> <xades:CertDigest> <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/> <ds:DigestValue>xxxxxxxxx</ds:DigestValue> </xades:CertDigest> <xades:IssuerSerial> <ds:X509IssuerName>xxxxxxxxxxxx </ds:X509IssuerName> <ds:X509SerialNumber>xxxxxxxxxxxx</ds:X509SerialNumber> </xades:IssuerSerial> </xades:Cert> </xades:SigningCertificate> </xades:SignedSignatureProperties> <xades:SignedDataObjectProperties> <xades:CommitmentTypeIndication> <xades:CommitmentTypeId> <xades:Identifier>http://uri.etsi.org/01903/v1.2.2#ProofOfOrigin</xades:Identifier> <xades:Description>xxxxxxxxxxxxx </xades:Description> </xades:CommitmentTypeId> <xades:AllSignedDataObjects/> </xades:CommitmentTypeIndication> </xades:SignedDataObjectProperties> </xades:SignedProperties> </xades:QualifyingProperties> </ds:Object> </ds:Signature>
疑问与解答
1. 验证方法是否正确?
整体框架是正确的:使用xades4j的XadesVerificationProfile结合PKIX证书验证提供者,符合XAdES规范的标准验证流程。但存在细节疏漏,比如证书链完整性(未使用根证书)和数据源可靠性(磁盘文件读取风险)。
2. 摘要值不匹配的原因?
- 数据源问题:磁盘读取的XML文件可能在存储或读取过程中被修改(如编码变更、额外空格/换行插入),导致待验证内容与签名时的原始内容不一致,摘要计算结果不匹配。
- 证书链不完整:未引入根证书,导致证书验证环节存在缺陷,间接影响签名验证流程的完整性。
二、XMLDSig验证新异常(XAdES问题已解决)
XAdES问题解决原因
- 原从磁盘读取XML文件存在篡改或读取异常,改为直接验证API返回的字符串后修复。
- 验证过程未使用根证书,添加根证书后完成XAdES验证。
新的XMLDSig验证代码
NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Signature"); DOMValidateContext valContext = new DOMValidateContext(new KeyValueKeySelector(), nl.item(0)); XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); XMLSignature signature = fac.unmarshalXMLSignature(valContext); signature.validate(valContext);
异常信息
om.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID xmldsig-xxx-abc-signedprops
解决方案
该异常是因为默认XMLDSig解析器无法识别XAdES命名空间下的Id属性,导致无法解析引用的#xmldsig-xxx-abc-signedprops元素,可通过以下方式解决:
1. 注册XAdES命名空间的Id属性为ID类型
在创建DOMValidateContext后,显式将XAdES命名空间的Id属性注册为ID:
// 获取文档DOM实现并注册XAdES的Id属性 DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance(); DOMImplementationLS domImpl = (DOMImplementationLS) registry.getDOMImplementation("LS"); // 注册XAdES命名空间下的Id属性为ID ((org.w3c.dom.Document) doc).getDocumentElement() .setIdAttributeNS("http://uri.etsi.org/01903/v1.3.2#", "Id", true);
2. 使用自定义ResourceResolver
实现自定义解析器,处理XAdES元素的ID查找:
valContext.setResourceResolver(new ResourceResolver() { @Override public ResourceResolverResult resolve(ResourceResolverContext context) throws ResourceResolverException { String uri = context.getUri(); if (uri.startsWith("#")) { String id = uri.substring(1); // 查找XAdES命名空间下的元素 NodeList xadesNodes = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "*"); for (int i = 0; i < xadesNodes.getLength(); i++) { Element elem = (Element) xadesNodes.item(i); if (id.equals(elem.getAttribute("Id"))) { return new ResourceResolverResult(elem, context.getBaseUri(), null, null); } } // 查找所有命名空间的元素 NodeList allNodes = doc.getElementsByTagName("*"); for (int i = 0; i < allNodes.getLength(); i++) { Element elem = (Element) allNodes.item(i); if (id.equals(elem.getAttribute("Id")) || id.equals(elem.getAttributeNS(null, "Id"))) { return new ResourceResolverResult(elem, context.getBaseUri(), null, null); } } } // 调用默认解析器处理其他情况 return XMLSecurity.getDefaultResourceResolver().resolve(context); } });
3. 确保XML解析时保留命名空间和ID属性
解析XML时开启命名空间支持:
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); dbf.setValidating(false); // 禁用DTD加载避免干扰 dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); DocumentBuilder db = dbf.newDocumentBuilder(); Document doc = db.parse(inputStream);
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

