I'm calling a WCF service from a Java client using Apache CXF. The service is secured using an STS at another address. I've configured the service client to call out for the security token before invoking the main service and it works (it's trying to call the STS), but the STS is expecting some extra data to be provided in the RequestSecurityToken
element. The STS's policy specifies that the RequestSecurityToken
be encrypted and signed before being sent up and that's what's causing me the issues. The encryption and signing is working, but I can't seem to modify the SOAP message before it gets encrypted.
I looked at this question: How To Modify The Raw XML message of an Outbound CXF Request? and while it helped a lot, the part of the XML I need to alter resides inside a part of the SOAP message that gets encrypted and signed.
I made an Interceptor
and tried it on all the different phases I could find, but none of them seem to get invoked between the RequestSecurityToken
being created and the encryption and signing taking place.
Is there one? Or already a facility to add extra elements to the RequestSecurityToken
?
Edit for clarity:
Here's what my RST looks like now:
<wst:RequestSecurityToken xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>
<wsp:AppliesTo xmlns:wsp="http://www.w3.org/ns/ws-policy">
<wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:Address>http://localhost:9085/MyService</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>
<wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</wst:KeyType>
<wst:KeySize>192</wst:KeySize>
<wst:Entropy>
<wst:BinarySecret Type="http://docs.oasis-open.org/ws-sx/ws-trust/200512/Nonce">OlbfbuCUf3N2lNf9mhD03gfeMk0TfPI2nLWx8edlL5w=</wst:BinarySecret>
</wst:Entropy>
<wst:ComputedKeyAlgorithm>http://docs.oasis-open.org/ws-sx/ws-trust/200512/CK/PSHA1</wst:ComputedKeyAlgorithm>
<wst:Renewing/>
</wst:RequestSecurityToken>
Here's what the service provider's documentation says it needs to look like roughly (notice the Credentials
element near the end):
<t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<EndpointReference xmlns="http://www.w3.org/2005/08/addressing">
<Address>http://localhost:9085/MyService</Address>
</EndpointReference>
</wsp:AppliesTo>
<t:Entropy>
<t:BinarySecret u:Id="uuid-e2d08122-45ab-45cd-80d1-46de2306836b-1" Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">Ssex4V/175NCIOK1j4Mmbl47GiThOQMd</t:BinarySecret>
</t:Entropy>
<t:KeySize>192</t:KeySize>
<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</t:TokenType>
<t:KeyType>http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey</t:KeyType>
<Credentials>
<UserName type="string">username</UserName>
<Password type="string">password</Password>
</Credentials>
<t:ComputedKeyAlgorithm>http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1</t:ComputedKeyAlgorithm>
</t:RequestSecurityToken>
And this is more or less my code - where would I alter the RST?:
CXFBusFactory bf = new CXFBusFactory();
Bus bus = bf.createBus();
STSClient stsClient = new STSClient(bus);
Map<String, Object> stsProperties = new HashMap<>();
stsProperties.put(SecurityConstants.ENCRYPT_CRYPTO, stsMerlin);
stsProperties.put(SecurityConstants.SIGNATURE_CRYPTO, stsMerlin);
stsProperties.put(SecurityConstants.IS_BSP_COMPLIANT, "false");
stsClient.setProperties(stsProperties);
stsClient.setWsdlLocation("http://localhost:8090/SecurityTokenService?wsdl");
stsClient.setServiceName("{http://tempuri.org/}Service");
stsClient.setEndpointName("{http://tempuri.org/}Service_Port");
stsClient.setKeySize(192);
stsClient.getInInterceptors().add(new LoggingInInterceptor());
stsClient.getOutInterceptors().add(new LoggingOutInterceptor());
stsClient.setTokenType("http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1");
stsClient.setSoap12();
// Set the STS Client on the bus
bus.setProperty(SecurityConstants.STS_CLIENT, stsClient);
BusFactory.setDefaultBus(bus);
BusFactory.setThreadDefaultBus(bus);
MyService myService = new MyService();
IMyService myServicePort = myService.getCustomBindingIMyService();
Map<String, Object> ctx = ((BindingProvider)myServicePort).getRequestContext();
ctx.put(SecurityConstants.ENCRYPT_CRYPTO, merlin);
ctx.put(SecurityConstants.SIGNATURE_CRYPTO, merlin);
ctx.put(SecurityConstants.IS_BSP_COMPLIANT, "false");
myServicePort.doSomething();
Any insights appreciated.
So I ended up getting some help from the cxf users mailing list.
The response was as follows:
What I would suggest you do here is to subclass the STSClient in CXF:
In particular, you want to override the "issue" method here:
Just copy the existing method code + add in your own custom code at the end.
Sure enough, I subclassed the STSClient
and was able to copy-paste-override the issue
method which gives you access to the W3CDOMStreamWriter
that comprises the RequestSecurityToken
so I was able to make any modifications I want by just doing more writer.writeStartElement(...)
etc. before the final writer.writeEndElement()
.