首页 > 解决方案 > 如何仅签署 XML 文件的一部分 - Java XMLSignature - 但出现错误

问题描述

我只是想对 XML 的一部分进行数字签名。

要签名的 XML 如下所示:

<Invoice>
    <DataInvoice Id="data">
        ----
    </DataInvoice>
    <DataSignature>
        <SignatureSeller Id="seller">
        </SignatureSeller>
    </DataInvoice>
</Invoice>

签名后的xml

<Invoice>
    <DataInvoice Id="data">
        ----
    </DataInvoice>
    <DataSignature>
        <SignatureSeller Id="seller">
            <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="data">
                        <Transforms>
                            <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                        </Transforms>
                        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                        <DigestValue>49uyshWL+fnQ2UetAe2t3BNQqnA=</DigestValue>
                    </Reference>
                </SignedInfo>
                <SignatureValue>
                    VWI03lBKlE/wVT//uHME4qLyUs2PqxOgq0KBYaGYcR6z3QZdnXqOZClu9EVMT2V3RBA7hr+QQy2cCHVsdtb85B2gsNsRdxHPjj9KfLetTFx1lL8nlC6L5SA0x6k9Lh7xNBZTJl275IT4hnSnNe8q+9dWskgE/6We24DKBLMrOLg=
                </SignatureValue>
                <KeyInfo>
                    <X509Data>
                        <X509Certificate>
                            MIIEDzCCAvegAwIBAgIQVAT//rcDP7MW1nIgG4H6OjANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJWTjESMBAGA1UEBwwJSMOgIE7hu5lpMRYwFAYDVQQKEw1WaWV0dGVsIEdyb3VwMRMwEQYDVQQDEwpWaWV0dGVsLUNBMB4XDTIwMDYwODA5NDMyNFoXDTIxMDYwODA5NDMyNFowgYoxITAfBgkqhkiG9w0BCQEWEmh1b25nbnRAbWF0YmFvLmNvbTEeMBwGCgmSJomT8ixkAQEMDk1TVDowMzAyNzEyNTcxMSgwJgYDVQQDDB9Dw5RORyBUWSBD4buUIFBI4bqmTiBN4bquVCBCw4NPMQ4wDAYDVQQHDAVUUEhDTTELMAkGA1UEBhMCVk4wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJQ3aZioAdDIU1rQOO5tN7ORCqAWDWkQvP24gqD8LzRhA1Ui27DcOukuTp2bG8zSB3vGUMoFxV8qOpkfpt4/YUFhrMR6fG3k7J4YAKhkI/Ear3m3cEHo/MKxKsMbWHriI2CrKbG7Ih51iafRyt/mLGI66fNfnIwAoSIeXvLXi5YDAgMBAAGjggEuMIIBKjA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9vY3NwLnZpZXR0ZWwtY2Eudm4wHQYDVR0OBBYEFHQbrRodIeEnE0sMBLuNyp+lCsvhMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU/stBFOldp9kyDeSyCFzxXOy2srUwgZIGA1UdHwSBijCBhzCBhKAuoCyGKmh0dHA6Ly9jcmwudmlldHRlbC1jYS52bi9WaWV0dGVsLUNBLXYzLmNybKJSpFAwTjETMBEGA1UEAwwKVmlldHRlbC1DQTEWMBQGA1UECgwNVmlldHRlbCBHcm91cDESMBAGA1UEBwwJSMOgIE7hu5lpMQswCQYDVQQGEwJWTjAOBgNVHQ8BAf8EBAMCBeAwDQYJKoZIhvcNAQEFBQADggEBAJInz1BLmnJIfl8juAs+StPAl4a5bd9Gf4B6eksmYOBFLeUzc/B78LcO9B2z3fpOVcqOvsK9rCFMO81+TePa6KZODk4xnP2HjgpuiN+E/qdYucUSMQNNVH2BDa/BraK41H1ySVVuqxvQ5fAvkIfhgBuyt3RVRmBpWkRT1faJ1hh5o9sq3SBqTmqd+W5PmA4hOUHj/Rb4xcQF3hMywWIBnVXZAwJ+GTbF4T/XkFavxb54UObTuebaQ8deB4hIOMvM6SKagFofBVeurS7CELMQtHKBjVRndlzecK0zR6vzKDLBukL6W4K7s9u55A2563Vv5HhZJhQHnftlrmJ59bBUHx0=
                        </X509Certificate>
                    </X509Data>
                </KeyInfo>
                <Object xmlns="http://www.w3.org/2000/09/xmldsig#">
                    <SignatureProperties>
                        <SignatureProperty>
                            <SigningTime>2021-05-27T14:13:48</SigningTime>
                        </SignatureProperty>
                    </SignatureProperties>
                </Object>
            </Signature>
        </SignatureSeller>
    </DataInvoice>
</Invoice>

这是我用来尝试对其进行签名的代码,但我只需要对 XML 的一部分进行签名并将结果放入此 Id 中

public String xmlSign(String xml, String idData, String signBy) throws
        NoSuchAlgorithmException, InvalidAlgorithmParameterException, InterruptedException,
        UnrecoverableKeyException, KeyStoreException, SAXException, IOException, ParserConfigurationException,
        XPathExpressionException, MarshalException, XMLSignatureException
    {
        PublicKey publicKey = null;
        PrivateKey privateKey = null;
   
        if (!(cert instanceof Certificate)) {
            Map<String, Certificate> aliases = store.getAliases();
            for (Map.Entry<String, Certificate> item : aliases.entrySet()) {
                Certificate tmpCert = item.getValue();
                publicKey = tmpCert.getCertificate().getPublicKey();
                privateKey = (PrivateKey) store.getKeyStore().getKey(tmpCert.getAlias(), null);

                if (publicKey instanceof PublicKey && privateKey instanceof PrivateKey) {
                    cert = tmpCert;
                    break;
                }
            }
            
        } else {
            publicKey = cert.getCertificate().getPublicKey();
            privateKey = (PrivateKey) store.getKeyStore().getKey(cert.getAlias(), null);
        }
        
        if (publicKey == null || privateKey == null) {
            return null;
        }

        // Load factory
        XMLSignatureFactory factory = XMLSignatureFactory.getInstance("DOM");
        XPathFactory xpf = XPathFactory.newInstance();
        XPath xpath = xpf.newXPath();   
        
        // Load xml document
        Document document = this.importXML(this.ignoreLineBreak(xml));
        
        XMLObject xmlObject = factory.newXMLObject(Collections.singletonList(new DOMStructure(createTimestamp(document))), null, null, null);
        
        XPathExpression exprAssertion =  xpath.compile(String.format("//*[@Id='%s']", idData));
        Element assertionNode = (Element) exprAssertion.evaluate(document, XPathConstants.NODE);        
        assertionNode.setIdAttribute("Id", true);

    
        // Retreive Subject Node because the signature will be inserted before.
        XPathExpression exprAssertionSubject = xpath.compile(String.format("//*[@Id='%s']", "seller"));
        Node insertionNode = (Node) exprAssertionSubject.evaluate(document, XPathConstants.NODE);        

        DOMSignContext signContext = new DOMSignContext(privateKey, assertionNode, insertionNode);
        
        // Create transform
        List<Transform> transformList = new ArrayList<Transform>();
        Transform exc14nTranform = ((XMLSignatureFactory) factory).newTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315", (TransformParameterSpec) null);
        Transform envTransform = ((XMLSignatureFactory) factory).newTransform(Transform.ENVELOPED, (TransformParameterSpec) null);
        transformList.add(exc14nTranform);
        transformList.add(envTransform);
        
        // Create reference
        DigestMethod digestMethod = factory.newDigestMethod(DigestMethod.SHA1, null);
        Reference reference = factory.newReference("#" + idData, digestMethod, transformList, null, (signBy != null ? "#" + signBy : null));
    
        // Create key info
        KeyInfoFactory kif = ((XMLSignatureFactory) factory).getKeyInfoFactory();
        List<X509Certificate> x509Content = new ArrayList<X509Certificate>();
        x509Content.add(cert.getCertificate());
        X509Data xd = kif.newX509Data(x509Content);
        KeyInfo keyInfo = kif.newKeyInfo(Collections.singletonList(xd));
        
        // Create signature info
        SignedInfo signInfo = ((XMLSignatureFactory) factory).newSignedInfo(
            ((XMLSignatureFactory) factory).newCanonicalizationMethod(
                    CanonicalizationMethod.INCLUSIVE,
                    (C14NMethodParameterSpec) null
                ),
            ((XMLSignatureFactory) factory).newSignatureMethod(
                    SignatureMethod.RSA_SHA1,
                    null
                ),
            Collections.singletonList(reference)
        );
        
        
        XMLSignature signature = factory.newXMLSignature(signInfo, keyInfo, Collections.singletonList(xmlObject), null, null);
        
        signature.sign(signContext);
        
        return this.ignoreLineBreak(
            this.exportXML(new DOMSource(document))
        );
    }

它无法工作并显示错误:

NOT_FOUND_ERR:尝试在不存在的上下文中引用节点。com.sun.org.apache.xerces.internal.dom.ParentNode.internalInsertBefore(未知来源)

任何帮助将不胜感激。问候

标签: javaxmlxml-signature

解决方案


推荐阅读