I need to canonicalize a part of the XML below :
<test xmlns="http://www.test.org/H003" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Revision="1" Version="H003">
<header authenticate="true">
<static>
<HostID>ABCDEF</HostID>
<Nonce>123456</Nonce>
<Timestamp>2017-02-20T14:41:38.965Z</Timestamp>
<PartnerID>ED123</PartnerID>
<UserID>0123</UserID>
<Product InstituteID="XX" Language="fr">XYZ</Product>
<OrderDetails>
<OrderType>ABC</OrderType>
<OrderAttribute>DDDD</OrderAttribute>
</OrderDetails>
<SecurityMedium>0000</SecurityMedium>
</static>
<mutable></mutable>
</header>
<AuthSignature>
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></ds:SignatureMethod>
<ds:Reference URI="#xpointer(//*[#authenticate='true'])">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></ds:DigestMethod>
<ds:DigestValue>abcdefghijklmnopqrstuvwxyz</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="X002">abcd123456</ds:SignatureValue>
</AuthSignature>
<body></body>
</test>
To do that I use Canonicalizer and XPathAPI :
private byte[] testCanon(Document document, String xpathString) throws XXXXException {
byte[] retour;
Node node;
NodeIterator iter;
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
Canonicalizer canonicalizer = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
iter = XPathAPI.selectNodeIterator(document, xpathString);
while ((node = iter.nextNode()) != null) {
copierAttributs((Element) document.getFirstChild(), (Element) node);
output.write(canonicalizer.canonicalizeSubtree(node));
}
retour = output.toByteArray();
} catch (TransformerException | InvalidCanonicalizerException | CanonicalizationException | IOException e) {
throw new XXXXException(e);
}
return retour;
}
I got a error "The prefix must become a namespace : ds" when I call selectNodeIterator with my xpathString equals to //ds:SignedInfo
if the namespace xmlns:ds move from SignedInfo to the parent it's ok, then the XML come from a client and I can't ask him to change it ...
Thanks
Related
In OpenSAML 2.5, I used the following code to generate a SAML Assertion with Security Certificate details:
Credential signingCredential = sign.getSigningCredential();
Signature signature = null;
try {
DefaultBootstrap.bootstrap();
} catch (ConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
signature = (Signature) Configuration.getBuilderFactory()
.getBuilder(Signature.DEFAULT_ELEMENT_NAME)
.buildObject(Signature.DEFAULT_ELEMENT_NAME);
signature.setSigningCredential(signingCredential);
// This is also the default if a null SecurityConfiguration is
// specified
SecurityConfiguration secConfig = Configuration
.getGlobalSecurityConfiguration();
try {
SecurityHelper.prepareSignatureParams(signature,
signingCredential, secConfig, null);
} catch (SecurityException e) {
e.printStackTrace();
} catch (org.opensaml.xml.security.SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SsoSamlWriter samlWriter = new SsoSamlWriter(ssoData);
Assertion assertion = samlWriter.buildSamlAssertion();
SAML msg extract:
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#514131e4-8ef0-469c-b8b0-a185b874320e">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="xs" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>fmIxhw8sGFU/J3SWDk5BnBCKRog=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>HOHkf...pqj2w==</ds:SignatureValue>
<KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIID...6BS9K
L/SvOZxWrA==</ds:X509Certificate>
</ds:X509Data>
</KeyInfo>
</ds:Signature>
I am trying to update this code to OpenSAML 3.2 and I have been able to get the SAML msg generated, but I don;t know how to attach the detailed security cert data. Does anyone have a code example for adding the SignatureValue and X509Certificate details to the SAML Assertion?
OpenSAML 3.2 Code I have so far:
Credential signingCredential = sign.getSigningCredential();
SsoSamlWriter samlWriter = new SsoSamlWriter(ssoData);
Assertion assertion = samlWriter.buildSamlAssertion();
Signature signature = SAMLUtils.buildSAMLObject(Signature.class);
signature.setSigningCredential(signingCredential);
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
// Need to supply an org.opensaml.security.credential.Credential;
signature.setSigningCredential(signingCredential);
assertion.setSignature(signature);
try {
XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(assertion).marshall(assertion);
} catch (MarshallingException e) {
throw new RuntimeException(e);
}
try {
Signer.signObject(signature);
} catch (SignatureException e) {
throw new RuntimeException(e);
}
// Wrap assertion in SAML Response
ResponseBuilder responseBuilder = new ResponseBuilder();
Response samlResponse = responseBuilder.buildObject();
// Build an Issuer object
Issuer issuer = SAMLUtils.buildSAMLObject(Issuer.class);
issuer.setValue(ssoIssuerName);
issuer.setSPProvidedID(ssoUrlSuffix);
// Add NameID element to assertion with Oasis Employee SSN
Subject subject = new SubjectBuilder().buildObject();
NameID nameID = new NameIDBuilder().buildObject();
nameID.setValue(empSsn);
nameID.setFormat(NameIDType.X509_SUBJECT);
subject.setNameID(nameID);
assertion.setSubject(subject);
String responseIdStr = UUID.randomUUID().toString();
assertion.setID(responseIdStr);
samlResponse.setID(responseIdStr);
samlResponse.setIssueInstant(new DateTime());
samlResponse.setIssuer(issuer);
samlResponse.setVersion(SAMLVersion.VERSION_20);
samlResponse.setStatus(samlWriter.createStatus());
samlResponse.getAssertions().add(assertion);
ResponseMarshaller marshaller = new ResponseMarshaller();
Element plain = marshaller.marshall(samlResponse);
String samlResponseStr = XMLHelper.nodeToString(plain);
// Remove ds: prefix from <ds:KeyInfo> elements
// stegre => Accomodate bug on CIC side, remove this line eventually
samlResponseStr = samlResponseStr.replaceAll("ds:KeyInfo", "KeyInfo");
System.out.println("");
System.out.println("SAML Response: ");
System.out.println(samlResponseStr);
System.out.println("");
Resulting SAML message:
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response ID="356498c8-036d-41b1-9602-89fa90e40331" IssueInstant="2016-10-12T15:15:23.987Z" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2:Issuer SPProvidedID="OasisAdvantage" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">OasisAdvantage</saml2:Issuer>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</saml2p:Status>
<saml2:Assertion ID="356498c8-036d-41b1-9602-89fa90e40331" IssueInstant="2016-10-12T15:15:20.442Z" Version="2.0" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer SPProvidedID="OasisAdvantage">OasisAdvantage</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#356498c8-036d-41b1-9602-89fa90e40331">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces PrefixList="xsd" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
<ds:DigestValue/>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue/>
</ds:Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName">OasisUser</saml2:NameID>
</saml2:Subject>
<saml2:Conditions NotBefore="2016-10-12T15:15:20.442Z" NotOnOrAfter="2016-10-12T15:20:20.442Z"/>
<saml2:AuthnStatement AuthnInstant="2016-10-12T15:15:20.566Z" SessionNotOnOrAfter="2016-10-12T15:15:20.581Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement>
<saml2:Attribute Name="companyid">
<saml2:AttributeValue xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">OasisAdvantage</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
The following code added the computed SignatureValue:
org.opensaml.saml.saml2.core.Response samlResponse = responseBuilder.buildObject();
// Compute and add SignatureValue element to Signature
XMLObjectProviderRegistrySupport.getMarshallerFactory()
.getMarshaller(samlResponse).marshall(samlResponse);
Signer.signObject(signature);
I am able to create a SAMLResponse using OpenSAML, but as a sanity check I wanted to validate the signature. The recipient wants the Assertion to be signed but not the Response, which looks just fine:
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response ID="1111111111" Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2:Assertion Version="2.0"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
<ds:Reference URI="" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transforms xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transform
Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
<ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">H1M/MSp4KUwKryB9c99XvE6PWaU=
</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
npeqj35bPh06G6WcSBzfpaDEqVUzhEOgqARKB6NIowGcAoD6yHpmDYTJ0nXvS31cRFYxqWtCVIrX
e7abo3mpwfLI7FjpEawP+8IEaMi++b0hN1Xri/bprMNjxB7kldDGJc2WkbdVRAAIknQetxytd9gB
WGhP97zLyGPVK23orH/JF09bFgoqGPgd/14fuY5ksOJuFfuZD4rsCOxxicYrfMsJDnlUfz8n18Xw
Jj6vX/0SDoDknMz9h4kh1TU4tus0FvIAPYLNB7QV7Iarpd+5Q9R8mj+NJEviFH/DZlqE/NYrxRNt
iinOgOnz1x3dtnDr2O/BHIW55mlBFi0L6wJ7ow==
</ds:SignatureValue>
</ds:Signature>
</saml2:Assertion>
</saml2p:Response>
If I validate the signature before marshalling the response, it's valid:
protected static void testSignature(Credential credential) throws Exception {
DefaultBootstrap.bootstrap();
SignatureBuilder builder = new SignatureBuilder();
Signature signature = builder.buildObject();
signature.setSigningCredential(credential);
signature.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1);
signature.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
Assertion assertion = new AssertionBuilder().buildObject();
assertion.setVersion(SAMLVersion.VERSION_20);
assertion.setSignature(signature);
AssertionMarshaller aMarsh = new AssertionMarshaller();
aMarsh.marshall(assertion);
Signer.signObject(signature);
Response response = new ResponseBuilder().buildObject();
response.setVersion(SAMLVersion.VERSION_20);
response.setID("1111111111");
response.getAssertions().add(assertion);
Signature sig = response.getAssertions().get(0).getSignature();
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
profileValidator.validate(sig);
SignatureValidator sigValidator = new SignatureValidator(credential);
sigValidator.validate(sig); //valid
}
However if I marshall the response by adding the following code, it comes back invalid even if I set the signature before marshalling:
...
Signature sig = response.getAssertions().get(0).getSignature();
ResponseMarshaller rMarsh = new ResponseMarshaller();
Element plain = rMarsh.marshall(response);
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
profileValidator.validate(sig);
SignatureValidator sigValidator = new SignatureValidator(credential);
sigValidator.validate(sig); //invalid
Lastly I tried to unmarshall the marshalled response, then read and validate the signature, and it still comes back invalid even though I can verify the response is the same as before marshalling/unmarshalling.
...
ResponseMarshaller rMarsh = new ResponseMarshaller();
Element plain = rMarsh.marshall(response);
String samlResponse1 = XMLHelper.nodeToString(plain);
UnmarshallerFactory unmarshallerFactory = Configuration.getUnmarshallerFactory();
Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(plain);
XMLObject responseXmlObj = unmarshaller.unmarshall(plain);
Response newResponse = (Response) responseXmlObj;
plain = rMarsh.marshall(newResponse);
String samlResponse2 = XMLHelper.nodeToString(plain);
System.out.println(StringUtils.equals(samlResponse1, samlResponse2)); //true
Signature sig = newResponse.getAssertions().get(0).getSignature();
SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator();
profileValidator.validate(sig);
SignatureValidator sigValidator = new SignatureValidator(credential);
sigValidator.validate(sig); //invalid
What's going on? Is there something I'm missing here?
Solved this. The Assertion requires an ID value that is different from the Response ID. I just needed to add the following line of code and it worked.
assertion.setID("1111111112");
I'm using spring-ws (2.1.4) WebServiceTemplate with a custom WebServiceMessageCallback to insert the security header with a SAML which I will receive as a org.w3c.dom.Element and only create myself for testing.
I use webServiceTemplate.marshalSendAndReceive(request, new HeaderCallback(ctx, uri)) to marshal and send my request.
My issue is that the verification of the signature fails on the server side, the digests don't match. Comparing the output of the SAML before sending and after receiving I see that on the receiving side I have namespace declarations on elements that didn't have one before sending.
What I currently do in my callback:
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
super.doWithMessage(message);
Assert.isInstanceOf(SoapMessage.class, message);
SoapMessage soapMessage = (SoapMessage) message;
SoapHeaderElement securityHeader = soapMessage.getSoapHeader().addHeaderElement(WS_SECURITY_NAME);
try {
Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(new DOMSource(assertion), securityHeader.getResult());
} catch (org.springframework.oxm.MarshallingException ex) {
throw new IOException(ex);
}
Does anyone know how I can insert an element into a SOAP XML making sure that the element will not be modified? Or is there a different approach to achieve this using Spring WebServiceTemplate?
I came across org.apache.ws.security.saml.ext.SAMLCallback which has a method setAssertionElement, however I did not find out how to get this working with the WebServiceTemplate (respectively Wss4jSecurityInterceptor) particularly considering that I need to pass in an existing SAML assertion as a parameter.
SAML XML before sending:
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" IssueInstant="2015-09-23T09:46:14.357Z" Version="2.0">
<saml2:Issuer>iTEST</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>rVbvzGr4LI/Kd0c+kuhqkupSY44=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>TIbtFHnqURfko94eqANDX8UEhw0HSI3xL0sAQ5GbhjVn2DicckzxpoJL3RVPF6KhLOkUPm5IgnF5At6Es69D48DLE+QvWvP0F7VmcdNJHRSnzIWYaJsXfNfDt2AXXM1kanp5Gq8nj1EsT6C8SlkO1x77C3vKkn1nLSNszEkukuhEzA8V7G+l6L65bIveA36IyYqjiVDKIMOIzHMF9qK03Um7qJHWoxvxSig1UPBINlBtDqyhbGW01OnoHP8qXK7bzuP6p3zbBDhhEYMyNV2P8GT8zX1+cv+JE6ghde4K9hyf6V+x6L218L1AOjACEJUkBrxw0q1uJAVGA1UNJPJbJQ==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIC1TCCAb2gAwIBAgIENFWkEzANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBpVEVTVDpjb21tdW5pdHlBMB4XDTE1MDkxMDE0NDcyMFoXDTI1MDkwNzE0NDcyMFowGzEZMBcGA1UEAxMQaVRFU1Q6Y29tbXVuaXR5QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMX9f6af8s8bqZSlSl7nZVmVz9sQuaxK/fYHwKcBCmeQGfx67K6Lrmg4ZkVlMxFPFAZta8i0UHVbFXD6hY4nD8skA+P1RjnHvk4KboBM2krtx7tSC8IzXmtb8sxXsfLKfu1Blqq0lX0kjPyvCIo4FLOSRE//AuKy8zeTSrKAX8tcZYo23mX8TWkp22Hd6O/x4IxuakWRB6fq8DxYWFsu9f1cnt2n3R+d/myrZ6XRVdpZhptWlZjzy90L2VOuHFC3kY8DvgSCdI3UgL+PzD7R3cKUytHlz04LO5cJue4kRaGbNOdgJoRdj4D24BsjpIZ9PCbzSw689N/hRftsUtO5PRECAwEAAaMhMB8wHQYDVR0OBBYEFBL153jJBGRArnJ5dSyHZGKC3vGcMA0GCSqGSIb3DQEBCwUAA4IBAQCWa0KtdISsQecjA3i4Kyr50oynOnas6YK9Jgb3LzJEU911JW4vX4rHmXEe64OkbIO/fpdCE30saTiBoLEFxrUx0xn+0wGvPMIBJZYncaZUUN9412XPEjYoj5DnFjFvdYqNsdNZ9cOs3sa5hRWufea+QFhSJKSG0nouwW1/5WT3/GYtKJZystL2N2q6pUqhwfP5x1JDELTcoiZZzbTkHOxFiz5iLMrZyDL8Bmb856pF7SzmD6NzLXpqiOwMjTeDWyJ3I9mZqDwoTwxC/e8wG42d486GKu3O/BbDkxEv5UDClMY2722oZKKhu3TdQLOIOLJChKgghjuEWOXb+mDdj/Qq</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID>authSigner</saml2:NameID>
</saml2:Subject>
</saml2:Assertion>
SAML XML after receiving:
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" IssueInstant="2015-09-23T09:46:14.357Z" Version="2.0">
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">iTEST</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:Reference URI="" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transforms xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#"/>
<ds:DigestValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">rVbvzGr4LI/Kd0c+kuhqkupSY44=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue xmlns:ds="http://www.w3.org/2000/09/xmldsig#">TIbtFHnqURfko94eqANDX8UEhw0HSI3xL0sAQ5GbhjVn2DicckzxpoJL3RVPF6KhLOkUPm5IgnF5At6Es69D48DLE+QvWvP0F7VmcdNJHRSnzIWYaJsXfNfDt2AXXM1kanp5Gq8nj1EsT6C8SlkO1x77C3vKkn1nLSNszEkukuhEzA8V7G+l6L65bIveA36IyYqjiVDKIMOIzHMF9qK03Um7qJHWoxvxSig1UPBINlBtDqyhbGW01OnoHP8qXK7bzuP6p3zbBDhhEYMyNV2P8GT8zX1+cv+JE6ghde4K9hyf6V+x6L218L1AOjACEJUkBrxw0q1uJAVGA1UNJPJbJQ==</ds:SignatureValue>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Certificate xmlns:ds="http://www.w3.org/2000/09/xmldsig#">MIIC1TCCAb2gAwIBAgIENFWkEzANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBpVEVTVDpjb21tdW5pdHlBMB4XDTE1MDkxMDE0NDcyMFoXDTI1MDkwNzE0NDcyMFowGzEZMBcGA1UEAxMQaVRFU1Q6Y29tbXVuaXR5QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMX9f6af8s8bqZSlSl7nZVmVz9sQuaxK/fYHwKcBCmeQGfx67K6Lrmg4ZkVlMxFPFAZta8i0UHVbFXD6hY4nD8skA+P1RjnHvk4KboBM2krtx7tSC8IzXmtb8sxXsfLKfu1Blqq0lX0kjPyvCIo4FLOSRE//AuKy8zeTSrKAX8tcZYo23mX8TWkp22Hd6O/x4IxuakWRB6fq8DxYWFsu9f1cnt2n3R+d/myrZ6XRVdpZhptWlZjzy90L2VOuHFC3kY8DvgSCdI3UgL+PzD7R3cKUytHlz04LO5cJue4kRaGbNOdgJoRdj4D24BsjpIZ9PCbzSw689N/hRftsUtO5PRECAwEAAaMhMB8wHQYDVR0OBBYEFBL153jJBGRArnJ5dSyHZGKC3vGcMA0GCSqGSIb3DQEBCwUAA4IBAQCWa0KtdISsQecjA3i4Kyr50oynOnas6YK9Jgb3LzJEU911JW4vX4rHmXEe64OkbIO/fpdCE30saTiBoLEFxrUx0xn+0wGvPMIBJZYncaZUUN9412XPEjYoj5DnFjFvdYqNsdNZ9cOs3sa5hRWufea+QFhSJKSG0nouwW1/5WT3/GYtKJZystL2N2q6pUqhwfP5x1JDELTcoiZZzbTkHOxFiz5iLMrZyDL8Bmb856pF7SzmD6NzLXpqiOwMjTeDWyJ3I9mZqDwoTwxC/e8wG42d486GKu3O/BbDkxEv5UDClMY2722oZKKhu3TdQLOIOLJChKgghjuEWOXb+mDdj/Qq</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:NameID xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">authSigner</saml2:NameID>
</saml2:Subject>
</saml2:Assertion>
</wsse:Security>
EDIT: I found a way that does not add the additional namespaces. What I'm doing now is using the javax.xml.soap.SOAPMessage and importing the node:
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
super.doWithMessage(message);
Assert.isInstanceOf(SaajSoapMessage.class, message);
try {
SOAPMessage soapMessage = ((SaajSoapMessage) message).getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
if (soapEnvelope.getHeader() == null) {
soapEnvelope.addHeader();
}
SOAPHeaderElement securityElement = soapEnvelope.getHeader().addHeaderElement(WS_SECURITY_NAME);
securityElement.appendChild(soapPart.importNode(assertion, true));
} catch (SOAPException ex) {
throw new IOException(ex);
}
}
However, even though the diff shows no difference between the outputs the verification is still failing. At this point I'm not sure if I can trust the output and that doing it like that really did not change the SAML...
Any ideas what else could cause the digests not to match?
The following approach (which I already documented on my question as an edit) prevented additional namespaces from being added.
#Override
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
super.doWithMessage(message);
Assert.isInstanceOf(SaajSoapMessage.class, message);
try {
SOAPMessage soapMessage = ((SaajSoapMessage) message).getSaajMessage();
SOAPPart soapPart = soapMessage.getSOAPPart();
SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
if (soapEnvelope.getHeader() == null) {
soapEnvelope.addHeader();
}
SOAPHeaderElement securityElement = soapEnvelope.getHeader().addHeaderElement(WS_SECURITY_NAME);
securityElement.appendChild(soapPart.importNode(assertion, true));
} catch (SOAPException ex) {
throw new IOException(ex);
}
}
I also got the SAML validation to work. It had nothing to do with the way the SAML was added to the SOAP message (also my original approach using Transformer- even though it adds the additional namespaces - validates now). Missing was the ID on the assertion.
Here's how my SAML looks now:
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" ID="_63ecd7ce7163507e56270edcd644494b" IssueInstant="2015-09-24T18:23:15.118Z" Version="2.0">
<saml2:Issuer>iTEST</saml2:Issuer>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<ds:Reference URI="#_63ecd7ce7163507e56270edcd644494b">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>yjlkSOIJC3qNYzEYfipGIwoAD9A=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>fIEp5aYFVesexZWAfMM+e8kcwe52nl54xMr92wtz2BZ3TIVkY6u5vfh8hA0q6hkzEEaHV5vM0cEXholekn4og41jhMn7XwmScggrbr/neYBMdE95GRz6Nv2Sc6PzqPTYeFp+Dylho7sy1yZtWAb3jiGm8zdHJIQuk6NNUZhnMPkZqjXpM68U9cRGojAmnczbRVl2couxDp5fErivKJgJuffN3FVBfCFrFOjGpVf00ukZxqJJ3RP+n7wXWl251W0i0KvWCZPCD5gzl69EzNFc78o8khzd26aLIeDFLBQr0BPnrC454dZZ/SSNeH52HtDstNJ6SBrWFAA8c8vnLSJ0fg==</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>MIIC1TCCAb2gAwIBAgIENFWkEzANBgkqhkiG9w0BAQsFADAbMRkwFwYDVQQDExBpVEVTVDpjb21tdW5pdHlBMB4XDTE1MDkxMDE0NDcyMFoXDTI1MDkwNzE0NDcyMFowGzEZMBcGA1UEAxMQaVRFU1Q6Y29tbXVuaXR5QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMX9f6af8s8bqZSlSl7nZVmVz9sQuaxK/fYHwKcBCmeQGfx67K6Lrmg4ZkVlMxFPFAZta8i0UHVbFXD6hY4nD8skA+P1RjnHvk4KboBM2krtx7tSC8IzXmtb8sxXsfLKfu1Blqq0lX0kjPyvCIo4FLOSRE//AuKy8zeTSrKAX8tcZYo23mX8TWkp22Hd6O/x4IxuakWRB6fq8DxYWFsu9f1cnt2n3R+d/myrZ6XRVdpZhptWlZjzy90L2VOuHFC3kY8DvgSCdI3UgL+PzD7R3cKUytHlz04LO5cJue4kRaGbNOdgJoRdj4D24BsjpIZ9PCbzSw689N/hRftsUtO5PRECAwEAAaMhMB8wHQYDVR0OBBYEFBL153jJBGRArnJ5dSyHZGKC3vGcMA0GCSqGSIb3DQEBCwUAA4IBAQCWa0KtdISsQecjA3i4Kyr50oynOnas6YK9Jgb3LzJEU911JW4vX4rHmXEe64OkbIO/fpdCE30saTiBoLEFxrUx0xn+0wGvPMIBJZYncaZUUN9412XPEjYoj5DnFjFvdYqNsdNZ9cOs3sa5hRWufea+QFhSJKSG0nouwW1/5WT3/GYtKJZystL2N2q6pUqhwfP5x1JDELTcoiZZzbTkHOxFiz5iLMrZyDL8Bmb856pF7SzmD6NzLXpqiOwMjTeDWyJ3I9mZqDwoTwxC/e8wG42d486GKu3O/BbDkxEv5UDClMY2722oZKKhu3TdQLOIOLJChKgghjuEWOXb+mDdj/Qq</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>
<saml2:Subject>
<saml2:NameID>authSigner</saml2:NameID>
</saml2:Subject>
</saml2:Assertion>
I am using CXF to consume a webservice
It involves signing the <SignedInfo> node with my private key and also encrypting the <soap:Body> node .
I am using WSS4JOutInterceptor to sign the soap message .
I am not able to figure out on how to specify WSS4J to sign the <SignedInfo> header node .
Also I want to put my custom Thumbprint in the header . Which I guess is used by the service provider to identify the public key they have to use .
Below is my Soap Header generated right now with the Java code I have used in the comments
<soap:Header>
<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="1">
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Id="SIG-3">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
<!--
Have to transform body and message control block
Did it using
outProps.put(WSHandlerConstants.SIGNATURE_PARTS, "{Element} {http://schemas.xmlsoap.org/soap/envelope/}Body;{Element}{https://checkout.buckaroo.nl/PaymentEngine/}MessageControlBlock");
I am not sure wether us should be SIGNATUE_ or ENCRYPT_ PARTS
-->
<ds:Reference URI="#id-1">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>Ougg/udU=</ds:DigestValue>
</ds:Reference>
<ds:Reference URI="#id-2">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>9cObhmiilqw=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<!--
Also have to generate a signature value which should be of the SignedInfo node
outProps.put(WSHandlerConstants.ACTION, "Signature");
outProps.put(WSHandlerConstants.SIG_PROP_FILE, "client_sign.properties");
outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS,ClientCallbackHandler.class.getName());
-->
<ds:SignatureValue>
no6CvOuwhLNGVOP1ByuICAVnSFTNq77QQttl4GIgHzatxr/ldna6yf36kufuzsHNx2n0Fp1k/05WZF9UPnzw=
</ds:SignatureValue>
<ds:KeyInfo Id="KI-AB3C213816422">
<wsse:SecurityTokenReference wsu:Id="STR-AB3C213816422">
<!--
Have to include my custom Thumbprint value here in wsse:KeyIdentifier node
outProps.put(WSHandlerConstants.SIG_KEY_ID, "Thumbprint");
-->
<wsse:KeyIdentifier
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
ValueType="http://docs.oasis-open.org/wss/oasis-wss-soap-message-security-1.1#ThumbprintSHA1">
z9576mEB7M7uRGq1vXGxwX4w4A0=
</wsse:KeyIdentifier>
</wsse:SecurityTokenReference>
</ds:KeyInfo>
</ds:Signature>
</wsse:Security>
<!--
This block I have added manually using
List<Header> headersList = new ArrayList<Header>();
MessageControlBlock messageControlBlock = new MessageControlBlock();
messageControlBlock.setWebsiteKey("I6t2");
messageControlBlock.setCulture("123L");
ObjectFactory factory = new ObjectFactory();
JAXBElement<MessageControlBlock> messCtlBlock = factory.createMessageControlBlock(messageControlBlock);
JAXBContext context = JAXBContext.newInstance(https.checkout_buckaroo_nl.paymentengine.MessageControlBlock.class);
Header messageControlBlockElement = new Header(
new QName("https://checkout.buckaroo.nl/PaymentEngine/", "MessageControlBlock"),
messageControlBlock,
new JAXBDataBinding(context));
headersList.add(messageControlBlockElement);
provider.getRequestContext().put(Header.HEADER_LIST, headersList); -->
<MessageControlBlock xmlns="https://checkout.buckaroo.nl/PaymentEngine/"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="id-2">
<WebsiteKey>I6tNcjYiMt</WebsiteKey>
<Culture>nl-NL</Culture>
</MessageControlBlock>
</soap:Header>
**I have also tried to intercept the message so that I can get the header from soap envelope and can myself update the thumbprint value .
But I am not able to get the Signature value in the Interceptor .
**
public class HeaderInterceptor extends AbstractSoapInterceptor {
public HeaderInterceptor() {
super(Phase.PRE_PROTOCOL_ENDING);
getAfter().add(org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor.SAAJOutEndingInterceptor.class.getName());
}
public void handleMessage(SoapMessage message) throws Fault {
System.out.println("HeaderInterceptor");
List<Header> headers = message.getHeaders();
System.out.println("Version is " + message.getVersion());
Map<String, List<String>> headersw = CastUtils.cast((Map)message.get(Message.PROTOCOL_HEADERS));
System.out.println("Headers w is " + headersw);
message.getInterceptorChain().forEach(chain -> {
System.out.println(chain.getClass());
});
Header security = message.getHeader(
new QName("http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", "Security")
);
System.out.println("Security header is " + security);
headers.stream().forEach(head ->{
System.out.println(head.getName().getLocalPart());
});
}
}
Output of above program is
Version is org.apache.cxf.binding.soap.Soap11#5404d5
Headers w is {Accept=[*/*], SOAPAction=["https://checkout.buckaroo.nl/PaymentEngine/CentralSoapGateway/InvoiceInfo"]}
class org.apache.cxf.jaxws.interceptors.HolderOutInterceptor
class org.apache.cxf.jaxws.interceptors.SwAOutInterceptor
class org.apache.cxf.jaxws.interceptors.WrapperClassOutInterceptor
class org.apache.cxf.binding.soap.interceptor.SoapHeaderOutFilterInterceptor
class org.apache.cxf.binding.soap.interceptor.SoapPreProtocolOutInterceptor
class org.apache.cxf.interceptor.MessageSenderInterceptor
class com.testapp.commons.MyLogInterceptor
class org.apache.cxf.interceptor.AttachmentOutInterceptor
class org.apache.cxf.interceptor.StaxOutInterceptor
class org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor
class org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor
class org.apache.cxf.interceptor.WrappedOutInterceptor
class org.apache.cxf.interceptor.BareOutInterceptor
class org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor$WSS4JOutInterceptorInternal
class org.apache.cxf.binding.soap.interceptor.SoapOutInterceptor$SoapOutEndingInterceptor
class org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor$SAAJOutEndingInterceptor
**class com.testapp.commons.HeaderInterceptor**
class org.apache.cxf.interceptor.StaxOutEndingInterceptor
class org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor
Security header is null
MessageControlBlock
I'm connecting to a webservice with axis/rampart and was told to remove the InclusiveNamespaces as the prefixList was "" which is not allowed. How do I do that?
The part looks like
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="wsa soapenv" />
</ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
<ds:Reference URI="#Id-289005241">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
<ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="" />
</ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<ds:DigestValue>bla bla bla=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
Is it possible to configure axis/rampart to not print the inclusivenamespace when it's empty?
I'm using axis/rampart 1.6.2 and connecting to a .NET service
Any ideas how to archive this? Or how do I make it render a non empty prefixList ?
You have to add a custom handler to filter the unwanted xml tag.
custom handler:
package com.perre;
public class InclusiveNamespacesFilter extends AbstractHandler {
public InvocationResponse invoke(MessageContext ctx) throws AxisFault {
SOAPEnvelope msgEnvelope = ctx.getEnvelope();
SOAPHeader msgHeader = msgEnvelope.getHeader();
Iterator theDescendants = msgHeader.getDescendants(true);
while(theDescendants.hasNext()){
Object o = theDescendants.next();
//here, add your code to traverse DOM and get the node to filter
//...
Node aNode = ele.getElementsByTagName("ec:InclusiveNamespacesFilter").item(0);
if(aNode != null){
ele.removeChild(aNode);
}
}
return InvocationResponse.CONTINUE;
}
edit your axis2.xml and declare the handler:
<phase name="PostSecurity">
<handler name="FilterHandler" class="com.perre.InclusiveNamespacesFilter"/>
</phase>
You should be ready to go. Find more reading about custom handlers here.