2011-07-29 10 views
7

XML 파일을 읽고 게터 메소드를 데이터에 제공하기 위해 이미 DOM4J을 사용하는 클래스가 있습니다. 이제 XML 디지털 서명을 검사 할 가능성을 추가해야합니다.org.dom4j.Document를 org.w3c.dom.Document 및 XML 서명으로 변환 할 때의 문제점

org.w3c.dom을 사용하여 다음을 입력하십시오. http://java.sun.com/developer/technicalArticles/xml/dig_signature_api/ 모든 것이 올바르게 작동합니다.

그래서 DOMWriter를 사용하여 org.dom4j.Document를 org.w3c.dom.Document로 변환하려고 시도하지만이 후에는 서명 유효성 검사가 작동하지 않습니다. do은 DOMWiter가 (doc4.asXML()이 보이는 것처럼) XML 트리를 변경하기 때문에 발생한다고 생각합니다.

DOMWriter에는 문서의 무결성을 유지하기 위해 설정할 항목이 있지만 DOMWriter에는 이러한 메서드가 없습니다.

다음은 비대칭 변환을 보여주는 코드입니다.

테스트에 사용 된 파일은 http://www.robertodiasduarte.com.br/files/nfe/131090007910044_v1.10-procNFe.xml

입니다

사람이하는 이유/해결 방법을 알고 있나요?

감사합니다. (영어로는 불쌍합니다.)

<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()이 반환 :

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; 
     } 
    } 
} 

예를 들어, 원래의 XML로 시작하는 경우

<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"> 
... 
+1

+1 잘 쓰여진 질문 – andyb

+0

좋은 질문입니다. DOMWriter의 출력이 XML 트리를 어떻게 변경합니까? 다른 무엇입니까? –

+0

감사! 편집 된 질문보기 –

답변

1
나는이 좀 더 자세히 살펴했다

을, 그리고 회전 DOM4J DOMWriter가 이상한 일을하고있다. 분명히 정규화 과정을 혼란스럽게하는 네임 스페이스. 정확한 이유를 지적하지 않았지만 DOM 요소에 추가 xmlns 특성을 삽입하는 DOMWriter와 관련이 있다고 생각합니다. XML 전자 서명 API에 대한 로깅을 켜면 (참조 기사에서 설명한대로) 정규화 된 < SignedInfo> 요소에는 DOM4J에서 생성 된 DOM 문서의 네임 스페이스 선언이 없습니다.

그러나 DOMWriter를 사용하는 대신 DOM4J DocumentSource 및 DOMResult를 사용하여 변환하여 DOM 문서를 생성 할 수 있습니다.

결과 DOM 문서를 사용하여 유효성 검사가 작동합니다.

+0

작품, 고마워요! dom4j 소스 (마지막 stable 버전, 1.6.1)를 (조금) 들여다 보지만 일어나는 일을 파악할 수는 없습니다. –

+0

+1 좋은 조사와 대답 – andyb

+0

그것은 나를 위해 작동하지 않습니다. W3C 파서에만 의존 할 수 있지만 솔루션을 찾고 있습니다. – kbec