最近同银行做接口联调,需要对XML文件做加签和解签操作,本地的开发环境是Mac 10.10,JDK的版本是1.6.0.65。小小的一段加签代码,一直报错,却久久也找不到解决方法,网上的资料非常少,错误记录如下:

 javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID FIQReq
Exception in thread "main" java.lang.RuntimeException: javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID FIQReq
at util.xml.XMLSigner.sign(XMLSigner.java:111)
at test.TestSign.main(TestSign.java:34)
Caused by: javax.xml.crypto.dsig.XMLSignatureException: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID FIQReq
at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:412)
at org.jcp.xml.dsig.internal.dom.DOMReference.digest(DOMReference.java:338)
at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.digestReference(DOMXMLSignature.java:471)
at org.jcp.xml.dsig.internal.dom.DOMXMLSignature.sign(DOMXMLSignature.java:367)
at util.xml.XMLSigner.sign(XMLSigner.java:108)
... 1 more
Caused by: javax.xml.crypto.URIReferenceException: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID FIQReq
at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:124)
at org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:404)
... 5 more
Caused by: com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID FIQReq
at com.sun.org.apache.xml.internal.security.utils.resolver.implementations.ResolverFragment.engineResolve(ResolverFragment.java:90)
at com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolver.resolve(ResourceResolver.java:283)
at org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:117)
... 6 more

唯一一个说的比较对的上的就是这篇Bug记录了:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8017171

产生该问题的原因就是无法根据XML文档节点的ID属性定位到对应的节点,所以在加签的时候会提示不能解析该ID,那么怎样才能让他可以根据我节点的ID,解析并定位到对应的节点呢,让我们先看下我项目中的代码:

首先是要进行加签的XML文件:

<?xml version="1.0" encoding="UTF-8"?>
<SoEv>
<Message id="54b8db6c24574e23423********">
<FIQReq id="FIQReq">
<version>1.0.1</version>
<instId>9550811********</instId>
<certId>0001</certId>
<channelDate>20150608</channelDate>
<beginPos>0</beginPos>
<pageNum>10</pageNum>
</FIQReq>
</Message>
</SoEv>

加签的代码:

 public void sign(Document doc, PrivateKey privateKey, String referenceId, Node signNode) {
if(!isInit){
throw new RuntimeException("XMLSigner is not init !");
}
try{
//创建 <Reference> 元素,引用指定ID的节点,<Signature> 元素不会被计算在内
Reference refToRootDoc = signFactory.newReference("#"+referenceId,
sha1DigMethod, Collections.singletonList(envelopedTransform), null, null);
//创建 <SignedInfo> 元素
SignedInfo signedInfo = signFactory.newSignedInfo(c14nWithCommentMethod,
rsa_sha1SigMethod, Collections.singletonList(refToRootDoc));
//创建签名实例
XMLSignature signature = signFactory.newXMLSignature(signedInfo, null);
//创建签名上下文,在指定节点生成
DOMSignContext dsc = new DOMSignContext(privateKey, signNode);
//设置签名域命名空间前缀
if (defaultNamespacePrefix != null) {
dsc.setDefaultNamespacePrefix(defaultNamespacePrefix); }
//签名
signature.sign(dsc);
}catch(Exception e){
System.out.println(e.getMessage());
throw new RuntimeException(e);
}
}

方法外传入需要做加签处理的XML文档树,私钥,需要加签节点的ID,这里就是上面XML中的FIQReq节点的ID内容(FIQReq),还有一个节点就是加签所生成的SignedInfo的父元素,也就是要把SignedInfo节点写在那个节点下面。

这样执行这段代码就会报如上所示的错误代码。

在上面介绍的http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8017171中,有这样三条解决建议:

There are 3 potential workarounds that you can apply:

1. Use a validating schema which will register the elements with ID references.

2. Register the ID elements with the DOMValidateContext.setIdAttributeNS method before validating the signature

3. Implement a custom URIDereferencer which can find these references and override the builtin URIDereferencer with the DOMValidateContext.setURIDereferencer method.

但是看起来还不是特别直观不能一下子解决该问题。但是给出了解决问题的方向,而我解决该问题的着眼点就在于第二条:

2. Register the ID elements with the DOMValidateContext.setIdAttributeNS method before validating the signature

上面介绍的加签代码虽然没有DOMValidateContext对象的参与,但是有DOMSignContext,他也有setIdAttributeNS方法,所以我们就是要在这个方法上做文章:

在上面的加签代码上加入如下两条代码即可解决该问题:

Element element = (Element)doc.getElementsByTagName(referenceId).item(0);
dsc.setIdAttributeNS(element, null, "id");

加入这两行代码后的整体代码变为:

/**
* XML签名
* <p>签名使用内嵌方式,生成在指定节点</p>
* @param doc XML文档
* @param privateKey 私钥
* @param referenceId 需签名的元素标识
* @param signNode 生成签名的节点
*/
public void sign(Document doc, PrivateKey privateKey, String referenceId, Node signNode) {
if(!isInit){
throw new RuntimeException("XMLSigner is not init !");
}
try{
//创建 <Reference> 元素,引用指定ID的节点,<Signature> 元素不会被计算在内
Reference refToRootDoc = signFactory.newReference("#"+referenceId,
sha1DigMethod, Collections.singletonList(envelopedTransform), null, null);
//创建 <SignedInfo> 元素
SignedInfo signedInfo = signFactory.newSignedInfo(c14nWithCommentMethod,
rsa_sha1SigMethod, Collections.singletonList(refToRootDoc));
//创建签名实例
XMLSignature signature = signFactory.newXMLSignature(signedInfo, null);
//创建签名上下文,在指定节点生成
DOMSignContext dsc = new DOMSignContext(privateKey, signNode);
//设置签名域命名空间前缀
if (defaultNamespacePrefix != null) {
dsc.setDefaultNamespacePrefix(defaultNamespacePrefix);
Element element = (Element)doc.getElementsByTagName(referenceId).item(0);
dsc.setIdAttributeNS(element, null, "id");
}
//签名
signature.sign(dsc);
}catch(Exception e){
System.out.println(e.getMessage());
throw new RuntimeException(e);
}
}

再次运行程序后,问题解决。

具体的加签操作指导,详见如下文档:

http://www.apihome.cn/api/java/XMLSignatureFactory.html(中文)

http://docs.oracle.com/javase/6/docs/technotes/guides/security/xmldsig/XMLDigitalSignature.html(英文,但是介绍的更详细,直接拷贝出来即可)

XML签名Cannot resolve element with ID XXXX 解决方案的更多相关文章

  1. [Fatal Error] :3:13: Open quote is expected for attribute "{1}" associated with an element type "id".

    用DOM解析XML时出现了如下错误: [Fatal Error] :3:13: Open quote is expected for attribute "{1}" associa ...

  2. 【Java密码学】XML签名

    http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html XML签名的结构和类型 基本上XML签名 ...

  3. XML签名

    英文:https://www.javacodegeeks.com/2013/10/xml-security-with-digital-signature-in-java.html 中文:http:// ...

  4. mybatis mapper xml文件配置resultmap时,id行和result行有什么区别?

    mybatis mapper xml文件配置resultmap时,id行和result行有什么区别? <resultMap id = "CashInvoiceMap" typ ...

  5. org.apache.shiro.session.UnknownSessionException: There is no session with id [xxxx]的解决方案

    org.apache.shiro.session.UnknownSessionException: There is no session with id [xxxx]的解决方案 背景描述 Sprin ...

  6. Error parsing XML: junk after document element这样的错误 - CSDN博客

    很多开发者可能在编写XML布局文件时提示Error parsing XML: junk after document element这样的错误,这里Android123提示大家一般合法的XML文件只有 ...

  7. 【RF库XML测试】Get Element

    Name:Get ElementSource:XML <test library>Arguments:[ source | xpath=. ]Returns an element in t ...

  8. xml DTD中的ELEMENT和ATTLIST

    是W3C的一个文档类型定义规则文件,是用来让浏览器根据你定义的DTD(文档类型定义)来解释页面代码的. doctype声明指出阅读程序应该用什么规则集来解释文档中的标记.在Web文档的情况下,“阅读程 ...

  9. javax.servlet.ServletException: Could not resolve view with name‘ XXXX’in servlet with name 'spring'的解决方案-----SKY

    出现的异常如下: javax.servlet.ServletException: Could not resolve view with name '{"msg":"成功 ...

随机推荐

  1. c# ms chart 控件使用方法

    第一个简单的chart: spline// Create new data series and set it's visualattributes       Series series = new ...

  2. 动态语言的灵活性是把双刃剑 -- 以 Python 语言为例

    本文有些零碎,总题来说,包括两个问题:(1)可变对象(最常见的是list dict)被意外修改的问题,(2)对参数(parameter)的检查问题.这两个问题,本质都是因为动态语言(动态类型语言)的特 ...

  3. 带你玩转JavaScript中的隐式强制类型转换

    正题开始前我想先抛出一个问题,==和===有什么区别?可能一般人会想,不就是后者除了比较值相等之外还会比较类型是否相等嘛,有什么好问的,谁不知道?!但是这样说还不够准确,两者的真正区别其实是==在比较 ...

  4. Alpha发布——视频博客

    1.视频链接 视频上传至优酷自频道,地址链接:https://v.youku.com/v_show/id_XMzg5MzQ4MzM2MA==.html?spm=a2h0k.11417342.sores ...

  5. 冲刺ing-7

    第七次Scrum冲刺 队员完成的任务 队员 完成任务 吴伟华 Leangoo的看板截图,燃尽图 蔺皓雯 编写博客 蔡晨旸 进行测试 曾茜 界面设计 鲁婧楠 界面前后端 杨池宇 界面前后端 项目的发布说 ...

  6. CentOS 7 安装 MySql 8

    1-安装 CentOS 7   2-安装 NETCORE SDK      SDK 安装文档:https://dotnet.microsoft.com/download/linux-package-m ...

  7. Alpha-7

    前言 失心疯病源7 团队代码管理github 站立会议 队名:PMS 530雨勤(组长) 今天完成了那些任务 18:30~20:30 通过统计法来得出人车团块的区别和鉴别方法,然而效果并不显著 代码签 ...

  8. Ubuntu中Google Chrome安装

    转载自博客 1. 方法一   1.在ubuntu中启动终端   2.在终端中,输入以下命令: sudo wget http://www.linuxidc.com/files/repo/google-c ...

  9. 【第二周】Java实现英语文章词频统计

    1.需求:对于给定的英文文章进行单词频率的统计 2.分析: (1)建立一个如下图所示的数据库表word_frequency用来存放单词和其对应数量 (2)Scanner输入要查询的英文文章存入Stri ...

  10. crontab部署定时任务

    1.安装cron工具:apt-getinstall cron 2.开启定时任务:crontab –e 定时任务语句格式为:执行周期+命令. 周期有5个域,分别是分,时,日(day of month), ...