javax.xml.crypto.URIReferenceException:无法解析ID为1234的元素。最后提供了解决方案

时间:2019-05-10 11:49:01

标签: java xml cxf xml-signature

我的工作流程如下:

  1. 获取XML作为字符串
  2. 将字符串转换为文档,对文档进行签名(某些元素),然后将其返回为字符串
  3. 以字符串形式获取签名的XML,将其转换为文档并验证签名

以下是签名创建方法:

public static String createXMLDSign(String xmlString) throws Exception{
    // Create a DOM XMLSignatureFactory that will be used to
    // generate the enveloped signature.
    String providerName = System.getProperty("jsr105Provider", "org.jcp.xml.dsig.internal.dom.XMLDSigRI");
    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM", (Provider) Class.forName(providerName).newInstance());

    // Load the KeyStore and get the signing key and certificate.
    KeyStore ks = KeyStore.getInstance("JKS");
    ks.load(new FileInputStream("src/main/resources/jks/client-keystore.jks"), keyStorePass .toCharArray());
    KeyStore.PrivateKeyEntry keyEntry =
        (KeyStore.PrivateKeyEntry) ks.getEntry
            ("client-keypair", new KeyStore.PasswordProtection(keyPass .toCharArray()));
    X509Certificate cert = (X509Certificate) keyEntry.getCertificate();

    // Create the KeyInfo containing the X509Data.
    KeyInfoFactory kif = fac.getKeyInfoFactory();
    List x509Content = new ArrayList();
    x509Content.add(cert.getSubjectX500Principal().getName());
    x509Content.add(cert);
    X509Data xd = kif.newX509Data(x509Content);
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));

    // Instantiate the document to be signed.
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    Document doc = dbf.newDocumentBuilder().parse
            (new InputSource(new StringReader(xmlString)));


    // Get element which will be signed
    Element child =(Element)doc.getElementsByTagName("ns2:Eid").item(0);
    child.setAttribute("Id", "1234");
    child.setIdAttribute("Id", true); 
    String id = child.getAttributes().getNamedItem("Id").getNodeValue();
    String uri = String.format("#%s", id);

    List<Transform> transformList = new LinkedList<>();
    transformList.add(fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null));
    Reference reference = fac.newReference(uri,
            fac.newDigestMethod(DigestMethod.SHA256, null), transformList, null, null);

    SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
            CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null), fac
            .newSignatureMethod("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", null), Collections.singletonList(reference));

    // Remove element from the Node
    Node node = doc.getElementsByTagName("ns2:Eis").item(0);
    Node eumNode = doc.getElementsByTagName("ns2:EumSignature").item(0);
    node.removeChild(eumNode);

    // Create a DOMSignContext and specify the RSA PrivateKey and
    // location of the resulting XMLSignature's parent element.
    DOMSignContext dsc = new DOMSignContext
        (keyEntry.getPrivateKey(), /*doc.getDocumentElement()*/node);
    dsc.setDefaultNamespacePrefix("ds");

    // Create the XMLSignature, but don't sign it yet.
    XMLSignature signature = fac.newXMLSignature(si, ki);

    // Marshal, generate, and sign the enveloped signature.
    signature.sign(dsc);

    /////////////Signature verification
    // Find Signature element.
    signVerification(fac, doc, signature);

    // Convert DocToString and verify
    String stringDoc = Utils.docToString(doc);
    // Instantiate the document to be signed.
    DocumentBuilderFactory dbf2 = DocumentBuilderFactory.newInstance();
    dbf2.setNamespaceAware(true);
    Document doc2 = dbf2.newDocumentBuilder().parse
            (new InputSource(new StringReader(stringDoc)));
    signVerification(fac, doc2, signature);

    /////////////////////////
    return Utils.docToString(doc);

}

注意:我要在要签名的元素中手动添加“ Id”属性,因为该属性在原始XML中不存在。

这是用于docToString转换的方法:

public static String docToString(Document doc) {
    try {
        DOMSource domSource = new DOMSource(doc);
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.transform(domSource, result);
        return writer.toString();
    } catch (TransformerException ex) {
        ex.printStackTrace();
        return null;
    }
}

执行结果:第一个验证成功通过,但转换失败后的第二个验证失败并显示错误:

  

javax.xml.crypto.dsig.XMLSignatureException:javax.xml.crypto.URIReferenceException:com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException:无法解析ID 1234的元素       在org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:431)       在org.jcp.xml.dsig.internal.dom.DOMReference.validate(DOMReference.java:393)       在org.jcp.xml.dsig.internal.dom.DOMXMLSignature.validate(DOMXMLSignature.java:278)       在com.innonetworks.utils.XmlSignatureHelper.signVerification(XmlSignatureHelper.java:185)       在com.innonetworks.utils.XmlSignatureHelper.createXMLDSign(XmlSignatureHelper.java:160)       在com.innonetworks.interceptor.XMLDSignInterceptor.changeOutboundMessage(XMLDSignInterceptor.java:36)       在com.innonetworks.interceptor.MessageChangeInterceptor.handleMessage(MessageChangeInterceptor.java:56)       在org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)       在com.innonetworks.interceptor.MessageChangeInterceptor.handleMessage(MessageChangeInterceptor.java:41)       在org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308)       在org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:531)       在org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:440)       在org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:355)       在org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:313)       在org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)       在org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:140)       在com.sun.proxy。$ Proxy55.es1RegisterEIS(未知来源)       在com.innonetworks.es1.client.SoapClientApp.callSoapClient(SoapClientApp.java:178)       在com.innonetworks.es1.client.SoapClientApp.main(SoapClientApp.java:63)   引起原因:javax.xml.crypto.URIReferenceException:com.sun.org.apache.xml.internal.security.utils.resolver.ResourceResolverException:无法解析ID为1234的元素       在org.jcp.xml.dsig.internal.dom.DOMURIDereferencer.dereference(DOMURIDereferencer.java:134)       在org.jcp.xml.dsig.internal.dom.DOMReference.dereference(DOMReference.java:425)       ...另外18个

注意如果我将“ Reference”参数“ uri”替换为“”,则两次验证均成功通过

解决方案 我已解决此问题,并希望分享是否可以帮助其他人。我在Oracle页面JDK-8017265 : XMLSignature Cannot Resolve ID

中找到的解决方案

我使用了第二种解决方法-在验证签名之前,使用DOMValidateContext.setIdAttributeNS方法注册ID元素

代码如下:

DOMValidateContext valContext = new DOMValidateContext
        (new X509KeySelector(), nl.item(0));
     final NodeList elements = docNew.getElementsByTagName("ns2:EumSignedInfo");
    if (elements.getLength() > 0) {
        valContext.setIdAttributeNS((Element) elements.item(0), null, "Id");
    }

0 个答案:

没有答案