You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

信封式XML签名签名与验签原始数据不一致的解决咨询

信封式XML签名与验签原始数据不一致问题解决方案

问题根源

现有代码将原始XML文本解析为DOM Document对象时,DOM解析过程会自动调整XML格式(如去除多余空白、调整属性顺序、补全命名空间声明等),导致签名的是解析后的DOM结构,而非原始输入的XML文本。验签时若使用原始文本或不同解析逻辑处理,就会出现数据不一致。

调整方案

方案1:保留原始格式的DOM解析与规范化

若需继续使用DOM方式处理,需配置解析器严格保留原始文本的关键格式,同时修正规范化方法:

  1. 配置DocumentBuilderFactory,避免解析修改原始内容
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
// 保留原始空白字符
docBuilderFactory.setIgnoringElementContentWhitespace(false);
docBuilderFactory.setPreserveWhitespace(true);
// 禁用实体扩展,防止修改原始内容
docBuilderFactory.setExpandEntityReferences(false);
  1. 使用标准规范化方法地址
// 替换本地地址为官方标准带注释的C14N规范
CanonicalizationMethod c14nWithCommentMethod =
        fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, null);
  1. 确认签名引用的正确性
    检查refToRootDoc的URI和变换规则(Transform),确保仅执行必要的规范化步骤,避免额外修改目标内容。

方案2:直接基于原始字节流签名(贴合“原始文本操作”需求)

若要完全基于原始输入的XML字节签名,避免DOM解析的格式变化,可调整为直接对原始数据做摘要和签名,再嵌入信封式结构:

// 1. 直接对原始XML字节计算SM3摘要
byte[] originalData = data;
MessageDigest sm3Digest = MessageDigest.getInstance("SM3");
byte[] digestValue = sm3Digest.digest(originalData);

// 2. 创建XMLObject封装原始XML内容
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
docBuilderFactory.setPreserveWhitespace(true);
Document originalDoc = docBuilderFactory.newDocumentBuilder().parse(new ByteArrayInputStream(originalData));
XMLStructure content = new DOMStructure(originalDoc.getDocumentElement());
XMLObject obj = fac.newXMLObject(Collections.singletonList(content), "object", null, null);

// 3. 配置SignedInfo,确保引用原始内容
Transform transform = fac.newTransform(Transform.ENVELOPED, null);
Reference ref = fac.newReference(
        "",
        fac.newDigestMethod(DOMDigestMethod.SM3, null),
        Collections.singletonList(transform),
        null,
        null
);

CanonicalizationMethod c14nMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, null);
SignatureMethod sm2Sm3Method = fac.newSignatureMethod(DOMSignatureMethod.SM2_SM3, null);
SignedInfo signedInfo = fac.newSignedInfo(c14nMethod, sm2Sm3Method, Collections.singletonList(ref));

// 4. 构建KeyInfo与XMLSignature
KeyInfoFactory keyInfoFac = fac.getKeyInfoFactory();
KeyValue keyValue = keyInfoFac.newKeyValue(publicKey);
KeyInfo keyInfo = keyInfoFac.newKeyInfo(Collections.singletonList(keyValue));

XMLSignature signature = fac.newXMLSignature(signedInfo, keyInfo, Collections.singletonList(obj), null, null);

// 5. 执行签名并输出原始格式的XML
DOMSignContext dsc = new DOMSignContext(privateKey, originalDoc.getDocumentElement());
signature.sign(dsc);

// 输出时避免添加额外格式
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "no");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
transformer.transform(new DOMSource(originalDoc), new StreamResult(bos));
byte[] signedXml = bos.toByteArray();

关键注意事项

  • XML声明一致性:签名与验签阶段需统一保留或忽略XML声明,否则会导致字节差异。
  • 编码统一:全程使用相同字符编码(如UTF-8)处理文本,避免编码转换导致的内容变化。
  • 规范化方法匹配:签名与验签必须使用完全相同的C14N算法(包括是否带注释、独占/包含模式)。
  • 空白处理:若原始XML包含有意义的空白字符,需确保解析和输出过程不修改这些空白。

内容的提问来源于stack exchange,提问作者user22897558

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 20:43:12