Monday, April 26, 2010

Presenting on Semana Informática 2010


On April 27, my colleague Pedro Molina
and me will give a talk on frameworks and MDD. The talk will be during the Computer Week 2010: Development productivity and flexibility through frameworks and code generation event.

Having helped on many occasions with the development of frameworks for the most diverse institutions, the challenge is knowing how to convey the need for such frameworks, the complexity associated with their development and the productivity gains achieved.

But do not forget a fundamental part of what is intended to achieve with a framework: development flexibility. Is useless for us to standardize how to build applications, and provide very robust infrastructure services (That's a development framework, right?), if after every technology change we have to change the framework.

If after each technology change our framework becomes obsolete, the ability to adapt to the business needs are diminished, up to the point of becoming an obstacle to it. And our goal as software developers is to always be 100% aligned to business, not to act as an obstacle of change.

This is where the benefits of code generation are obvious. From Spring Roo, to model driven development, the way is "meta-programming".

How is it that after more than 30 years of computers, we're still devoting whole days to develop a simple table maintenance screen? How come we have not yet managed to say "HAL: build me a Customer table maintenance"?

I don't have the answer, but I know that 80% of the applications code is repeatable and can be modelled and generated, and we should put more effort into meta-programming and less into maintenances.

And I also know that I would like to spend my time developing the other 20%.

Thursday, April 08, 2010

OpenSAML signature verification


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 Map 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());">
With everything in place, we only have to code the interceptor, which in turn uses a utilities class:
public class MyInterceptor extends WSS4JInInterceptor {    private String issuer = "http://www.mycompany.com";    public MyInterceptor() {        super();    }    public MyInterceptor(Map 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;    }}
As 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.