WS-Security using Cert Authentification with Spring-WS II: Accessing the certificate

In the last episode, we’ve introduced the Security Interceptor and its Collaborators (KeyStoreHandler, Truststore, Security Policies) as the main Actors for activating Certificate Authentification along with an appropriate application context for configuring Spring-WS accordingly.
Now every incoming request message have to be signed by the Sender (using its private Key), which also implies that the Sender’s public Key has to be included within the SOAP envelope (in form of an appropriate Certificate which contains the Signers public Key, used to decrypt the clients digital Signature).

So far, the current configuration is causing our Security Interceptor to reject all request messages which doesn’t comply with that security constraints. Further, the transmitted client’s certificate is checked againt the Truststore in that it itself has to be signed (issued) by the CA we trust (by placing the trusted CAs certificate into the configured Truststore).
If an incoming request message meets all those requirements, it passes the Security Interception Process and can be received and consumed by an appropriate Message Endpoint (assumed that there are no other Endpoint Interceptors).

Authorisation

Now let’s suppose, that we need to get access to the client certificate for further processing (e.g. retrieving some of the certificate’s attributes). Let’s say that we need to retrieve the Common Name, which is registered within the client’s certificate for the sake of some custom authorisation: The Common Name should serve as input for deriving a set of rights which are permitted to the client (e.g. by associating the common name or a name pattern to a preconfigured set of rights). Since we are mainly interested on how to retrieve the client certificate, we will not dig into the authorisation process itself in greater detail (as it only serves as a valid example for motivating the retrievement of the client certificate).

Certificate Validation

To get access to the certificate, we have to register class SpringCertificateValidationCallbackHandler as another Callback Handler within our Security Interceptor:

...
<bean id="wsSecurityInterceptor"
      class="org.springframework.ws.soap.security.xwss.XwsSecurityInterceptor">
<property name="policyConfiguration" value="/WEB-INF/securityPolicy.xml"/>
<property name="callbackHandlers">
	<list>
      <ref bean="keyStoreHandler"/>
      <ref bean="certificateValidationHandler" />
    </list>
  </property>
</bean>

<bean id="certificateValidationHandler"
      class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
  ...
</bean>

As you can see, we place the new bean certificateValidationHandler right after the already registered KeyStoreCallbackHandler (which is checking the Signer of the client certificate against the trusted CA in our Truststore). Note, that this order is intented and of relevance – in this case, we will first check the certificate Signer against the CA in our Truststore (by building an accordant Certificate Chain). Only if this check is successful, our certificateValidationHandler will be called afterwards.

AuthenticationManager

SpringCertificateValidationCallbackHandler itself is only a delegator in our case – it will call a given AuthenticationManager if configured properly. For the purpose of retrieving the clients certificate, we’ll inject a custom implementation of AuthenticationManager:

...
<bean id="springCertificateHandler"
      class="org.springframework.ws.soap.security.xwss.callback.SpringCertificateValidationCallbackHandler">
<property name="authenticationManager">
    <bean class="com.mgi.authentication.SimpleAuthenticationManager" />
  </property>
</bean>

A valid AuthenticationManager have to implement a correspondend Interface. In this role, he gets called and passed an instance of type Authentication. In case of certificate Authentication, the provided credentials are of type X509Certificate – this is exactly the type of information we are looking for:

import java.security.cert.X509Certificate;
import org.springframework.security.Authentication;
import org.springframework.security.AuthenticationException;
import org.springframework.security.AuthenticationManager;

public class SimpleAuthenticationManager implements AuthenticationManager {

  public Authentication authenticate( Authentication authentication ) throws AuthenticationException {

    Object credentials = authentication.getCredentials();

    if( X509Certificate.class.isAssignableFrom( credentials.getClass() ) ){

      X509Certificate certificate = (X509Certificate) credentials;

      // do some custom authentication here
      setupRightsBasedOn( certificate );
    }

    authentication.setAuthenticated( true );

    return authentication;
  }
}

As you can see, we cast the given credentials (which could be any kind of information that prove the principal is correct, like a password in some other cases) to an instance of X509Certificate, do some authorization based on the given certificate and finally successfully complete authentication (you could of course come up with your own authentification logic, which might not always authenticate successfully in some cases).

X509Certificate

As we mentioned before, we might determine a set of rights based on the Common Name or any other attribute of the given certificate. In order to do this, we simply rely on the given API of java.security.cert.X509Certificate. Further, we might want to ‘publish’ the resulting set of rights, so that they can be retrieved by subsequent called business logic. One solution might be to instantiate a (maybe custom) SecurityContext (maybe using a ThreadLocal underneath, with all the given advantages and risks) which will accept the determined set of rights and provide them to all subsequent called business logic within the current Thread (since AuthenticationManager offers no way of directly returning a custom set of data):

...
private void setupRightsBasedOn( X509Certificate certificate ){

  X500Principal subjectX500Principal = certificate.getSubjectX500Principal();

  Rights rights = rightsFor( commonNameFrom( subjectX500Principal.getName() ) );

  SecurityContext.initContext( rights );
}

private String commonNameFrom( String subjectName ) {
  return
    StringUtils.extractNesting(
      subjectName, "CN=", ",",
      EXCLUDE_OPEN_BRACE_IN_NESTING,
      EXCLUDE_CLOSING_BRACE_IN_NESTING );
}

I’ve omitted the logic on how to retrieve the accordant set of rights for a given Common Name, since this is highly dependent by the given needs and infrastructure of the appclication. You might want to retrieve a database (since we are using Spring-WS you are free to inject any type of spring bean (e.g. a service or a DAO bean) to your custom implementation of AuthenticationManager – remember that the parent context of Spring-WS’ application context is automatically the given web application context) or calculate the rights by any meaningful logic.
You can clearly see, that we only rely on the given API of X509Certificate. Of course you are free to retrieve an arbitrary attribute from within the certificate.

Summary

Retrieving the client’s certificate is more or less a matter of providing a custom AuthenticationManager which itself gets called by a SpringCertificateValidationCallbackHandler (which is registered as another Callback Handler within our Security Interceptor). For having configured Certificate Authentification by the given Security policies, we’ll receive credentials in form of a X509Certificate. Retrieving some attributes from that certificate now is just a matter of using the provided API of X509Certificate in a suitable way – and that’s it.

In the next episode, we’ll take a closer look on how to set up the servers security infrastructure, including some step by step instructions on how to use OpenSSL and Keytool in order to create a CA Key-pair and importing the related CAs certificate into our Truststore. We’ll then move on to the client side in some further episode and will set up the client’s security infrastructure and of course come up with an implementation of a WebService-Client that will sign its request messages accordingly (also using Spring-WS) and call our secured WebService.

Advertisements

9 Responses to “WS-Security using Cert Authentification with Spring-WS II: Accessing the certificate”

  1. Security Service in South India Says:

    WS-Security using collaborators.main security services actor and activating certificate of the images media and polices security. good of the security services.

    tom

  2. abel Says:

    Thanks for this great post but I don’t know if it works because I can’t test it due to I don’t know how to make the client side, I’m waiting for the next episode to see some results.
    Thank you very much,
    Abel

  3. Markus Says:

    Thanks a lot for working out this topic. We use text credentials only .. might consider a certificate (if the WS-provider does too 😉 ).

  4. WS-Security using Cert Authentication with Spring-WS IV: How to set up your Clients’ Keystore « brain driven development Says:

    […] as the alias name for refering to that Key-Pair any time further. If you remember the second part of this tutorial, we did some authorization based on the Common Name of the client Certificate. So […]

  5. WS-Security using Cert Authentication with Spring-WS III: Setting up the Security infrastructure « brain driven development Says:

    […] the first episodes, we configured Spring-WS for rejecting incoming Messages which were sent from […]

  6. WS-Security using Cert Authentication with Spring-WS V: How to implement a Message Signing Client « brain driven development Says:

    […] Message Signing on the client side is also done by leveraging XWSS in form of an XWSSProcessor which is configured accordingly (using an appropriate Security Policy File). The Signing Process is encapsulated within a class of its own – MessageSigner. The client is able to use MessageSigner in that it request a WebServiceMessageCallback which in fact does the job of securing the outgoing message. In our case, according to the given Security Policies, a Hash is build based on the message payload (also including the Timestamp, which is also regarded by default, if not specified otherwise) which gets encrypted afterwards. Encryption is done by using the private Key of a Key-Pair, whose alias name is also defined within the Policy File – the underlying Keystore is configured via Spring-WS’ application config, which in turn is passed to the responsible XWSSProcessor. The encrypted Hash is in fact the digital Signature which is embedded within the outgoing message. In order to give the receiver a chance to detect some unwanted payload manipulation, it will also build a Hash, also based on the payload and Timestamp. To compare that Hash with the digital Signature which is embedded within the message, the receiver has to decrypt the Signature again. This can only be done with the related Public Key (contained within the clients certificate) which therefore also has to be passed to the receiver. Once you’ve retrieved the certificate (also embedded within the message), you can perform whatever kind of logic you want to do based on the client certificate (as seen in episode two). […]

  7. abc Says:

    thankx..
    will you plz post whole project here..

  8. Hans Says:

    Great article !
    You should be rewarded with a statue for this work.

    One small remark:
    the XwsSecurityInterceptor requires the SUN JDK.
    For deployment on application servers with an other JDK (for example Websphere), use the Wss4jSecurityInterceptor.

  9. bob Says:

    if( X509Certificate.class.isAssignableFrom( credentials.getClass() ) )
    should be written as
    credentials instanceof X509Certificate


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: