使用xades4j库签署厄瓜多尔SRI合规XML发票遇结构错误求助
厄瓜多尔SRI XML发票XAdES-BES签名结构错误排查与修复
SRI合规要求回顾
- 签名标准:XAdES-BES
- Schema版本:1.3.2
- 编码:UTF-8
- 签名类型:Enveloped
代码问题分析与修复方案
1. 核心算法适配问题
SRI 1.3.2 Schema已弃用SHA-1算法,必须使用SHA-256作为摘要和签名算法,这是签名结构验证失败的核心原因之一。同时要修正DSA算法ID的错误配置。
2. 证书链配置缺失
fullChain(false)会导致签名中缺少完整证书链,SRI要求完整信任链来验证签名有效性,需改为fullChain(true)。
3. Enveloped签名的Transform顺序
需先执行XML规范化(Canonicalization),再应用Enveloped转换,确保签名计算的内容与SRI验证逻辑一致。
4. XML输出编码强制指定
Transformer默认编码可能不是UTF-8,需显式设置输出编码为UTF-8。
5. 签名引用与属性完善
明确DataObject的MIME类型,避免属性缺失导致结构验证失败。
修改后的完整代码
public static void main(String[] args) { Document doc = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); factory.setXIncludeAware(true); // 适配SRI Schema的XInclude要求 try { DocumentBuilder builder = factory.newDocumentBuilder(); doc = builder.parse(new File("filepath")); } catch (ParserConfigurationException e) { System.err.println("无法解析XML"); e.printStackTrace(); } catch (SAXException | IOException e) { e.printStackTrace(); } // 配置完整证书链的KeyStore KeyingDataProvider kp = FileSystemKeyStoreKeyingDataProvider .builder("pkcs12", "keystorepath", SigningCertificateSelector.single()) .storePassword(new DirectPasswordProvider(args[1])) .entryPassword(new DirectPasswordProvider(args[1])) .fullChain(true) .build(); // 配置SHA-256系列算法,适配SRI要求 XadesBesSigningProfile p = new XadesBesSigningProfile(kp) .withSignatureAlgorithms(new SignatureAlgorithms() .withDigestAlgorithmForDataObjectReferences(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256) .withDigestAlgorithmForReferenceProperties(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256) .withDigestAlgorithmForTimeStampProperties(MessageDigestAlgorithm.ALGO_ID_DIGEST_SHA256) .withSignatureAlgorithm("RSA", XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256) .withSignatureAlgorithm("EC", XMLSignature.ALGO_ID_SIGNATURE_ECDSA_SHA256) .withSignatureAlgorithm("DSA", XMLSignature.ALGO_ID_SIGNATURE_DSA_SHA256)) .withBasicSignatureOptions(new BasicSignatureOptions() .includePublicKey(true) .signKeyInfo(true)); // 配置Enveloped签名的引用与Transform顺序 DataObjectDesc obj = new DataObjectReference("") .withTransform(new CanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS)) .withTransform(new EnvelopedSignatureTransform()) .withDataObjectFormat(new DataObjectFormatProperty("text/xml") .withDescription("contenido comprobante") .withMimeType("text/xml")); SignedDataObjects dataObjs = new SignedDataObjects(obj); try { XadesSigner signer = p.newSigner(); Element elemToSign = doc.getDocumentElement(); signer.sign(dataObjs, elemToSign); // 强制UTF-8编码输出XML TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); // 可选,便于调试 DOMSource source = new DOMSource(elemToSign); StreamResult result = new StreamResult(new File("outputPathFile")); transformer.transform(source, result); } catch (XadesProfileResolutionException e) { System.err.println("无法创建签名器"); e.printStackTrace(); } catch (XAdES4jException | TransformerException e) { e.printStackTrace(); } }
额外排查步骤
- 逐节点对比参考XML:重点检查命名空间(XAdES/DSIG)、Signature节点位置、SignedInfo中的Reference属性、KeyInfo证书链是否与有效参考XML完全一致
- Schema验证:使用SRI提供的1.3.2 Schema对签名后的XML做结构验证,排查节点缺失或格式错误
- 证书有效性检查:确认使用的证书为SRI认可的机构颁发,未过期且私钥未损坏
- 时间戳配置:若SRI要求签名包含时间戳,需补充对应的时间戳服务配置
内容的提问来源于stack exchange,提问作者JazzDTap
相关产品推荐
相关产品推荐

