<soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<env:Header
xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<wsse:Security
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" soap:mustUnderstand="true">
<ds:Signature
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-B9561BC9E482A7482717165370736895">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"/>
<ds:Reference URI="#id-B9561BC9E482A7482717165370736684">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>
<ds:DigestValue>some value</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>some value </ds:SignatureValue>
</ds:Signature>
</wsse:Security>
</env:Header>
<soap:Body
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="id-B9561BC9E482A7482717165370736684">
<Test/>
</soap:Body>
</soap:Envelope>
I have the requirement to sign the soap body as shown above.
I believe this is called detached signing.
I want to use opensaml and the implementation till now looks like this:
signature.setSigningCredential(credential);
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
Then marshall the signature and call Signer.signObject(signature);
. Reference: https://blog.samlsecurity.com/2012/11/signing-with-opensaml.html
I also added content reference pointing to the soap body i want to sign
Element bodyElement = (Element) doc.getDocumentElement().getElementsByTagName("SOAP-Body").item(0);
String bodyId = "body123"; // Unique reference ID
bodyElement.setAttribute("Id", bodyId);
bodyElement.setIdAttribute("Id", true);
DocumentInternalIDContentReference uriContentReference = new DocumentInternalIDContentReference( bodyId);
uriContentReference.setDigestAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
uriContentReference.getTransforms().add(SignatureConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
signature.getContentReferences().add(uriContentReference);
But I get this error:
Exception in thread "main" org.opensaml.xmlsec.signature.support.SignatureException: Signature computation error
at org.opensaml.xmlsec.signature.support.impl.provider.ApacheSantuarioSignerProviderImpl.signObject(ApacheSantuarioSignerProviderImpl.java:62)
at org.opensaml.xmlsec.signature.support.Signer.signObject(Signer.java:73)
at com.amazon.bancomatpayremittancelambda.service.OpenSAMLDetachedSignature.main(OpenSAMLDetachedSignature.java:98)
Caused by: org.apache.xml.security.signature.ReferenceNotInitializedException: Cannot resolve element with ID body123
Original Exception was org.apache.xml.security.signature.ReferenceNotInitializedException: Cannot resolve element with ID body123
Original Exception was org.apache.xml.security.signature.ReferenceNotInitializedException: Cannot resolve element with ID body123
Original Exception was org.apache.xml.security.utils.resolver.ResourceResolverException: Cannot resolve element with ID body123
at org.apache.xml.security.signature.Reference.calculateDigest(Reference.java:744)
at org.apache.xml.security.signature.Reference.generateDigestValue(Reference.java:405)
at org.apache.xml.security.signature.Manifest.generateDigestValues(Manifest.java:205)
at org.apache.xml.security.signature.XMLSignature.sign(XMLSignature.java:631)
at org.opensaml.xmlsec.signature.support.impl.provider.ApacheSantuarioSignerProviderImpl.signObject(ApacheSantuarioSignerProviderImpl.java:59)
... 2 more
tl;dr
How can I create the above xml request using openSaml?
I was able to figure out how to sign a detached internal signature by looking at some UTs in openSaml library :
// 📝 Create Signature object
Signature signature = (Signature) builderFactory.getBuilder(Signature.DEFAULT_ELEMENT_NAME)
.buildObject(Signature.DEFAULT_ELEMENT_NAME);
// set signature properties
signature.setSigningCredential(credential);
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
//Since you want to sign the body within the same document create a reference to it with transforms and digest algorithm
DocumentInternalIDContentReference uriContentReference = new DocumentInternalIDContentReference( "body123");
uriContentReference.setDigestAlgorithm(SignatureConstants.ALGO_ID_DIGEST_SHA256);
uriContentReference.getTransforms().add(SignatureConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
uriContentReference.getTransforms().add(SignatureConstants.TRANSFORM_C14N_EXCL_OMIT_COMMENTS);
//add the above content reference to the signature
signature.getContentReferences().add(uriContentReference);
//add the signature to the header
header.getUnknownXMLObjects().add(signature);
// Assemble the envelope
soapEnvelope.setHeader(header);
soapEnvelope.setBody(bodyElement);
//Now the most important part is marshalling the right part of the xml. In my case since this was detached signature, I needed to marshall the document containing the signature and the body to be signed
Marshaller envelopeMarshaller = marshallerFactory.getMarshaller(soapEnvelope);
Element envelopeElement = envelopeMarshaller.marshall(soapEnvelope);
//finally sign the object
Signer.signObject(signature);
This created the request as expected.