有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

在java中为特定xml标记签名时发生xml dsig错误

我需要用Java1.6签署一个特定的XML标记。我的XML是这样的:

<RecepcionarLoteRps>
<EnviarLoteRpsEnvio xmlns="http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd">
    <LoteRps>
        <ListaRps>
            <Rps>           
                <InfRps id="1">
                    . . .
                </InfRps>                       
            </Rps>                  
        </ListaRps>         
</EnviarLoteRpsEnvio>

我签署它的java代码(不包括密钥库部分)是:

    try {
    String ArqAssinar = "file.xml";
    String Charset = "UTF-8";

    /* URI and ID */
    String idRef = "1";
    String uriRef = "#" + idRef;

    XMLSignatureFactory XmlSignFac = XMLSignatureFactory.getInstance("DOM");

    DigestMethod DigMet = XmlSignFac.newDigestMethod(DigestMethod.SHA1, null);

    Transform TransfRef1 = XmlSignFac.newTransform(CanonicalizationMethod.INCLUSIVE, (TransformParameterSpec) null);
    Transform TransfRef2 = XmlSignFac.newTransform(SignatureMethod.RSA_SHA1, (C14NMethodParameterSpec) null);
    List<Transform> Transfs = new ArrayList<Transform>();
    Transfs.add(TransfRef1);
    Transfs.add(TransfRef2);

    /* Reference - where I use URI and ID */
    Reference Ref = XmlSignFac.newReference(uriRef, DigMet, Transfs, null, idRef);

    CanonicalizationMethod CannMet = XmlSignFac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
    SignatureMethod SignMet = XmlSignFac.newSignatureMethod(SignatureMethod.RSA_SHA1, null);

    /* SignedInfo - where I use Reference */
    SignedInfo SignInfo = XmlSignFac.newSignedInfo(CannMet, SignMet, Collections.singletonList(Ref));
    KeyInfoFactory keyInfFac = XmlSignFac.getKeyInfoFactory();

    List<X509Certificate> X509Content = new ArrayList<X509Certificate>();
    X509Content.add(Certif);

    X509Data X509dados = keyInfFac.newX509Data(X509Content);

    KeyInfo KeyInf = keyInfFac.newKeyInfo(Collections.singletonList(X509dados));

    /* Process file for input */
    DocumentBuilderFactory DocBuilderFactory = DocumentBuilderFactory.newInstance();
    DocBuilderFactory.setNamespaceAware(true);
    DocumentBuilder DocBuilder = DocBuilderFactory.newDocumentBuilder();
    InputStream Input = new FileInputStream(arq);
    Reader Leitor = new InputStreamReader(Input, Charset);
    InputSource Origem = new InputSource(Leitor);

    Document Doc = DocBuilder.parse(Origem);

    /* Search for tag in document using ID */
    XPathFactory factory = XPathFactory.newInstance();
    XPath xpathPesquisa = factory.newXPath();
    XPathExpression expr = xpathPesquisa.compile(String.format("//*[@id='%s']", idRef));
    NodeList nodes = (NodeList) expr.evaluate(docParaAssinar, XPathConstants.NODESET);
    Node Tag = null;
    DOMSignContext DocSignCont = null;
    XMLSignature Signature = null;

    if (nodes.getLength() != 0) {
        tagComId = nodes.item(0);
        Tag = tagComId.getParentNode();

        DocSignCont = new DOMSignContext(PrivPass, Tag);

        /* Do the signature */
        Signature = this.XmlSignFac.newXMLSignature(SignInfo, KeyInf);
        Signature.sign(DocSignCont);        

        /* Updates the file */      
        OutputStream Saida = new FileOutputStream(arqAtualizar);
        Writer Escritor = new OutputStreamWriter(Saida, Charset);
        StreamResult Resultado = new StreamResult(Escritor);
        TransformerFactory TransformFac = TransformerFactory.newInstance();
        Transformer Transf = TransformFac.newTransformer();
        Transf.transform(new DOMSource(docAssinado), Resultado);
    } else {
        . . .
    }
} catch (Exception E) {
    . . .
}

当我在Eclipse中运行这段代码时,我在DOMXMLSignature中得到了一个StackOverflow错误。sign()方法。有人打电话要签名。digestReference()方法,该函数将无限期地调用自身

如果URI为“”,ID为null,也就是说,当我需要对整个XML签名时,这段代码就可以工作

要获得特定的XML标记,我需要做哪些不同的操作

我对这段代码有一些问题:

public final class DOMXMLSignature extends DOMStructure
implements XMLSignature {


@Override
public void sign(XMLSignContext signContext)
    throws MarshalException, XMLSignatureException
{
    if (signContext == null) {
        throw new NullPointerException("signContext cannot be null");
    }
    DOMSignContext context = (DOMSignContext)signContext;
    marshal(context.getParent(), context.getNextSibling(),
            DOMUtils.getSignaturePrefix(context), context);

    // generate references and signature value
    List<Reference> allReferences = new ArrayList<>();

    // traverse the Signature and register all objects with IDs that
    // may contain References
    signatureIdMap = new HashMap<>();
    signatureIdMap.put(id, this);
    signatureIdMap.put(si.getId(), si);
    @SuppressWarnings("unchecked")
    List<Reference> refs = si.getReferences();
    for (Reference ref : refs) {
        signatureIdMap.put(ref.getId(), ref);
    }
    for (XMLObject obj : objects) {
        signatureIdMap.put(obj.getId(), obj);
        @SuppressWarnings("unchecked")
        List<XMLStructure> content = obj.getContent();
        for (XMLStructure xs : content) {
            if (xs instanceof Manifest) {
                Manifest man = (Manifest)xs;
                signatureIdMap.put(man.getId(), man);
                @SuppressWarnings("unchecked")
                List<Reference> manRefs = man.getReferences();
                for (Reference ref : manRefs) {
                    allReferences.add(ref);
                    signatureIdMap.put(ref.getId(), ref);
                }
            }
        }
    }
    // always add SignedInfo references after Manifest references so
    // that Manifest reference are digested first
    allReferences.addAll(refs);

    // generate/digest each reference
    for (Reference ref : allReferences) {
        digestReference((DOMReference)ref, signContext);
    }

    // do final sweep to digest any references that were skipped or missed
    for (Reference ref : allReferences) {
        if (((DOMReference)ref).isDigested()) {
            continue;
        }
        ((DOMReference)ref).digest(signContext);
    }

    Key signingKey = null;
    try {
        KeySelectorResult keySelectorResult = signContext.getKeySelector().select(ki,
                                                  KeySelector.Purpose.SIGN,
                                                  si.getSignatureMethod(),
                                                  signContext);
        signingKey = keySelectorResult.getKey();
        if (signingKey == null) {
            throw new XMLSignatureException("the keySelector did not " +
                                            "find a signing key");
        }
        ksr = keySelectorResult;
    } catch (KeySelectorException kse) {
        throw new XMLSignatureException("cannot find signing key", kse);
    }

    // calculate signature value
    try {
        byte[] val = ((AbstractDOMSignatureMethod)
            si.getSignatureMethod()).sign(signingKey, si, signContext);
        ((DOMSignatureValue)sv).setValue(val);
    } catch (InvalidKeyException ike) {
        throw new XMLSignatureException(ike);
    }

    this.localSigElem = sigElem;
}

private void digestReference(DOMReference ref, XMLSignContext signContext)
    throws XMLSignatureException
{
    if (ref.isDigested()) {
        return;
    }
    // check dependencies
    String uri = ref.getURI();
    if (Utils.sameDocumentURI(uri)) {
        String parsedId = Utils.parseIdFromSameDocumentURI(uri);
        if (parsedId != null && signatureIdMap.containsKey(parsedId)) {
            XMLStructure xs = signatureIdMap.get(parsedId);
            if (xs instanceof DOMReference) {
                digestReference((DOMReference)xs, signContext);
            } else if (xs instanceof Manifest) {
                Manifest man = (Manifest)xs;
                List<Reference> manRefs = DOMManifest.getManifestReferences(man);
                for (int i = 0, size = manRefs.size(); i < size; i++) {
                    digestReference((DOMReference)manRefs.get(i),
                                    signContext);
                }
            }
        }
        // if uri="" and there are XPath Transforms, there may be
        // reference dependencies in the XPath Transform - so be on
        // the safe side, and skip and do at end in the final sweep
        if (uri.length() == 0) {
            List<Transform> transforms = ref.getTransforms();
            for (Transform transform : transforms) {
                String transformAlg = transform.getAlgorithm();
                if (transformAlg.equals(Transform.XPATH) ||
                    transformAlg.equals(Transform.XPATH2)) {
                    return;
                }
            }
        }
    }
    ref.digest(signContext);
}

}

public final class Utils {

/**
 * Returns the ID from a same-document URI (ex: "#id")
 */
public static String parseIdFromSameDocumentURI(String uri) {
    if (uri.length() == 0) {
        return null;
    }
    String id = uri.substring(1);
    if (id != null && id.startsWith("xpointer(id(")) {
        int i1 = id.indexOf('\'');
        int i2 = id.indexOf('\'', i1+1);
        id = id.substring(i1+1, i2);
    }
    return id;
}

/**
 * Returns true if uri is a same-document URI, false otherwise.
 */
public static boolean sameDocumentURI(String uri) {
    return uri != null && (uri.length() == 0 || uri.charAt(0) == '#');
}

}

这只是DOMXMLSignature类和Util类的一部分,只是与案例有关的方法

我的代码高于类DOMXMLSignature。sign()方法。这个方法执行marshall,获取引用,获取引用id,调用DOMXMLSignature。digestReference()方法

签名。digestReference()方法验证引用是否已完全消化,而不是。因此,他获取URI,验证这是否是同一个文档URI——它是-,确认文档中的id就是URI中的id。然后,问题是:使用signatureIdMap获得的实例。get(parsedId)总是DOMReference类型,因此该方法无限期地调用自己

这是在Java1.6中发生的,在本机类中没有改变。如何解决这个问题并签署特定的XML标记?在调用DOMXMLSignature之前,我必须计算digestvalue。sign()方法


共 (1) 个答案

  1. # 1 楼答案

    首先,您的代码不是一个可复制的示例:

    • tagComId docParaAssinar docAssinado未声明,后两个从未设置。我为第一个添加了类型Node,并将后两个替换为Doc。仅供参考:对本地人的名字(也包括字段或方法)使用首字母大写是违反传统风格的,因此感觉很奇怪,但确实有效,所以我没有费心修改它们

    • SignatureMethod.RSA_SHA1不是有效的Transform,试图将其用作一个抛出异常的对象。在引用的Transforms列表中也没有意义,因为签名不是单独应用于一个引用,而是应用于SignedInfo,其中可能包含多个引用和其他数据。我把它拿走了

    • 此外,为了更方便地进行测试,我用屏蔽输入和控制台输出替换了文件I/O(看起来还行)

    通过这些更改,我确实复制了stackoverflow。这是因为您指定了#1作为URI,1作为引用的id,所以要解析引用,它必须转到被引用的元素并解析它,而被引用的元素本身就是(相同的)引用,所以它必须转到已标识的被引用元素并解析它,所以它必须等等。你不能使引用的id与你试图引用的元素相同,不应该使它与任何其他元素相同,而且你不需要引用本身,所以最好不要为它设置任何id。我将newReference中的id更改为null

    有了这些变化,我确实可以签署“”(整个文件),但不能签署“#1”。我发现之所以是因为#x没有引用(一个元素)名为的属性“id”,而是引用(一个元素)模式中定义的属性,或者DTD作为id,而你的^{没有定义为id。参见
    No working ID attribute when parsing XML in java
    Java XML DOM: how are id Attributes special?
    Xml id attribute to work with Java's getElementById?

    您引用的模式仅将该属性定义为字符串而不是id,并且由于默认情况下DOM解析器不使用模式,因此不会使用该属性。我没有使用模式编辑,而是写了一个(非常!)使该属性成为id的最小DTD,下面的代码对我来说可以为所需的元素签名(使用我创建的伪密钥和证书):

        static void SO69652045XMLSign (String[] args) throws Exception {
            //++ debug data
            String input = "<!DOCTYPE RecepcionarLoteRps [ <!ATTLIST InfRps id ID #REQUIRED> ] >"+ 
                    "<RecepcionarLoteRps><EnviarLoteRpsEnvio xmlns=\"http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd\">"+
                    "<LoteRps><ListaRps><Rps><InfRps id=\"1\">xxx</InfRps></Rps></ListaRps> </LoteRps> </EnviarLoteRpsEnvio></RecepcionarLoteRps>";
            KeyStore keystore = KeyStore.getInstance("PKCS12");
            try(InputStream is = new FileInputStream("69652045.p12")){ keystore.load(is,"sekrit".toCharArray()); }
            PrivateKey PrivPass = (PrivateKey) keystore.getKey("mykey","sekrit".toCharArray());
            X509Certificate Certif = (X509Certificate) keystore.getCertificateChain("mykey")[0];
            // String ArqAssinar = "file.xml";
            // String Charset = "UTF-8";
    
            /* URI and ID */
            String idRef = "1";
            String uriRef = "#" + idRef;
    
            XMLSignatureFactory XmlSignFac = XMLSignatureFactory.getInstance("DOM");
    
            DigestMethod DigMet = XmlSignFac.newDigestMethod(DigestMethod.SHA1, null);
    
            Transform TransfRef1 = XmlSignFac.newTransform(CanonicalizationMethod.INCLUSIVE, (TransformParameterSpec) null);
            // Transform TransfRef2 = XmlSignFac.newTransform(SignatureMethod.RSA_SHA1, (C14NMethodParameterSpec) null);
            List<Transform> Transfs = new ArrayList<Transform>();
            Transfs.add(TransfRef1);
            // Transfs.add(TransfRef2);
    
            /* Reference - where I use URI and ID */
            Reference Ref = XmlSignFac.newReference(uriRef, DigMet, Transfs, null, /*idRef*/null);
    
            CanonicalizationMethod CannMet = XmlSignFac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec) null);
            SignatureMethod SignMet = XmlSignFac.newSignatureMethod(SignatureMethod.RSA_SHA1, null);
    
            /* SignedInfo - where I use Reference */
            SignedInfo SignInfo = XmlSignFac.newSignedInfo(CannMet, SignMet, Collections.singletonList(Ref));
            KeyInfoFactory keyInfFac = XmlSignFac.getKeyInfoFactory();
    
            List<X509Certificate> X509Content = new ArrayList<X509Certificate>();
            X509Content.add(Certif);
    
            X509Data X509dados = keyInfFac.newX509Data(X509Content);
    
            KeyInfo KeyInf = keyInfFac.newKeyInfo(Collections.singletonList(X509dados));
    
            /* Process file for input */
            DocumentBuilderFactory DocBuilderFactory = DocumentBuilderFactory.newInstance();
            DocBuilderFactory.setNamespaceAware(true);
            DocumentBuilder DocBuilder = DocBuilderFactory.newDocumentBuilder();
            // InputStream Input = new FileInputStream(arq);
            // Reader Leitor = new InputStreamReader(Input, Charset);
            // InputSource Origem = new InputSource(Leitor);
            InputSource Origem = new InputSource (new StringReader (input)); //++
            Document Doc = DocBuilder.parse(Origem);
    
            /* Search for tag in document using ID */
            XPathFactory factory = XPathFactory.newInstance();
            XPath xpathPesquisa = factory.newXPath();
            XPathExpression expr = xpathPesquisa.compile(String.format("//*[@id='%s']", idRef));
            NodeList nodes = (NodeList) expr.evaluate(/*docParaAssinar*/Doc, XPathConstants.NODESET);
            Node Tag = null;
            DOMSignContext DocSignCont = null;
            XMLSignature Signature = null;
    
            if (nodes.getLength() != 0) {
                Node tagComId = nodes.item(0); //**
                Tag = tagComId.getParentNode();
    
                DocSignCont = new DOMSignContext(PrivPass, Tag);
    
                /* Do the signature */
                Signature = /*this.*/XmlSignFac.newXMLSignature(SignInfo, KeyInf);
                Signature.sign(DocSignCont);        
    
                /* Updates the file */      
                // OutputStream Saida = new FileOutputStream(arqAtualizar);
                // Writer Escritor = new OutputStreamWriter(Saida, Charset);
                Writer Escritor = new OutputStreamWriter(System.out,StandardCharsets.UTF_8); //++
                StreamResult Resultado = new StreamResult(Escritor);
                TransformerFactory TransformFac = TransformerFactory.newInstance();
                Transformer Transf = TransformFac.newTransformer();
                Transf.transform(new DOMSource(/*docAssinado*/Doc), Resultado);
            } else {
                throw new Exception ("id not found");
            }
        }
    

    为清晰起见,对其进行了轻微编辑:

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <RecepcionarLoteRps><EnviarLoteRpsEnvio xmlns="http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://isscuritiba.curitiba.pr.gov.br/iss/nfse.xsd">
     <LoteRps><ListaRps><Rps>
      <InfRps id="1">xxx</InfRps>
      <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
       <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
        <Reference URI="#1">
         <Transforms><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms>
         <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
         <DigestValue>fRXn427d6rObJ0udybG5aY5E6n4=</DigestValue>
         </Reference></SignedInfo>
       <SignatureValue>...</SignatureValue>
       <KeyInfo><X509Data><X509Certificate>...</X509Certificate></X509Data></KeyInfo>
      </Signature>
     </Rps></ListaRps> </LoteRps> </EnviarLoteRpsEnvio></RecepcionarLoteRps>
    

    在您的环境或应用程序中,这种形式的XML定义还是另一种形式是可以接受的,我不能说

    最后,如果你不知道,SHA1在2017年因碰撞而被破坏,大多数主管安全机构不再允许使用它(直接或间接)签名。(对于SSL/TLS证书,包括这样的网站,自2014-2015年以来,SHA1已经被禁止使用。)但这是个离题的话题