Assuming that a webservice call containing a signed SAML Assertion, how do we verify that signature? CXF provides "interceptors" to add code before and after a call to a webservice.
First we must configure the call (with Spring in this case):
<jaxws:client id="clientWS" serviceClass="es.mycompany.MyService" address="http://localhost:8080/app/services/myservice"> <jaxws:dataBinding> <ref bean="aegisBean" /> </jaxws:dataBinding> <jaxws:inInterceptors> <ref bean="clientWS.InInterceptor" /> </jaxws:inInterceptors></jaxws:client><bean id="clientWS.InInterceptor" class="es.mycompany.MyInterceptor"> <constructor-arg> <map> <entry key="action" value="Timestamp Signature"/> <entry key="passwordCallbackClass" value="es.mycompany.MyPasswordCallback"/> <entry key="signaturePropFile" value="keystore.properties"/> </map> </constructor-arg></bean>
After that, we need to create a "keystore.properties" file:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlinorg.apache.ws.security.crypto.merlin.keystore.type=jksorg.apache.ws.security.crypto.merlin.keystore.alias=MyAliasorg.apache.ws.security.crypto.merlin.keystore.password=MyPasswordorg.apache.ws.security.crypto.merlin.file=keystore.jks
And finally the class for managing the certificate passwords. This class needs improvements, but it's good as an example:
public class MyPasswordCallback implements CallbackHandler { private MapWith everything in place, we only have to code the interceptor, which in turn uses a utilities class:passwords = new HashMap (); public STSPasswordCallback() { passwords.put("MyAlias", "MyPassword"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (int i = 0; i < pc =" (WSPasswordCallback)" pass =" passwords.get(pc.getIdentifier());">
public class MyInterceptor extends WSS4JInInterceptor { private String issuer = "http://www.mycompany.com"; public MyInterceptor() { super(); } public MyInterceptor(MapAs you can see, all the work is done in the trustEngine.validate(assertion.getSignature(), criteriaSet); line, we only need to reach there with the right data.properties) { super(properties); } protected void doReceiverAction(int doAction, RequestData reqData) throws WSSecurityException { // Get SOAP Header SOAPMessage message = ((org.apache.cxf.binding.soap.SoapMessage) reqData.getMsgContext()).getContent(javax.xml.soap.SOAPMessage.class); SOAPHeader soapHeader = message.getSOAPHeader(); // Get Assertion XML XPathUtils xu = new XPathUtils(); Element xmlAssertion = (Element) xu.getValueNode("//saml2:Assertion", soapHeader); // Unmarshall Unmarshaller unmarshaller = unmarshallerFactory.getUnmarshaller(xmlAssertion); Assertion assertion = (Assertion) unmarshaller.unmarshall(xmlAssertion); // Get Handler properties String sigPropFile = getString(WSHandlerConstants.SIG_PROP_FILE, reqData.getMsgContext()); String callback = getString(WSHandlerConstants.PW_CALLBACK_CLASS, reqData.getMsgContext()); // Verify SAMLUtils.verifySignature(assertion, issuer, sigPropFile, callback); super.doReceiverAction(doAction, reqData); } public void handleMessage(SoapMessage message) throws Fault { super.handleMessage(message); }}public class SAMLUtils { public static void verifySignature(Assertion assertion, String issuer, String sigPropFile, String callback) throws WSSecurityException { X509Credential cred = getX509Credential(sigPropFile, callback); StaticCredentialResolver credResolver = new StaticCredentialResolver(cred); KeyInfoCredentialResolver kiResolver = SecurityHelper.buildBasicInlineKeyInfoResolver(); ExplicitKeySignatureTrustEngine trustEngine = new ExplicitKeySignatureTrustEngine(credResolver, kiResolver); CriteriaSet criteriaSet = new CriteriaSet(); criteriaSet.add(new EntityIDCriteria(issuer)); criteriaSet.add(new UsageCriteria(UsageType.SIGNING)); criteriaSet.add(new MetadataCriteria(IDPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS)); try { trustEngine.validate(assertion.getSignature(), criteriaSet); } catch (SecurityException e) { throw new WSSecurityException(e.getMessage()); } } public static X509Credential getX509Credential(String sigPropFile, String callback) throws WSSecurityException { PrivateKey privateKey = null; X509Certificate[] certs = null; Crypto crypto = crypto = CryptoFactory.getInstance(sigPropFile); try { CallbackHandler cbhandler = getPasswordCallBack(callback); WSPasswordCallback cb = new WSPasswordCallback(crypto.getDefaultX509Alias(), WSPasswordCallback.SIGNATURE); cbhandler.handle(new Callback[] { cb }); privateKey = crypto.getPrivateKey(crypto.getDefaultX509Alias(), cb.getPassword()); certs = crypto.getCertificates(crypto.getDefaultX509Alias()); } catch (Exception e) { throw new WSSecurityException(e.getMessage()); } if (certs.length != 1) { throw new WSSecurityException("Couldn't get the (" + crypto.getDefaultX509Alias() + ") signature certificate."); } X509Credential cred = SecurityHelper.getSimpleCredential(certs[0], privateKey); return cred; } public static CallbackHandler getPasswordCallBack(String callback) throws WSSecurityException { CallbackHandler cbHandler = null; try { Class cbClass = Loader.loadClass(callback); cbHandler = (CallbackHandler) cbClass.newInstance(); } catch (Exception e) { throw new WSSecurityException("WSHandler: cannot create instance of password callback: " + callback, e); } return cbHandler; }}
No comments:
Post a Comment