How to produce XML signature with no whitespaces and line-breaks in Java?

前端 未结 8 1566
情话喂你
情话喂你 2020-12-17 00:51

I work with the brazilian \"Nota Fiscal Eletronica\" project, in which they define a standart way to sign XML documents.

Recently, they started to require that there

相关标签:
8条回答
  • 2020-12-17 01:26

    Fortunately XMLSignature is opensource, so I guess you'll have to get the source code and hack it your self.

    Probably your solution will help others in the future so, create a patch and send it back to the project.

    Good luck!

    :) :)

    0 讨论(0)
  • 2020-12-17 01:29

    I found a (shameful) solution.

    It's not the expected solution, though: replacing apache's API with javax.xml.crypto API.

    Here's the changed code:

    // the element where to insert the signature
    Element element = ...;
    X509Certificate cert = ...;
    PrivateKey privateKey = ...;
    // Create a DOM XMLSignatureFactory that will be used to
    // generate the enveloped signature.
    XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
    
    // Create a Reference to the enveloped document (in this case,
    // you are signing the whole document, so a URI of "" signifies
    // that, and also specify the SHA1 digest algorithm and
    // the ENVELOPED Transform.
    List<Transform> transformList = new ArrayList<Transform>();
    TransformParameterSpec tps = null;
    Transform envelopedTransform;
    try {
        envelopedTransform = fac.newTransform(Transform.ENVELOPED,
                tps);
        Transform c14NTransform = fac.newTransform(
                "http://www.w3.org/TR/2001/REC-xml-c14n-20010315", tps);
    
        transformList.add(envelopedTransform);
        transformList.add(c14NTransform);
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
    } catch (InvalidAlgorithmParameterException e) {
        throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
    }
    
    // Create the KeyInfo containing the X509Data.
    KeyInfoFactory kif = fac.getKeyInfoFactory();
    List<Serializable> x509Content = new ArrayList<Serializable>();
    x509Content.add(cert);
    javax.xml.crypto.dsig.keyinfo.X509Data xd = kif.newX509Data(x509Content);
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));
    
    // Obtem elemento do documento a ser assinado, será criado uma
    // REFERENCE para o mesmo
    Element el = (Element) element.getElementsByTagName(subTag).item(0);
    String id = el.getAttribute("Id");
    
    // Create a DOM XMLSignatureFactory that will be used to
    // generate the enveloped signature.
    
    Reference ref;
    javax.xml.crypto.dsig.SignedInfo si;
    try {
        ref = fac.newReference("#" + id, fac.newDigestMethod(
                DigestMethod.SHA1, null), transformList, null, null);
    
        // Create the SignedInfo.
        si = fac.newSignedInfo(fac.newCanonicalizationMethod(
                CanonicalizationMethod.INCLUSIVE,
                (C14NMethodParameterSpec) null), fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
                Collections.singletonList(ref));
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
    } catch (InvalidAlgorithmParameterException e) {
        throw new RuntimeException("Erro inesperado: " + e.getMessage(), e);
    }
    
    // Create the XMLSignature, but don't sign it yet.
    javax.xml.crypto.dsig.XMLSignature signature = fac.newXMLSignature(si, ki);
    
    // Marshal, generate, and sign the enveloped signature.
    // Create a DOMSignContext and specify the RSA PrivateKey and
    // location of the resulting XMLSignature's parent element.
    DOMSignContext dsc = new DOMSignContext(privateKey, element);
    signature.sign(dsc);
    

    This API produces the signature with no whitespaces between tags at all.

    Still would like to see a solution for apache's API, since this code was very mature already, and we wouldn't like to risk as much as changing the entire signature implementation.

    0 讨论(0)
  • 2020-12-17 01:33

    You can simply set -Dorg.apache.xml.security.ignoreLineBreaks=true for disabling '\n' in XML generation. original mail

    bug description

    0 讨论(0)
  • 2020-12-17 01:36

    You can try:

    System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
    
    0 讨论(0)
  • 2020-12-17 01:37

    You can try:

    static {
        System.setProperty("com.sun.org.apache.xml.internal.security.ignoreLineBreaks", "true");
        com.sun.org.apache.xml.internal.security.Init.init();
    }
    

    Or

    static {
        System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
        org.apache.xml.security.Init.init();
    }
    

    Add this to the class which does signature job.

    0 讨论(0)
  • 2020-12-17 01:41

    the signature blocks are encoding binary information as Base64, which must follow some formating including line breaks (see http://en.wikipedia.org/wiki/Base64). So you simply can't remove them without alter the information.

    a better way to redure network traffic, is to use comression before sending data.

    0 讨论(0)
提交回复
热议问题