user3217883
user3217883

Reputation: 1435

How to prevent adding whitespace into java xml signature and certificate?

I'm signing part of an xml document but the resulting signature and certificate have unwanted whitespace in them. For example, here is the signature:

    <SignatureValue>QcjPfiZcmqE8aMNH5AKVk+oFBYQ4LynV3a5YlJIxuf0y22QQ0NA2BTkRriI85dd/6Qcezf5xFguJ&#13;
V+Mlk44c0uZD7TE+NlsFz3q1vtHHPi/9ygc2kJgQeSzxiCR2AHCONN3UN89RjidIqnN1qtKrBhc+&#13;
GNeEGhjqgV7DHvzK7tHVkC6c1EevsOV5bH2Gu0X5JsGwOtSHWe6eyOXue0TW7XWrqOLmOusWYhRR&#13;
ONJFoa49LQ4WV/RP498rp2TJ0bNE36PMWD6sMh52ERTj6NhngIl2cGjbbwzYteDN/ujo5bHmosmC&#13;
dVKBmgaw2YAICJy4BROyK7AmZI5BxKoZ6CY1Tw==</SignatureValue>

and here is part of the certificate:

    <X509Certificate>MIIFtTCCBJ2gAwIBAgIQBsK927DS8wePBQjvzVX9BjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQG&#13;
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMR0w&#13;
GwYDVQQDExRSYXBpZFNTTCBSU0EgQ0EgMjAxODAeFw0xOTA2MDcwMDAwMDBaFw0yMDA2MDYxMjAw&#13;

notice how both have "$#13;" appended?

Here is the code that does the signing:

    private void buildSignatureBlock5(String privateKeyPath, String publicKeyPath) {
    // 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 just the element with Id="Body", so a URI of "#Body" signifies
    // that, and also specify the SHA1 digest algorithm and the ENVELOPED Transform.
    Reference ref = null;
    try {
        ref = fac.newReference
         ("#Body", fac.newDigestMethod(DigestMethod.SHA1, null),
          Collections.singletonList
           (fac.newTransform
            (Transform.ENVELOPED, (TransformParameterSpec) null)),
             null, null);
    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    // Create the SignedInfo.
    SignedInfo si = null;
    try {
        si = fac.newSignedInfo
         (fac.newCanonicalizationMethod
          (CanonicalizationMethod.INCLUSIVE,
           (C14NMethodParameterSpec) null),
            fac.newSignatureMethod(SignatureMethod.RSA_SHA1, null),
             Collections.singletonList(ref));
    } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }       

    // Load the KeyStore and get the signing key and certificate.
    KeyStore ks = null;
    try {
        ks = KeyStore.getInstance("JKS");
    } catch (KeyStoreException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    try {
        ks.load(new FileInputStream(storage_path +"/keys/myproject.jks"), "changeit".toCharArray());
    } catch (NoSuchAlgorithmException | CertificateException | IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    KeyStore.PrivateKeyEntry keyEntry = null;
    try {
        keyEntry = (KeyStore.PrivateKeyEntry) ks.getEntry
            ("1", new KeyStore.PasswordProtection("changeit".toCharArray()));
    } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    X509Certificate cert = (X509Certificate) keyEntry.getCertificate();

    // Create the KeyInfo containing the X509Data.
    KeyInfoFactory kif = fac.getKeyInfoFactory();
    List x509Content = new ArrayList();
    String issuerName = cert.getIssuerX500Principal().getName();
    BigInteger serialNumber = cert.getSerialNumber();
    X509IssuerSerial issuer = kif.newX509IssuerSerial(issuerName, serialNumber);
    x509Content.add(issuer);
    x509Content.add(cert);
    X509Data xd = kif.newX509Data(x509Content);
    KeyInfo ki = kif.newKeyInfo(Collections.singletonList(xd));    

    // Create a DOMSignContext and specify the RSA PrivateKey and location of the resulting XMLSignature's parent element.
    Element envHeaderSig = (Element) document.getElementsByTagName("SOAP-SEC:Signature").item(0);
    DOMSignContext dsc = new DOMSignContext(keyEntry.getPrivateKey(), envHeaderSig);

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

    try {
        signature.sign(dsc);    //ResourceResolverException: Cannot resolve element with ID Body
    } catch (MarshalException | XMLSignatureException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

I have tried the solutions suggested here:

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

But nothing worked. Any ideas?

I have an example signature block created by PHP that I know works which doesn't have that whitespace. I'm trying to make my java output look just like it.

Upvotes: 0

Views: 1956

Answers (3)

luckyqiao
luckyqiao

Reputation: 52

Which xml security library do you use? If you use org.apache.santuario:xmlsec:3.0.1, you could try

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

It works fine for me.

Upvotes: 0

J&#243;zsef Mecsei
J&#243;zsef Mecsei

Reputation: 19

Try this parameter : -Dorg.apache.xml.security.ignoreLineBreaks=true

Reference : [https://stackoverflow.com/questions/4728300/how-to-produce-xml-signature-with-no-whitespaces-and-line-breaks-in-java][1]

Upvotes: 0

user3217883
user3217883

Reputation: 1435

I could not find a way to do it "the right way" so wrote a hack to modify it after it got generated. Here it is:

//hack to remove unwanted CR at the end of each line in SignatureValue and X509Certificate
private void removeWhitespaceFromSignature() {
    Element sig = (Element) document.getElementsByTagName("SignatureValue").item(0);
    String sigValue = sig.getTextContent().replaceAll("\r\n", "");
    sig.setTextContent(sigValue);

    Element cert = (Element) document.getElementsByTagName("X509Certificate").item(0);
    String certValue = cert.getTextContent().replaceAll("\r\n", "");
    cert.setTextContent(certValue);
}

Seems ridiculous to have to resort to this but hours of searching produced no other alternatives.

Upvotes: 3

Related Questions