Monday, February 17, 2014

MEAN Flipboard Magazine

Obviously I have not updated this blog for a long time, but if you are interested in the MEAN stack (MongoDB, Express, AngularJS y Node.js) please do not forget to visit this Flipboard magazine.

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.

Monday, October 26, 2009

CXF, WSS4J & Spring Security Recipe


How to use Spring Security to authenticate a user/password web service implemented with CXF using WSS4J (Apache Java WS-Security):

In the Spring Application context, we define the namespaces, import CXF's xml and define the "Interceptor" to deal with security:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:cxf="http://cxf.apache.org/core"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
        http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd"
    >

    <!-- Load CXF modules from cxf.jar -->
    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <bean id="WSS4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
     <property name="properties">
      <map>
          <entry key="action" value="UsernameToken Timestamp"/>
          <entry key="passwordType" value="PasswordDigest"/>
          <entry key="passwordCallbackRef">
           <bean class="my.company.SecurityInPasswordHandler"/>
          </entry>
      </map>
     </property>
   </bean>
</beans>

And then we add the interceptor to the webservice:
<jaxws:endpoint id="salaWebService" implementor="#salaService" address="/salas">
    <jaxws:inInterceptors>
        <ref bean="WSS4JInInterceptor"/>
    </jaxws:inInterceptors>
</jaxws:endpoint>

Assuming we have the following service:
@WebService
@SOAPBinding
public interface SalaService {
    @WebResult(name = "sala")
    public abstract Sala get(@WebParam(name = "id") Long id);
}

@Service("salaService")
@WebService(serviceName = "SalaService", portName = "SalaPort", endpointInterface = "my.company.service.SalaService")
public class SalaServiceImpl implements SalaService {
    public Sala get(Long id) {
        return (Sala) executor.execute(SalaBusinessOperations.GET, new Long(id));
    }
}

And CXF configured in web.xml as follows:
<servlet>
    <servlet-name>CXFServlet</servlet-name>
    <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
</servlet>    
<servlet-mapping>
    <servlet-name>CXFServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
</servlet-mapping>

The spring security should be configured with something like this:
<http >
    <intercept-url pattern="/services/**" access="ROLE_ANONYMOUS" />
    <intercept-url pattern="/**" access="ROLE_USER" />
    <logout/>
    <anonymous/>
</http>

Finally we could write the interceptor class:
public class SecurityInPasswordHandler implements CallbackHandler {
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userService;

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException, AuthenticationException {

        WSPasswordCallback pwdCallback = (WSPasswordCallback) callbacks[0];

        int usage = pwdCallback.getUsage();
        if ((usage == WSPasswordCallback.USERNAME_TOKEN) || (usage == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN)) {
            String password = pwdCallback.getPassword();
            if (usage == WSPasswordCallback.USERNAME_TOKEN) {
                UserDetails userDetails = userService.loadUserByUsername(pwdCallback.getIdentifier());
                password = userDetails.getPassword();
            }
            Authentication authentication = new UsernamePasswordAuthenticationToken(pwdCallback.getIdentifier(), password);
            authentication = authenticationManager.authenticate(authentication); //throws AuthenticationException
            SecurityContextHolder.getContext().setAuthentication(authentication);
            // Return the password to the caller
            pwdCallback.setPassword(password);
        }
    }
}

Now we could handle plain (PasswordText) or encrypted (PasswordDigest) passwords. In both cases creating a SecurityContext for the request to have the user roles and use them in our Spring Security "@Secured" annotations.