All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.rahas.impl.SAMLTokenIssuer Maven / Gradle / Ivy

There is a newer version: 1.7.1
Show newest version
/*
 * Copyright 2004,2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.rahas.impl;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.soap.SOAPEnvelope;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.description.Parameter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.rahas.RahasConstants;
import org.apache.rahas.RahasData;
import org.apache.rahas.Token;
import org.apache.rahas.TokenIssuer;
import org.apache.rahas.TrustException;
import org.apache.rahas.TrustUtil;
import org.apache.rahas.impl.util.*;
import org.apache.ws.security.WSSecurityException;
import org.apache.ws.security.WSUsernameTokenPrincipal;
import org.apache.ws.security.components.crypto.Crypto;
import org.apache.ws.security.components.crypto.CryptoFactory;
import org.apache.ws.security.util.Loader;
import org.apache.ws.security.util.XmlSchemaDateFormat;

import org.joda.time.DateTime;
import org.opensaml.common.SAMLException;
import org.opensaml.saml1.core.*;
import org.opensaml.xml.signature.KeyInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.security.Principal;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Issuer to issue SAMl tokens
 */
public class SAMLTokenIssuer implements TokenIssuer {

    private String configParamName;

    private OMElement configElement;

    private String configFile;


    //TODO move this to TrustUtil
    private static final String  AUTHENTICATION_METHOD_PASSWORD = "urn:oasis:names:tc:SAML:1.0:am:password";

    private static final Log log = LogFactory.getLog(SAMLTokenIssuer.class);

    public SOAPEnvelope issue(RahasData data) throws TrustException {
        MessageContext inMsgCtx = data.getInMessageContext();

        SAMLTokenIssuerConfig config = null;
        if (this.configElement != null) {
            config = new SAMLTokenIssuerConfig(configElement
                            .getFirstChildWithName(SAMLTokenIssuerConfig.SAML_ISSUER_CONFIG));
        }

        // Look for the file
        if (config == null && this.configFile != null) {
            config = new SAMLTokenIssuerConfig(this.configFile);
        }

        // Look for the param
        if (config == null && this.configParamName != null) {
            Parameter param = inMsgCtx.getParameter(this.configParamName);
            if (param != null && param.getParameterElement() != null) {
                config = new SAMLTokenIssuerConfig(param
                        .getParameterElement().getFirstChildWithName(
                                SAMLTokenIssuerConfig.SAML_ISSUER_CONFIG));
            } else {
                throw new TrustException("expectedParameterMissing",
                        new String[] { this.configParamName });
            }
        }

        if (config == null) {
            throw new TrustException("configurationIsNull");
        }

        SOAPEnvelope env = TrustUtil.createSOAPEnvelope(inMsgCtx
                .getEnvelope().getNamespace().getNamespaceURI());

        Crypto crypto;
        if (config.cryptoElement != null) { // crypto props defined as elements
            crypto = CommonUtil.getCrypto(TrustUtil
                    .toProperties(config.cryptoElement), inMsgCtx
                    .getAxisService().getClassLoader());

        } else { // crypto props defined in a properties file
            crypto = CommonUtil.getCrypto(config.cryptoPropertiesFile, inMsgCtx
                    .getAxisService().getClassLoader());
        }

        // Creation and expiration times
        DateTime creationTime = new DateTime();
        DateTime expirationTime = new DateTime(creationTime.getMillis() + config.ttl);

        // Get the document
        Document doc = ((Element) env).getOwnerDocument();

        // Get the key size and create a new byte array of that size
        int keySize = data.getKeysize();

        keySize = (keySize == -1) ? config.keySize : keySize;

        /*
         * Find the KeyType If the KeyType is SymmetricKey or PublicKey,
         * issue a SAML HoK assertion. - In the case of the PublicKey, in
         * coming security header MUST contain a certificate (maybe via
         * signature)
         * 
         * If the KeyType is Bearer then issue a Bearer assertion
         * 
         * If the key type is missing we will issue a HoK assertion
         */

        String keyType = data.getKeyType();
        Assertion assertion;
        if (keyType == null) {
            throw new TrustException(TrustException.INVALID_REQUEST,
                    new String[] { "Requested KeyType is missing" });
        }

        if (keyType.endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)
                || keyType.endsWith(RahasConstants.KEY_TYPE_PUBLIC_KEY)) {
            assertion = createHoKAssertion(config, doc, crypto,
                    creationTime, expirationTime, data);
        } else if (keyType.endsWith(RahasConstants.KEY_TYPE_BEARER)) {
            assertion = createBearerAssertion(config, doc, crypto,
                    creationTime, expirationTime, data);
        } else {
            throw new TrustException("unsupportedKeyType");
        }

        OMElement rstrElem;
        int wstVersion = data.getVersion();
        if (RahasConstants.VERSION_05_02 == wstVersion) {
            rstrElem = TrustUtil.createRequestSecurityTokenResponseElement(
                    wstVersion, env.getBody());
        } else {
            OMElement rstrcElem = TrustUtil
                    .createRequestSecurityTokenResponseCollectionElement(
                            wstVersion, env.getBody());
            rstrElem = TrustUtil.createRequestSecurityTokenResponseElement(
                    wstVersion, rstrcElem);
        }

        TrustUtil.createTokenTypeElement(wstVersion, rstrElem).setText(
                RahasConstants.TOK_TYPE_SAML_10);

        if (keyType.endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) {
            TrustUtil.createKeySizeElement(wstVersion, rstrElem, keySize);
        }

        if (config.addRequestedAttachedRef) {
            TrustUtil.createRequestedAttachedRef(rstrElem, assertion.getID(),wstVersion);
        }

        if (config.addRequestedUnattachedRef) {
            TrustUtil.createRequestedUnattachedRef(rstrElem, assertion.getID(),wstVersion);
        }

        if (data.getAppliesToAddress() != null) {
            TrustUtil.createAppliesToElement(rstrElem, data
                    .getAppliesToAddress(), data.getAddressingNs());
        }

        // Use GMT time in milliseconds
        DateFormat zulu = new XmlSchemaDateFormat();

        // Add the Lifetime element
        TrustUtil.createLifetimeElement(wstVersion, rstrElem, zulu
                .format(creationTime.toDate()), zulu.format(expirationTime.toDate()));

        // Create the RequestedSecurityToken element and add the SAML token
        // to it
        OMElement reqSecTokenElem = TrustUtil
                .createRequestedSecurityTokenElement(wstVersion, rstrElem);
        Token assertionToken;
        //try {
            Node tempNode = assertion.getDOM();
            reqSecTokenElem.addChild((OMNode) ((Element) rstrElem)
                    .getOwnerDocument().importNode(tempNode, true));

            // Store the token
            assertionToken = new Token(assertion.getID(),
                    (OMElement) assertion.getDOM(), creationTime.toDate(),
                    expirationTime.toDate());

            // At this point we definitely have the secret
            // Otherwise it should fail with an exception earlier
            assertionToken.setSecret(data.getEphmeralKey());
            TrustUtil.getTokenStore(inMsgCtx).add(assertionToken);

       /* } catch (SAMLException e) {
            throw new TrustException("samlConverstionError", e);
        }*/

        if (keyType.endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)
                && config.keyComputation != SAMLTokenIssuerConfig.KeyComputation.KEY_COMP_USE_REQ_ENT) {

            // Add the RequestedProofToken
            TokenIssuerUtil.handleRequestedProofToken(data, wstVersion,
                    config, rstrElem, assertionToken, doc);
        }

        return env;
    }



    private Assertion createBearerAssertion(SAMLTokenIssuerConfig config,
                                            Document doc, Crypto crypto, DateTime creationTime,
                                            DateTime expirationTime, RahasData data) throws TrustException {

        Principal principal = data.getPrincipal();
        Assertion assertion;
        // In the case where the principal is a UT
        if (principal instanceof WSUsernameTokenPrincipal) {
            NameIdentifier nameId = null;
            if (config.getCallbackHandler() != null) {
                SAMLNameIdentifierCallback cb = new SAMLNameIdentifierCallback(data);
                cb.setUserId(principal.getName());
                SAMLCallbackHandler callbackHandler = config.getCallbackHandler();
                try {
                    callbackHandler.handle(cb);
                } catch (SAMLException e) {
                    throw new TrustException("unableToRetrieveCallbackHandler", e);
                }
                nameId = cb.getNameId();
            } else {

                nameId = SAMLUtils.createNamedIdentifier(principal.getName(), NameIdentifier.EMAIL);
            }

            assertion = createAuthAssertion(RahasConstants.SAML11_SUBJECT_CONFIRMATION_BEARER,
                    nameId, null, config, crypto, creationTime,
                    expirationTime, data);
            return assertion;
        } else {
            throw new TrustException("samlUnsupportedPrincipal",
                    new String[]{principal.getClass().getName()});
        }
    }

    private Assertion createHoKAssertion(SAMLTokenIssuerConfig config,
            Document doc, Crypto crypto, DateTime creationTime,
            DateTime expirationTime, RahasData data) throws TrustException {

        if (data.getKeyType().endsWith(RahasConstants.KEY_TYPE_SYMM_KEY)) {
            X509Certificate serviceCert = null;
            try {

                // TODO what if principal is null ?
                NameIdentifier nameIdentifier = null;
                if (data.getPrincipal() != null) {
                    String subjectNameId = data.getPrincipal().getName();
                    nameIdentifier =SAMLUtils.createNamedIdentifier(subjectNameId, NameIdentifier.EMAIL);
                }

                /**
                 * In this case we need to create a KeyInfo similar to following,
                 * *  
                 *     
                 *   
                 */

                // Get ApliesTo to figure out which service to issue the token
                // for
                serviceCert = getServiceCert(config, crypto, data
                        .getAppliesToAddress());

                // set keySize
                int keySize = data.getKeysize();
                keySize = (keySize != -1) ? keySize : config.keySize;

                // Create the encrypted key
                KeyInfo encryptedKeyInfoElement
                        = SAMLUtils.getSymmetricKeyBasedKeyInfo(doc, data, serviceCert, keySize,
                        crypto, config.keyComputation);

                return this.createAttributeAssertion(data, encryptedKeyInfoElement, nameIdentifier, config,
                    crypto, creationTime, expirationTime);


            } catch (WSSecurityException e) {

                if (serviceCert != null) {
                    throw new TrustException(
                            "errorInBuildingTheEncryptedKeyForPrincipal",
                            new String[]{serviceCert.getSubjectDN().getName()},
                            e);
                } else {
                    throw new TrustException(
                            "trustedCertNotFoundForEPR",
                            new String[]{data.getAppliesToAddress()},
                            e);
                }

            }
        } else {
            try {

                /**
                 * In this case we need to create KeyInfo as follows,
                 * 
                 *   
                 *        
                 *              MIICNTCCAZ6gAwIBAgIES343....
                 *           
                 *       
                 *   
                 */

                String subjectNameId = data.getPrincipal().getName();
                
                NameIdentifier nameId = SAMLUtils.createNamedIdentifier(subjectNameId, NameIdentifier.EMAIL);

                // Create the ds:KeyValue element with the ds:X509Data
                X509Certificate clientCert = data.getClientCert();

                if(clientCert == null) {
                    clientCert = CommonUtil.getCertificateByAlias(crypto,data.getPrincipal().getName());;
                }

                KeyInfo keyInfo = SAMLUtils.getCertificateBasedKeyInfo(clientCert);

                return this.createAuthAssertion(RahasConstants.SAML11_SUBJECT_CONFIRMATION_HOK, nameId, keyInfo,
                        config, crypto, creationTime, expirationTime, data);
            } catch (Exception e) {
                throw new TrustException("samlAssertionCreationError", e);
            }
        }
    }

    /**
     * Uses the wst:AppliesTo to figure out the certificate to
     * encrypt the secret in the SAML token
     * 
     * @param config Token issuer configuration.
     * @param crypto Crypto properties.
     * @param serviceAddress
     *            The address of the service
     * @return The X509 certificate.
     * @throws org.apache.rahas.TrustException If an error occurred while retrieving certificate from crypto.
     */
    private X509Certificate getServiceCert(SAMLTokenIssuerConfig config,
            Crypto crypto, String serviceAddress) throws TrustException {

        // TODO a duplicate method !!
        if (serviceAddress != null && !"".equals(serviceAddress)) {
            String alias = (String) config.trustedServices.get(serviceAddress);
            if (alias != null) {
                return CommonUtil.getCertificateByAlias(crypto,alias);
            } else {
                alias = (String) config.trustedServices.get("*");
                return CommonUtil.getCertificateByAlias(crypto,alias);
            }
        } else {
            String alias = (String) config.trustedServices.get("*");
            return CommonUtil.getCertificateByAlias(crypto,alias);
        }

    }

    /**
     * Create the SAML assertion with the secret held in an
     * xenc:EncryptedKey
     * @param data The Rahas configurations, this is needed to get the callbacks.
     * @param keyInfo OpenSAML KeyInfo representation.
     * @param subjectNameId Principal as an OpenSAML Subject
     * @param config SAML Token issuer configurations.
     * @param crypto To get certificate information.
     * @param notBefore Validity period start.
     * @param notAfter Validity period end
     * @return OpenSAML Assertion object.
     * @throws TrustException If an error occurred while creating the Assertion.
     */
    private Assertion createAttributeAssertion(RahasData data,
                                               KeyInfo keyInfo, NameIdentifier subjectNameId,
                                               SAMLTokenIssuerConfig config,
                                               Crypto crypto, DateTime notBefore, DateTime notAfter) throws TrustException {
        try {

            Subject subject
                    = SAMLUtils.createSubject(subjectNameId, RahasConstants.SAML11_SUBJECT_CONFIRMATION_HOK, keyInfo);

            Attribute[] attrs;
            if (config.getCallbackHandler() != null) {
                SAMLAttributeCallback cb = new SAMLAttributeCallback(data);
                SAMLCallbackHandler handler = config.getCallbackHandler();
                handler.handle(cb);
                attrs = cb.getAttributes();
            } else if (config.getCallbackHandlerName() != null
                    && config.getCallbackHandlerName().trim().length() > 0) {
                SAMLAttributeCallback cb = new SAMLAttributeCallback(data);
                SAMLCallbackHandler handler = null;
                MessageContext msgContext = data.getInMessageContext();
                ClassLoader classLoader = msgContext.getAxisService().getClassLoader();
                Class cbClass;
                try {
                    cbClass = Loader.loadClass(classLoader, config.getCallbackHandlerName());
                } catch (ClassNotFoundException e) {
                    throw new TrustException("cannotLoadPWCBClass", new String[]{config
                            .getCallbackHandlerName()}, e);
                }
                try {
                    handler = (SAMLCallbackHandler) cbClass.newInstance();
                } catch (java.lang.Exception e) {
                    throw new TrustException("cannotCreatePWCBInstance", new String[]{config
                            .getCallbackHandlerName()}, e);
                }
                handler.handle(cb);
                attrs = cb.getAttributes();
            } else {
                //TODO Remove this after discussing
                Attribute attribute = SAMLUtils.createAttribute("Name", "https://rahas.apache.org/saml/attrns",
                        "Colombo/Rahas");
                attrs = new Attribute[]{attribute};
            }

            AttributeStatement attributeStatement = SAMLUtils.createAttributeStatement(subject, Arrays.asList(attrs));


            List attributeStatements = new ArrayList();
            attributeStatements.add(attributeStatement);

            Assertion assertion = SAMLUtils.createAssertion(config.issuerName, notBefore,
                    notAfter, attributeStatements);

            SAMLUtils.signAssertion(assertion, crypto, config.getIssuerKeyAlias(), config.getIssuerKeyPassword());

            return assertion;
        } catch (Exception e) {
            throw new TrustException("samlAssertionCreationError", e);
        }
    }

    /**
     * Creates an authentication assertion.
     * @param confirmationMethod The confirmation method. (HOK, Bearer ...)
     * @param subjectNameId The principal name.
     * @param keyInfo OpenSAML representation of KeyInfo.
     * @param config Rahas configurations.
     * @param crypto Certificate information.
     * @param notBefore Validity start.
     * @param notAfter Validity end.
     * @param data Other Rahas data.
     * @return An openSAML Assertion.
     * @throws TrustException If an exception occurred while creating the Assertion.
     */
    private Assertion createAuthAssertion(String confirmationMethod,
            NameIdentifier subjectNameId, KeyInfo keyInfo,
            SAMLTokenIssuerConfig config, Crypto crypto, DateTime notBefore,
            DateTime notAfter, RahasData data) throws TrustException {
        try {

            Subject subject = SAMLUtils.createSubject(subjectNameId,confirmationMethod, keyInfo);

            AuthenticationStatement authenticationStatement
                    = SAMLUtils.createAuthenticationStatement(subject, AUTHENTICATION_METHOD_PASSWORD,
                    notBefore);

            List statements = new ArrayList();
            if (data.getClaimDialect() != null && data.getClaimElem() != null) {
                Statement attrStatement = createSAMLAttributeStatement(
                        SAMLUtils.createSubject(subject.getNameIdentifier(),
                                confirmationMethod, keyInfo), data, config);
                statements.add(attrStatement);
            }

            statements.add(authenticationStatement);

            Assertion assertion = SAMLUtils.createAssertion(config.issuerName,
                    notBefore, notAfter, statements);

            // Signing the assertion
            // The ... element appears only after
            // signing.
            SAMLUtils.signAssertion(assertion, crypto, config.getIssuerKeyAlias(), config.getIssuerKeyPassword());

            return assertion;
        } catch (Exception e) {
            throw new TrustException("samlAssertionCreationError", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public String getResponseAction(RahasData data) throws TrustException {
        return TrustUtil.getActionValue(data.getVersion(),
                RahasConstants.RSTR_ACTION_ISSUE);
    }

    /**
     * Create an ephemeral key
     * 
     * @return The generated key as a byte array
     * @throws TrustException
     */
    protected byte[] generateEphemeralKey(int keySize) throws TrustException {
        try {
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
            byte[] temp = new byte[keySize / 8];
            random.nextBytes(temp);
            return temp;
        } catch (Exception e) {
            throw new TrustException("Error in creating the ephemeral key", e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void setConfigurationFile(String configFile) {
        this.configFile = configFile;

    }
    
    /**
     * {@inheritDoc}
     */
    public void setConfigurationElement(OMElement configElement) {
        this.configElement = configElement;
    }

    /**
     * {@inheritDoc}
     */
    public void setConfigurationParamName(String configParamName) {
        this.configParamName = configParamName;
    }

    private AttributeStatement createSAMLAttributeStatement(Subject subject,
                                                            RahasData rahasData,
                                                            SAMLTokenIssuerConfig config)
            throws TrustException {
        Attribute[] attrs = null;
        if (config.getCallbackHandler() != null) {
            SAMLAttributeCallback cb = new SAMLAttributeCallback(rahasData);
            SAMLCallbackHandler handler = config.getCallbackHandler();
            try {
                handler.handle(cb);
                attrs = cb.getAttributes();
            } catch (SAMLException e) {
                throw new TrustException("unableToRetrieveCallbackHandler", e);
            }

        } else if (config.getCallbackHandlerName() != null
                && config.getCallbackHandlerName().trim().length() > 0) {
            SAMLAttributeCallback cb = new SAMLAttributeCallback(rahasData);
            SAMLCallbackHandler handler = null;
            MessageContext msgContext = rahasData.getInMessageContext();
            ClassLoader classLoader = msgContext.getAxisService().getClassLoader();
            Class cbClass = null;
            try {
                cbClass = Loader.loadClass(classLoader, config.getCallbackHandlerName());
            } catch (ClassNotFoundException e) {
                throw new TrustException("cannotLoadPWCBClass",
                        new String[]{config.getCallbackHandlerName()}, e);
            }
            try {
                handler = (SAMLCallbackHandler) cbClass.newInstance();
            } catch (Exception e) {
                throw new TrustException("cannotCreatePWCBInstance",
                        new String[]{config.getCallbackHandlerName()}, e);
            }
            try {
                handler.handle(cb);
            } catch (SAMLException e) {
                throw new TrustException("unableToRetrieveCallbackHandler", e);
            }
            attrs = cb.getAttributes();
        } else {
            //TODO Remove this after discussing
            Attribute attribute =
                    SAMLUtils.createAttribute("Name", "https://rahas.apache.org/saml/attrns", "Colombo/Rahas");

            attrs = new Attribute[]{attribute};
        }

        AttributeStatement attributeStatement = SAMLUtils.createAttributeStatement(subject, Arrays.asList(attrs));

        return attributeStatement;

    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy