将org.dom4j.Document转换为org.w3c.dom.Document和XML签名的问题 [英] Problem with conversion of org.dom4j.Document to org.w3c.dom.Document and XML Signature

查看:1200
本文介绍了将org.dom4j.Document转换为org.w3c.dom.Document和XML签名的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些类已经使用 DOM4J 读取XML文件并提供
getter方法数据。现在,我需要添加检查XML数字
签名的可能性。



使用org.w3c.dom和以下 http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/
一切正常。



所以,我尝试使用DOMWriter将org.dom4j.Document转换为
org.w3c.dom.Document,但此后签名验证不起作用。我认为它是
发生,因为DOMWiter正在更改XML树(如doc4.asXML()似乎显示)。



我尝试找到一些设置为了维护文档的完整性,但
DOMWriter没有这样的方法。



下面是演示非对称转换的代码。



用于测试的文件是 http://www.robertodiasduarte.com.br/files/nfe/131090007910044_v1.10-procNFe.xml



有人知道原因/解决方法吗?



谢谢(对不起我可怜的英语)。

 code> package testevalidanfe; 

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import javax.swing.JOptionPane;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class Testevalidanfe {

public static void main(String [] args)throws Exception {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
文档d = db.parse(exemplo-nfe.xml);

Node no = d.getElementsByTagNameNS(XMLSignature.XMLNS,Signature)。item(0);

DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(),no);
XMLSignatureFactory fac = XMLSignatureFactory.getInstance(DOM);
XMLSignature signature = fac.unmarshalXMLSignature(valContext);

JOptionPane.showMessageDialog(null,使用org.w3c.dom验证:+ signature.validate(valContext));
org.dom4j.io.DOMReader domreader = new org.dom4j.io.DOMReader();
org.dom4j.Document doc4 = domreader.read(d);
org.dom4j.io.DOMWriter domwriter = new org.dom4j.io.DOMWriter();
d = domwriter.write(doc4);

String after = doc4.asXML();

PrintWriter writer = new PrintWriter(new File(after-convertion.xml));
writer.print(after);
writer.close();

no = d.getElementsByTagNameNS(XMLSignature.XMLNS,签名)。item(0);

valContext = new DOMValidateContext(new X509KeySelector(),no);
fac = XMLSignatureFactory.getInstance(DOM);
signature = fac.unmarshalXMLSignature(valContext);

JOptionPane.showMessageDialog(null,转换后验证:+ signature.validate(valContext));
}
}

包testevalidanfe;

import java.security.Key;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;

public class X509KeySelector extends KeySelector {
public KeySelectorResult select(KeyInfo keyInfo,
KeySelector.Purpose purpose,
AlgorithmMethod method,
XMLCryptoContext context)
throws KeySelectorException {
Iterator ki = keyInfo.getContent()。iterator();
while(ki.hasNext()){
XMLStructure info =(XMLStructure)ki.next();
if(!(info instanceof X509Data))
continue;
X509Data x509Data =(X509Data)info;
迭代器xi = x509Data.getContent()。iterator();
while(xi.hasNext()){
Object o = xi.next();
if(!(o instanceof X509Certificate))
continue;
final PublicKey key =((X509Certificate)o).getPublicKey();
if(algEquals(method.getAlgorithm(),key.getAlgorithm())){
return new KeySelectorResult(){
public Key getKey(){return key; }
};
}
}
}
抛出新的KeySelectorException(No key found!);
}

static boolean algEquals(String algURI,String algName){
if((algName.equalsIgnoreCase(DSA)&&
algURI.equalsIgnoreCase (SignatureMethod.DSA_SHA1))||
(algName.equalsIgnoreCase(RSA)&&$&
algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1))){
return true;
} else {
return false;
}
}
}

例如,如果原始XML始于:

 < nfeProc versao =1.10xmlns =http://www.portalfiscal.inf.br / NFE> 
< NFe xmlns =http://www.portalfiscal.inf.br/nfe>
< infNFe Id =NFe31090807301671000131550010001000216008030809versao =1.10xmlns:xsi =http://www.w3.org/2001/XMLSchema-instance>
...

doc4.asXML()返回:

 < nfeProc xmlns =http://www.portalfiscal.inf.br/nfeversao =1.10> 
< NFe>
< infNFe xmlns:xsi =http://www.w3.org/2001/XMLSchema-instanceId =NFe31090807301671000131550010001000216008030809versao =1.10>
...


解决方案

看看这个,事实证明,DOM4J DOMWriter正在做一些奇怪的wrt命名空间明显混淆了规范化过程。我没有指出确切的原因,但我认为它与DOMWriter在DOM元素中插入额外的xmlns属性有关。如果打开XML数字签名API的日志记录(如所引用的文章所述),则可以看到效果,规范化的< SignedInfo>元素在DOM4J生成的DOM文档中缺少命名空间声明。



但是,不用使用DOMWriter,您可以使用DOM4J DocumentSource和DOMResult转换生成DOM文档。

  / ** 
*从DOM4J文档创建一个DOM文档
* /
static Document copy(org.dom4j.Document orig){
尝试{
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
DOMResult result = new DOMResult();
t.transform(new DocumentSource(orig),result);
return(Document)result.getNode();
} catch(Exception e){
throw new RuntimeException(e);
}
}

使用生成的DOM文档,验证工作。 / p>

I have some classes that already use DOM4J to read XML files and provide getter methods to the data. Now, I need to add the possibility of checking XML digital signatures.

Using org.w3c.dom and following http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/ everything works correctly.

So, I try to use DOMWriter to convert from org.dom4j.Document to org.w3c.dom.Document, but after this the signature validation doesn't work. I think it happens because DOMWiter is changing the XML tree (as doc4.asXML() seems to show).

I try to find something to set in order to mantain the integrity of the document, but DOMWriter don't have such methods.

Below is the code demonstrating the asymmetric conversion.

The file used for tests is http://www.robertodiasduarte.com.br/files/nfe/131090007910044_v1.10-procNFe.xml

Does someone know reasons/workarounds to this?

Thanks (and sorry my poor english).

package testevalidanfe;

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import javax.swing.JOptionPane;
import javax.xml.crypto.dsig.XMLSignature;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.dom.DOMValidateContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.dom4j.io.XMLWriter;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class Testevalidanfe {

    public static void main(String[] args) throws Exception {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document d = db.parse("exemplo-nfe.xml");

        Node no = d.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0);

        DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(), no);
        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
        XMLSignature signature = fac.unmarshalXMLSignature(valContext);

        JOptionPane.showMessageDialog(null, "Validation using org.w3c.dom: " + signature.validate(valContext));
        org.dom4j.io.DOMReader domreader = new org.dom4j.io.DOMReader();
        org.dom4j.Document doc4 = domreader.read(d);
        org.dom4j.io.DOMWriter domwriter = new org.dom4j.io.DOMWriter();
        d = domwriter.write(doc4);

        String after = doc4.asXML();

        PrintWriter writer = new PrintWriter(new File("after-convertion.xml"));
        writer.print(after);
        writer.close();

        no = d.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature").item(0);

        valContext = new DOMValidateContext(new X509KeySelector(), no);
        fac = XMLSignatureFactory.getInstance("DOM");
        signature = fac.unmarshalXMLSignature(valContext);

        JOptionPane.showMessageDialog(null, "Validation after convert: " + signature.validate(valContext));
    }
}

package testevalidanfe;

import java.security.Key;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import javax.xml.crypto.AlgorithmMethod;
import javax.xml.crypto.KeySelector;
import javax.xml.crypto.KeySelectorException;
import javax.xml.crypto.KeySelectorResult;
import javax.xml.crypto.XMLCryptoContext;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.SignatureMethod;
import javax.xml.crypto.dsig.keyinfo.KeyInfo;
import javax.xml.crypto.dsig.keyinfo.X509Data;

public class X509KeySelector extends KeySelector {
    public KeySelectorResult select(KeyInfo keyInfo,
                                KeySelector.Purpose purpose,
                                AlgorithmMethod method,
                                XMLCryptoContext context)
    throws KeySelectorException {
        Iterator ki = keyInfo.getContent().iterator();
        while (ki.hasNext()) {
            XMLStructure info = (XMLStructure) ki.next();
            if (!(info instanceof X509Data))
                continue;
            X509Data x509Data = (X509Data) info;
            Iterator xi = x509Data.getContent().iterator();
            while (xi.hasNext()) {
                Object o = xi.next();
                if (!(o instanceof X509Certificate))
                    continue;
                final PublicKey key = ((X509Certificate)o).getPublicKey();
                if (algEquals(method.getAlgorithm(), key.getAlgorithm())) {
                    return new KeySelectorResult() {
                        public Key getKey() { return key; }
                    };
                }
           }
       }
       throw new KeySelectorException("No key found!");
    }

    static boolean algEquals(String algURI, String algName) {
        if ((algName.equalsIgnoreCase("DSA") &&
            algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) ||
            (algName.equalsIgnoreCase("RSA") &&
            algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1))) {
            return true;
        } else {
            return false;
        }
    }
}

For example, if the original XML starts with:

<nfeProc versao="1.10" xmlns="http://www.portalfiscal.inf.br/nfe">
<NFe xmlns="http://www.portalfiscal.inf.br/nfe">
<infNFe Id="NFe31090807301671000131550010001000216008030809" versao="1.10" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...

doc4.asXML() return this:

<nfeProc xmlns="http://www.portalfiscal.inf.br/nfe" versao="1.10">
<NFe>
<infNFe xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Id="NFe31090807301671000131550010001000216008030809" versao="1.10">
...

解决方案

I had a closer look at this, and it turns out that DOM4J DOMWriter is doing something odd w.r.t. namespaces that obviously confuses the canonicalization process. I haven't pin pointed the exact reason, but I think it has to do with DOMWriter inserting extra xmlns attributes in the DOM elements. You can see the effect if you turn on logging for the XML digital signature API (as described in the article you refer to), the canonicalized <SignedInfo> element lacks namespace declaration in the DOM document produced by DOM4J.

However, instead of using DOMWriter, you can produce a DOM document by transformation, using a DOM4J DocumentSource and a DOMResult.

/**
 * Create a DOM document from a DOM4J document 
 */
static Document copy(org.dom4j.Document orig) {
    try {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        DOMResult result = new DOMResult();
        t.transform(new DocumentSource(orig), result);
        return (Document) result.getNode();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Using the resulting DOM document, the validation works.

这篇关于将org.dom4j.Document转换为org.w3c.dom.Document和XML签名的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆