Reputation: 1435
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
V+Mlk44c0uZD7TE+NlsFz3q1vtHHPi/9ygc2kJgQeSzxiCR2AHCONN3UN89RjidIqnN1qtKrBhc+
GNeEGhjqgV7DHvzK7tHVkC6c1EevsOV5bH2Gu0X5JsGwOtSHWe6eyOXue0TW7XWrqOLmOusWYhRR
ONJFoa49LQ4WV/RP498rp2TJ0bNE36PMWD6sMh52ERTj6NhngIl2cGjbbwzYteDN/ujo5bHmosmC
dVKBmgaw2YAICJy4BROyK7AmZI5BxKoZ6CY1Tw==</SignatureValue>
and here is part of the certificate:
<X509Certificate>MIIFtTCCBJ2gAwIBAgIQBsK927DS8wePBQjvzVX9BjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQG
EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMR0w
GwYDVQQDExRSYXBpZFNTTCBSU0EgQ0EgMjAxODAeFw0xOTA2MDcwMDAwMDBaFw0yMDA2MDYxMjAw
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
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
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
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