JAXB Marshalling with xmldsig Signature

2019-02-07 05:11发布

问题:

Is it possible to create jaxb marshaller which automatically adds digital signature to xml content.

For example if I have a class which is defined:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Test {
    @XmlElement
    private String info;

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

And my xml which is produced by marshaller looks like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><test><info>value</info></test>

And I expect it to look like:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <Security>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
                <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#WithComments"/>
                <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                <Reference URI="">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#envelopedsignature"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>4432kZ6c2JPwP3A=</DigestValue>
                </Reference>
            </SignedInfo>
            <SignatureValue>Mvbd4603knhh2LZTyE1MIiEF7N46b7GoTzxsqs5eyIXYNG96MFPIMo+P6okzIPzRKrL2obpf3V4D/F0gw5vM/UJwb2MvrCo/5JM5qvV0f09dzWLrgkPyShiQnFL2vzECwmMOrCA=</SignatureValue>
            <KeyInfo>
                <X509Data>
                        <X509Certificate>MIIEnjCCBAegAwIBAgIDJQDMA0GCSqGSICBMGSETU0VOMRgwFgYDVQQHEw82NTAwOCBXaWVzYmFkZW4xGjAYBgNVBkESE9MRElORyBBRzEiMCAGA1UEAxMZSW50ZXJuZXQgQmVudXR6ZXIgU2VydmljZTEqMCgGCSqGSIb3DQEJARbeVydmaWthdEBzY2h1ZmEtb25saW5lLmRlMB4XDTA5MDcyNzEwNTkyNVoXDTEwMDcyNzEwNTkyNVowgZsxCzAJdNVBATAkFMQLmRlMCYGfUdEgQfMB2BG3plcnRpZmlrYXRAc2NodWZhLW9ubGluZS5kZTAfsgkqhkiG9w0BAQQFAAOBgQB7DBly8bqksxrkwcmN2A/xfu3lm0IfGD6PoJ7JFgPq4aHBDWgdUW3EzvRE+cuFGjkoBvATKfcbF7ReTz+4C+dLJShYiN/HxUnxgiO7R2y4c/I4pNnmlfQT261/dNlQ8Wm8pyUeMcr32fxvtoY4dqlQy7GePmrHpR3HE/dMRAd6gA==</X509Certificate>
                </X509Data>
                <KeyValue>
                    <RSAKeyValue>
                        <Modulus>1EN/UxtM2fLYxxDmSxgjSd10AzCxvZtNGAER9j3+OMqZjBXG9uLiZR+GbtOXbsDz3fyiwEfu/FDeeGGESppYAL5foQ72t2ztV5w2GLtTH0K+wrlImmvoTdl6bsdC7RXAsXVxtlkoG0xL7HGwZLvM=</Modulus>
                        <Exponent>AQAB</Exponent>
                    </RSAKeyValue>
                </KeyValue>
            </KeyInfo>
        </Signature>
    </Security>
<test><info>value</info></test>

I hope there is a way to do it by marshaller? If not maybe there is any other simple way to sign an xml ?

Thanks in advance

回答1:

You will need to use JAXB to marshal your domain model to a DOM Document and then apply the signature to that using an approach like the following:

import java.security.*;
import java.util.Collections;
import javax.xml.bind.*;
import javax.xml.crypto.XMLStructure;
import javax.xml.crypto.dsig.*;
import javax.xml.crypto.dsig.dom.DOMSignContext;
import javax.xml.crypto.dsig.keyinfo.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Test.class);

        Test test = new Test();
        test.setInfo("value");

        Marshaller marshaller = jc.createMarshaller();
        DOMResult domResult = new DOMResult();
        marshaller.marshal(test, domResult);

        String providerName = System.getProperty("jsr105Provider",
                "org.jcp.xml.dsig.internal.dom.XMLDSigRI");

        XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM",
                (Provider) Class.forName(providerName).newInstance());

        Reference ref = fac.newReference("", fac.newDigestMethod(
                DigestMethod.SHA1, null), Collections.singletonList(fac
                .newTransform(Transform.ENVELOPED, (XMLStructure) null)), null,
                null);

        SignedInfo si = fac.newSignedInfo(fac.newCanonicalizationMethod(
                CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS, (XMLStructure) null), fac
                .newSignatureMethod(SignatureMethod.DSA_SHA1, null),
                Collections.singletonList(ref));

        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(512);
        KeyPair kp = kpg.generateKeyPair();

        KeyInfoFactory kif = fac.getKeyInfoFactory();
        KeyValue kv = kif.newKeyValue(kp.getPublic());

        KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv));

        Document doc = (Document) domResult.getNode();

        DOMSignContext dsc = new DOMSignContext(kp.getPrivate(),
                doc.getDocumentElement());

        XMLSignature signature = fac.newXMLSignature(si, ki);
        signature.sign(dsc);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        DOMSource source = new DOMSource(domResult.getNode());
        StreamResult result = new StreamResult(System.out);
        t.transform(source, result);
    }

}