be.agiv.security.client.SecureConversationClient.java Source code

Java tutorial

Introduction

Here is the source code for be.agiv.security.client.SecureConversationClient.java

Source

/*
 * AGIV Java Security Project.
 * Copyright (C) 2011-2012 AGIV.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.agiv.security.client;

import java.security.SecureRandom;
import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ws.security.conversation.ConversationException;
import org.apache.ws.security.conversation.dkalgo.P_SHA1;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.w3c.dom.Element;

import be.agiv.security.SecurityToken;
import be.agiv.security.handler.LoggingHandler;
import be.agiv.security.handler.WSAddressingHandler;
import be.agiv.security.handler.WSSecurityHandler;
import be.agiv.security.handler.WSTrustHandler;
import be.agiv.security.jaxb.wsse.ReferenceType;
import be.agiv.security.jaxb.wsse.SecurityTokenReferenceType;
import be.agiv.security.jaxb.wstrust.BinarySecretType;
import be.agiv.security.jaxb.wstrust.CancelTargetType;
import be.agiv.security.jaxb.wstrust.EntropyType;
import be.agiv.security.jaxb.wstrust.LifetimeType;
import be.agiv.security.jaxb.wstrust.ObjectFactory;
import be.agiv.security.jaxb.wstrust.RequestSecurityTokenResponseCollectionType;
import be.agiv.security.jaxb.wstrust.RequestSecurityTokenResponseType;
import be.agiv.security.jaxb.wstrust.RequestSecurityTokenType;
import be.agiv.security.jaxb.wstrust.RequestedReferenceType;
import be.agiv.security.jaxws.wstrust.SecurityTokenService;
import be.agiv.security.jaxws.wstrust.SecurityTokenService_Service;

/**
 * JAX-WS based WS-SecureConversation client. Via this client one can setup
 * secure conversations according to the WS-SecureConversation specification. A
 * conversation is presented via a secure conversation token.
 * 
 * @author Frank Cornelis
 * 
 */
public class SecureConversationClient {

    private static final Log LOG = LogFactory.getLog(SecureConversationClient.class);

    private final String location;

    private final SecurityTokenService port;

    private final ObjectFactory objectFactory;

    private final SecureRandom secureRandom;

    private final be.agiv.security.jaxb.wsse.ObjectFactory wssObjectFactory;

    private final WSAddressingHandler wsAddressingHandler;

    private final WSTrustHandler wsTrustHandler;

    private final WSSecurityHandler wsSecurityHandler;

    /**
     * Main constructor. The given location is the same as where the actual
     * business web service is running.
     * 
     * @param location
     *            the location of the WS-SecureConversation enabled web service.
     */
    public SecureConversationClient(String location) {
        this.location = location;
        SecurityTokenService_Service service = SecurityTokenServiceFactory.getInstance();
        this.port = service.getSecurityTokenServicePort();
        BindingProvider bindingProvider = (BindingProvider) this.port;
        bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, location);

        Binding binding = bindingProvider.getBinding();
        List<Handler> handlerChain = binding.getHandlerChain();
        this.wsTrustHandler = new WSTrustHandler();
        handlerChain.add(this.wsTrustHandler);
        this.wsAddressingHandler = new WSAddressingHandler();
        handlerChain.add(this.wsAddressingHandler);
        this.wsSecurityHandler = new WSSecurityHandler();
        handlerChain.add(this.wsSecurityHandler);
        handlerChain.add(new LoggingHandler());
        binding.setHandlerChain(handlerChain);

        this.objectFactory = new ObjectFactory();
        this.wssObjectFactory = new be.agiv.security.jaxb.wsse.ObjectFactory();

        this.secureRandom = new SecureRandom();
        this.secureRandom.setSeed(System.currentTimeMillis());
    }

    /**
     * Gives back a secure conversation token using the given R-STS security
     * token. The R-STS security token should apply to this web service.
     * 
     * @param rStsSecurityToken
     *            the R-STS security token.
     * @return the secure conversation token to be used to secure the web
     *         service calls.
     */
    public SecurityToken getSecureConversationToken(SecurityToken rStsSecurityToken) {
        RequestSecurityTokenType requestSecurityToken = this.objectFactory.createRequestSecurityTokenType();
        List<Object> requestSecurityTokenContent = requestSecurityToken.getAny();

        requestSecurityTokenContent
                .add(this.objectFactory.createTokenType(WSConstants.SECURE_CONVERSATION_TOKEN_TYPE));

        requestSecurityTokenContent.add(this.objectFactory.createRequestType(WSConstants.ISSUE_REQUEST_TYPE));

        EntropyType entropy = this.objectFactory.createEntropyType();
        requestSecurityTokenContent.add(this.objectFactory.createEntropy(entropy));
        BinarySecretType binarySecret = this.objectFactory.createBinarySecretType();
        entropy.getAny().add(this.objectFactory.createBinarySecret(binarySecret));
        binarySecret.setType(WSConstants.SECRET_TYPE_NONCE);
        byte[] entropyData = new byte[256 / 8];
        this.secureRandom.setSeed(System.currentTimeMillis());
        this.secureRandom.nextBytes(entropyData);
        binarySecret.setValue(entropyData);

        requestSecurityTokenContent.add(this.objectFactory.createKeySize(256L));

        BindingProvider bindingProvider = (BindingProvider) this.port;
        this.wsAddressingHandler.setAddressing(WSConstants.SEC_CONV_ISSUE_ACTION, this.location);
        this.wsSecurityHandler.setKey(rStsSecurityToken.getKey(), rStsSecurityToken.getAttachedReference(),
                rStsSecurityToken.getToken());

        RequestSecurityTokenResponseCollectionType requestSecurityTokenResponseCollection = this.port
                .requestSecurityToken(requestSecurityToken);

        SecurityToken securityToken = new SecurityToken();

        List<RequestSecurityTokenResponseType> requestSecurityTokenResponseList = requestSecurityTokenResponseCollection
                .getRequestSecurityTokenResponse();
        RequestSecurityTokenResponseType requestSecurityTokenResponse = requestSecurityTokenResponseList.get(0);
        List<Object> requestSecurityTokenResponseContent = requestSecurityTokenResponse.getAny();
        for (Object contentObject : requestSecurityTokenResponseContent) {
            LOG.debug("content object: " + contentObject.getClass().getName());
            if (contentObject instanceof Element) {
                Element contentElement = (Element) contentObject;
                LOG.debug("element name: " + contentElement.getLocalName());
            }
            if (contentObject instanceof JAXBElement) {
                JAXBElement jaxbElement = (JAXBElement) contentObject;
                QName qname = jaxbElement.getName();
                if (WSConstants.ENTROPY_QNAME.equals(qname)) {
                    LOG.debug("trust:Entropy");
                    EntropyType serverEntropy = (EntropyType) jaxbElement.getValue();
                    List<Object> entropyContent = serverEntropy.getAny();
                    for (Object entropyObject : entropyContent) {
                        if (entropyObject instanceof JAXBElement) {
                            JAXBElement entropyElement = (JAXBElement) entropyObject;
                            if (WSConstants.BINARY_SECRET_QNAME.equals(entropyElement.getName())) {
                                BinarySecretType serverBinarySecret = (BinarySecretType) entropyElement.getValue();
                                byte[] serverSecret = serverBinarySecret.getValue();
                                P_SHA1 p_SHA1 = new P_SHA1();
                                byte[] key;
                                try {
                                    key = p_SHA1.createKey(entropyData, serverSecret, 0, 256 / 8);
                                } catch (ConversationException e) {
                                    LOG.error(e);
                                    return null;
                                }
                                LOG.debug("client secret size: " + entropyData.length);
                                LOG.debug("server secret size: " + serverSecret.length);
                                LOG.debug("key size: " + key.length);
                                securityToken.setKey(key);
                            }
                        }
                    }
                } else if (WSConstants.LIFETIME_QNAME.equals(qname)) {
                    LOG.debug("trust:Lifetime");
                    LifetimeType lifetime = (LifetimeType) jaxbElement.getValue();
                    String createdValue = lifetime.getCreated().getValue();
                    DateTimeFormatter dateTimeFormatter = ISODateTimeFormat.dateTimeParser();
                    DateTime created = dateTimeFormatter.parseDateTime(createdValue);
                    securityToken.setCreated(created.toDate());
                    String expiresString = lifetime.getExpires().getValue();
                    DateTime expires = dateTimeFormatter.parseDateTime(expiresString);
                    securityToken.setExpires(expires.toDate());
                } else if (WSConstants.REQUESTED_ATTACHED_REFERENCE_QNAME.equals(qname)) {
                    RequestedReferenceType requestedReference = (RequestedReferenceType) jaxbElement.getValue();
                    SecurityTokenReferenceType securityTokenReference = requestedReference
                            .getSecurityTokenReference();
                    List<Object> securityTokenReferenceContent = securityTokenReference.getAny();
                    for (Object securityTokenReferenceObject : securityTokenReferenceContent) {
                        LOG.debug("SecurityTokenReference object: "
                                + securityTokenReferenceObject.getClass().getName());
                        if (securityTokenReferenceObject instanceof JAXBElement) {
                            JAXBElement securityTokenReferenceElement = (JAXBElement) securityTokenReferenceObject;
                            LOG.debug("SecurityTokenReference element: " + securityTokenReferenceElement.getName());
                            if (WSConstants.REFERENCE_QNAME.equals(securityTokenReferenceElement.getName())) {
                                ReferenceType reference = (ReferenceType) securityTokenReferenceElement.getValue();
                                String tokenIdentifier = reference.getURI().substring(1);
                                securityToken.setAttachedReference(tokenIdentifier);
                            }
                        }
                    }
                } else if (WSConstants.REQUESTED_UNATTACHED_REFERENCE_QNAME.equals(qname)) {
                    RequestedReferenceType requestedReference = (RequestedReferenceType) jaxbElement.getValue();
                    SecurityTokenReferenceType securityTokenReference = requestedReference
                            .getSecurityTokenReference();
                    List<Object> securityTokenReferenceContent = securityTokenReference.getAny();
                    for (Object securityTokenReferenceObject : securityTokenReferenceContent) {
                        LOG.debug("SecurityTokenReference object: "
                                + securityTokenReferenceObject.getClass().getName());
                        if (securityTokenReferenceObject instanceof JAXBElement) {
                            JAXBElement securityTokenReferenceElement = (JAXBElement) securityTokenReferenceObject;
                            LOG.debug("SecurityTokenReference element: " + securityTokenReferenceElement.getName());
                            if (WSConstants.REFERENCE_QNAME.equals(securityTokenReferenceElement.getName())) {
                                ReferenceType reference = (ReferenceType) securityTokenReferenceElement.getValue();
                                String tokenIdentifier = reference.getURI();
                                securityToken.setUnattachedReference(tokenIdentifier);
                            }
                        }
                    }
                }
            }
        }

        Element requestedSecurityToken = this.wsTrustHandler.getRequestedSecurityToken();
        securityToken.setToken(requestedSecurityToken);
        securityToken.setStsLocation(this.location);
        securityToken.setRealm(this.location); // what else?
        securityToken.setParentSecurityToken(rStsSecurityToken);

        return securityToken;
    }

    /**
     * Cancels a given secure conversation token for this WS-SecureConversation
     * enabled web service.
     * 
     * @param secureConversationToken
     */
    public void cancelSecureConversationToken(SecurityToken secureConversationToken) {
        RequestSecurityTokenType requestSecurityToken = this.objectFactory.createRequestSecurityTokenType();
        List<Object> requestSecurityTokenContent = requestSecurityToken.getAny();

        requestSecurityTokenContent.add(this.objectFactory.createRequestType(WSConstants.CANCEL_REQUEST_TYPE));

        CancelTargetType cancelTarget = this.objectFactory.createCancelTargetType();
        requestSecurityTokenContent.add(this.objectFactory.createCancelTarget(cancelTarget));
        SecurityTokenReferenceType securityTokenReference = this.wssObjectFactory
                .createSecurityTokenReferenceType();
        cancelTarget.setAny(this.wssObjectFactory.createSecurityTokenReference(securityTokenReference));
        ReferenceType reference = this.wssObjectFactory.createReferenceType();
        securityTokenReference.getAny().add(this.wssObjectFactory.createReference(reference));
        reference.setURI(secureConversationToken.getUnattachedReference());
        reference.setValueType(WSConstants.SECURE_CONVERSATION_TOKEN_TYPE);

        BindingProvider bindingProvider = (BindingProvider) this.port;
        this.wsAddressingHandler.setAddressing(WSConstants.SEC_CONV_CANCEL_ACTION, this.location);
        this.wsSecurityHandler.setKey(secureConversationToken.getKey(),
                secureConversationToken.getAttachedReference(), secureConversationToken.getToken(), false);

        RequestSecurityTokenResponseCollectionType requestSecurityTokenResponseCollection = this.port
                .requestSecurityToken(requestSecurityToken);

        List<RequestSecurityTokenResponseType> requestSecurityTokenResponseList = requestSecurityTokenResponseCollection
                .getRequestSecurityTokenResponse();
        RequestSecurityTokenResponseType requestSecurityTokenResponse = requestSecurityTokenResponseList.get(0);
        List<Object> requestSecurityTokenResponseContent = requestSecurityTokenResponse.getAny();
        boolean tokenCancelled = false;
        for (Object contentObject : requestSecurityTokenResponseContent) {
            LOG.debug("content object: " + contentObject.getClass().getName());
            if (contentObject instanceof JAXBElement) {
                JAXBElement jaxbElement = (JAXBElement) contentObject;
                QName qname = jaxbElement.getName();
                LOG.debug("qname: " + qname);
                if (WSConstants.REQUESTED_TOKEN_CANCELLED_QNAME.equals(qname)) {
                    tokenCancelled = true;
                }
            }
        }
        if (false == tokenCancelled) {
            throw new RuntimeException("token not cancelled");
        }
    }
}