eu.stork.peps.auth.engine.STORKSAMLEngine.java Source code

Java tutorial

Introduction

Here is the source code for eu.stork.peps.auth.engine.STORKSAMLEngine.java

Source

/* 
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
 * the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence. You may
 * obtain a copy of the Licence at:
 * 
 * http://www.osor.eu/eupl/european-union-public-licence-eupl-v.1.1
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * Licence for the specific language governing permissions and limitations under
 * the Licence.
 */

package eu.stork.peps.auth.engine;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.jce.X509Principal;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.common.Extensions;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.saml2.core.AttributeValue;
import org.opensaml.saml2.core.Audience;
import org.opensaml.saml2.core.AudienceRestriction;
import org.opensaml.saml2.core.AuthnContext;
import org.opensaml.saml2.core.AuthnContextDecl;
import org.opensaml.saml2.core.AuthnRequest;
import org.opensaml.saml2.core.AuthnStatement;
import org.opensaml.saml2.core.Conditions;
import org.opensaml.saml2.core.Issuer;
import org.opensaml.saml2.core.LogoutRequest;
import org.opensaml.saml2.core.LogoutResponse;
import org.opensaml.saml2.core.NameID;
import org.opensaml.saml2.core.OneTimeUse;
import org.opensaml.saml2.core.Response;
import org.opensaml.saml2.core.Status;
import org.opensaml.saml2.core.StatusCode;
import org.opensaml.saml2.core.StatusMessage;
import org.opensaml.saml2.core.Subject;
import org.opensaml.saml2.core.SubjectConfirmation;
import org.opensaml.saml2.core.SubjectConfirmationData;
import org.opensaml.saml2.core.SubjectLocality;
import org.opensaml.saml2.core.impl.SubjectConfirmationBuilder;
import org.opensaml.xml.Namespace;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.schema.XSAny;
import org.opensaml.xml.schema.impl.XSAnyBuilder;
import org.opensaml.xml.schema.impl.XSAnyImpl;
import org.opensaml.xml.schema.impl.XSAnyMarshaller;
import org.opensaml.xml.schema.impl.XSAnyUnmarshaller;
import org.opensaml.xml.schema.impl.XSStringImpl;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.validation.ValidationException;
import org.opensaml.xml.validation.Validator;
import org.opensaml.xml.validation.ValidatorSuite;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import eu.stork.peps.auth.commons.IPersonalAttributeList;
import eu.stork.peps.auth.commons.PersonalAttribute;
import eu.stork.peps.auth.commons.PersonalAttributeList;
import eu.stork.peps.auth.commons.STORKAttrQueryRequest;
import eu.stork.peps.auth.commons.STORKAttrQueryResponse;
import eu.stork.peps.auth.commons.STORKAuthnRequest;
import eu.stork.peps.auth.commons.STORKAuthnResponse;
import eu.stork.peps.auth.commons.STORKLogoutRequest;
import eu.stork.peps.auth.commons.STORKLogoutResponse;
import eu.stork.peps.auth.engine.core.AuthenticationAttributes;
import eu.stork.peps.auth.engine.core.CitizenCountryCode;
import eu.stork.peps.auth.engine.core.CustomAttributeQuery;
import eu.stork.peps.auth.engine.core.CustomRequestAbstractType;
import eu.stork.peps.auth.engine.core.EIDCrossBorderShare;
import eu.stork.peps.auth.engine.core.EIDCrossSectorShare;
import eu.stork.peps.auth.engine.core.EIDSectorShare;
import eu.stork.peps.auth.engine.core.QAAAttribute;
import eu.stork.peps.auth.engine.core.RequestedAttribute;
import eu.stork.peps.auth.engine.core.RequestedAttributes;
import eu.stork.peps.auth.engine.core.SAMLCore;
import eu.stork.peps.auth.engine.core.SPApplication;
import eu.stork.peps.auth.engine.core.SPCountry;
import eu.stork.peps.auth.engine.core.SPID;
import eu.stork.peps.auth.engine.core.SPInformation;
import eu.stork.peps.auth.engine.core.SPInstitution;
import eu.stork.peps.auth.engine.core.SPSector;
import eu.stork.peps.auth.engine.core.VIDPAuthenticationAttributes;
import eu.stork.peps.auth.engine.core.impl.AuthenticationAttributesBuilder;
import eu.stork.peps.auth.engine.core.impl.AuthenticationAttributesMarshaller;
import eu.stork.peps.auth.engine.core.impl.AuthenticationAttributesUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.CitizenCountryCodeBuilder;
import eu.stork.peps.auth.engine.core.impl.CitizenCountryCodeMarshaller;
import eu.stork.peps.auth.engine.core.impl.CitizenCountryCodeUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDCrossBorderShareBuilder;
import eu.stork.peps.auth.engine.core.impl.EIDCrossBorderShareMarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDCrossBorderShareUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDCrossSectorShareBuilder;
import eu.stork.peps.auth.engine.core.impl.EIDCrossSectorShareMarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDCrossSectorShareUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDSectorShareBuilder;
import eu.stork.peps.auth.engine.core.impl.EIDSectorShareMarshaller;
import eu.stork.peps.auth.engine.core.impl.EIDSectorShareUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.QAAAttributeBuilder;
import eu.stork.peps.auth.engine.core.impl.QAAAttributeMarshaller;
import eu.stork.peps.auth.engine.core.impl.QAAAttributeUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributeBuilder;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributeMarshaller;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributeUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributesBuilder;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributesMarshaller;
import eu.stork.peps.auth.engine.core.impl.RequestedAttributesUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPApplicationBuilder;
import eu.stork.peps.auth.engine.core.impl.SPApplicationMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPApplicationUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPCountryBuilder;
import eu.stork.peps.auth.engine.core.impl.SPCountryMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPCountryUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPIDBuilder;
import eu.stork.peps.auth.engine.core.impl.SPIDMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPIDUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPInformationBuilder;
import eu.stork.peps.auth.engine.core.impl.SPInformationMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPInformationUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPInstitutionBuilder;
import eu.stork.peps.auth.engine.core.impl.SPInstitutionMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPInstitutionUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.SPSectorBuilder;
import eu.stork.peps.auth.engine.core.impl.SPSectorMarshaller;
import eu.stork.peps.auth.engine.core.impl.SPSectorUnmarshaller;
import eu.stork.peps.auth.engine.core.impl.VIDPAuthenticationAttributesBuilder;
import eu.stork.peps.auth.engine.core.impl.VIDPAuthenticationAttributesMarshaller;
import eu.stork.peps.auth.engine.core.impl.VIDPAuthenticationAttributesUnmarshaller;
import eu.stork.peps.auth.engine.core.validator.CustomAttributeQueryValidator;
import eu.stork.peps.auth.engine.core.validator.ExtensionsSchemaValidator;
import eu.stork.peps.auth.engine.core.validator.MultipleAssertionResponseValidator;
import eu.stork.peps.auth.engine.core.validator.QAAAttributeSchemaValidator;
import eu.stork.peps.exceptions.SAMLEngineException;
import eu.stork.peps.exceptions.STORKSAMLEngineException;
import eu.stork.peps.exceptions.STORKSAMLEngineRuntimeException;

/**
 * Class that wraps the operations over SAML tokens, both generation and
 * validation of SAML STORK requests and SAML STORK responses. Complaint with
 * "OASIS Secure Assertion Markup Language (SAML) 2.0, May 2005", but taking
 * into account STORK specific requirements.
 * 
 * @author fjquevedo
 * @author iinigo
 */
public final class STORKSAMLEngine extends SAMLEngine {

    /** The Constant LOG. */
    private static final Logger LOG = LoggerFactory.getLogger(STORKSAMLEngine.class.getName());

    private static final String ATTRIBUTE_EMPTY_LITERAL = "Attribute name is null or empty.";

    /**
     * Gets the single instance of STORKSAMLEngine.
     * 
     * @param nameInstance the name instance
     * 
     * @return single instance of STORKSAMLEngine
     */
    public static synchronized STORKSAMLEngine getInstance(final String nameInstance) {
        STORKSAMLEngine engine = null;
        LOG.info("Get instance: " + nameInstance);
        try {
            engine = new STORKSAMLEngine(nameInstance.trim());
        } catch (Exception e) {
            LOG.error("Error getting instance: " + nameInstance);
            e.printStackTrace();
        }
        return engine;
    }

    /**
     * Instantiate a new STORKSAML engine.
     * 
     * @param nameInstance the name instance
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private STORKSAMLEngine(final String nameInstance) throws STORKSAMLEngineException {
        // Initialization OpenSAML.
        super(nameInstance);
        LOG.info("Register STORK objects provider.");
        Configuration.registerObjectProvider(QAAAttribute.DEF_ELEMENT_NAME, new QAAAttributeBuilder(),
                new QAAAttributeMarshaller(), new QAAAttributeUnmarshaller());

        Configuration.registerObjectProvider(EIDSectorShare.DEF_ELEMENT_NAME, new EIDSectorShareBuilder(),
                new EIDSectorShareMarshaller(), new EIDSectorShareUnmarshaller());

        Configuration.registerObjectProvider(EIDCrossSectorShare.DEF_ELEMENT_NAME, new EIDCrossSectorShareBuilder(),
                new EIDCrossSectorShareMarshaller(), new EIDCrossSectorShareUnmarshaller());

        Configuration.registerObjectProvider(EIDCrossBorderShare.DEF_ELEMENT_NAME, new EIDCrossBorderShareBuilder(),
                new EIDCrossBorderShareMarshaller(), new EIDCrossBorderShareUnmarshaller());

        Configuration.registerObjectProvider(SPSector.DEF_ELEMENT_NAME, new SPSectorBuilder(),
                new SPSectorMarshaller(), new SPSectorUnmarshaller());

        Configuration.registerObjectProvider(SPInstitution.DEF_ELEMENT_NAME, new SPInstitutionBuilder(),
                new SPInstitutionMarshaller(), new SPInstitutionUnmarshaller());

        Configuration.registerObjectProvider(SPApplication.DEF_ELEMENT_NAME, new SPApplicationBuilder(),
                new SPApplicationMarshaller(), new SPApplicationUnmarshaller());

        Configuration.registerObjectProvider(SPCountry.DEF_ELEMENT_NAME, new SPCountryBuilder(),
                new SPCountryMarshaller(), new SPCountryUnmarshaller());

        Configuration.registerObjectProvider(XSAny.TYPE_NAME, new XSAnyBuilder(), new XSAnyMarshaller(),
                new XSAnyUnmarshaller());

        Configuration.registerObjectProvider(RequestedAttribute.DEF_ELEMENT_NAME, new RequestedAttributeBuilder(),
                new RequestedAttributeMarshaller(), new RequestedAttributeUnmarshaller());

        Configuration.registerObjectProvider(RequestedAttributes.DEF_ELEMENT_NAME, new RequestedAttributesBuilder(),
                new RequestedAttributesMarshaller(), new RequestedAttributesUnmarshaller());

        Configuration.registerObjectProvider(AuthenticationAttributes.DEF_ELEMENT_NAME,
                new AuthenticationAttributesBuilder(), new AuthenticationAttributesMarshaller(),
                new AuthenticationAttributesUnmarshaller());

        Configuration.registerObjectProvider(VIDPAuthenticationAttributes.DEF_ELEMENT_NAME,
                new VIDPAuthenticationAttributesBuilder(), new VIDPAuthenticationAttributesMarshaller(),
                new VIDPAuthenticationAttributesUnmarshaller());

        Configuration.registerObjectProvider(CitizenCountryCode.DEF_ELEMENT_NAME, new CitizenCountryCodeBuilder(),
                new CitizenCountryCodeMarshaller(), new CitizenCountryCodeUnmarshaller());

        Configuration.registerObjectProvider(SPID.DEF_ELEMENT_NAME, new SPIDBuilder(), new SPIDMarshaller(),
                new SPIDUnmarshaller());

        Configuration.registerObjectProvider(SPInformation.DEF_ELEMENT_NAME, new SPInformationBuilder(),
                new SPInformationMarshaller(), new SPInformationUnmarshaller());

        LOG.info("Register STORK object validators.");
        final ValidatorSuite validatorSuite = new ValidatorSuite(QAAAttribute.DEF_LOCAL_NAME);

        validatorSuite.registerValidator(QAAAttribute.DEF_ELEMENT_NAME, new QAAAttributeSchemaValidator());
        final Extensions extensions = SAMLEngineUtils.generateExtension();
        validatorSuite.registerValidator(extensions.getElementQName(), new ExtensionsSchemaValidator());

        Configuration.registerValidatorSuite("stork:QualityAuthenticationAssuranceLevel", validatorSuite);

    }

    /**
     * Generate authentication response base.
     * 
     * @param status the status
     * @param assertConsumerURL the assert consumer URL.
     * @param inResponseTo the in response to
     * 
     * @return the response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Response genAuthnRespBase(final Status status, final String assertConsumerURL,
            final String inResponseTo) throws STORKSAMLEngineException {
        LOG.debug("Generate Authentication Response base.");
        final Response response = SAMLEngineUtils.generateResponse(SAMLVersion.VERSION_20,
                SAMLEngineUtils.generateNCName(), SAMLEngineUtils.getCurrentTime(), status);

        // Set name Spaces
        this.setNameSpaces(response);

        // Mandatory STORK
        LOG.debug("Generate Issuer");
        final Issuer issuer = SAMLEngineUtils.generateIssuer();
        issuer.setValue(super.getSamlCoreProperties().getResponder());

        // Format Entity Optional STORK
        issuer.setFormat(super.getSamlCoreProperties().getFormatEntity());

        response.setIssuer(issuer);

        // destination Mandatory Stork
        response.setDestination(assertConsumerURL.trim());

        // inResponseTo Mandatory Stork
        response.setInResponseTo(inResponseTo.trim());

        // Optional STORK
        response.setConsent(super.getSamlCoreProperties().getConsentAuthnResponse());

        return response;
    }

    /**
     * Generate attribute query response base.
     * 
     * @param status the status
     * @param destinationURL the assert consumer URL.
     * @param inResponseTo the in response to
     * 
     * @return the response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Response genAttrQueryRespBase(final Status status, final String destinationURL,
            final String inResponseTo) throws STORKSAMLEngineException {
        LOG.debug("Generate Attribute query Response base.");
        final Response response = SAMLEngineUtils.generateResponse(SAMLVersion.VERSION_20,
                SAMLEngineUtils.generateNCName(), SAMLEngineUtils.getCurrentTime(), status);

        // Set name Spaces
        this.setNameSpaces(response);

        // Mandatory STORK
        LOG.debug("Generate Issuer");
        final Issuer issuer = SAMLEngineUtils.generateIssuer();
        issuer.setValue(super.getSamlCoreProperties().getResponder());

        // Format Entity Optional STORK
        issuer.setFormat(super.getSamlCoreProperties().getFormatEntity());

        response.setIssuer(issuer);

        // destination Mandatory Stork
        response.setDestination(destinationURL.trim());

        // inResponseTo Mandatory Stork
        response.setInResponseTo(inResponseTo.trim());

        // Optional STORK
        response.setConsent(super.getSamlCoreProperties().getConsentAuthnResponse());

        return response;
    }

    /**
     * Generate assertion.
     * 
     * @param ipAddress the IP address.
     * @param assertConsumerURL the assert consumer URL.
     * @param inResponseTo the in response to
     * @param issuer the issuer
     * @param notOnOrAfter the not on or after
     * 
     * @return the assertion
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Assertion generateAssertion(final String ipAddress, final String assertConsumerURL,
            final String inResponseTo, final String issuer, final DateTime notOnOrAfter)
            throws STORKSAMLEngineException {
        LOG.info("Generate Assertion.");

        // Mandatory STORK
        LOG.debug("Generate Issuer to Assertion");
        final Issuer issuerAssertion = SAMLEngineUtils.generateIssuer();
        issuerAssertion.setValue(super.getSamlCoreProperties().getResponder());

        // Format Entity Optional STORK
        issuerAssertion.setFormat(super.getSamlCoreProperties().getFormatEntity());

        final Assertion assertion = SAMLEngineUtils.generateAssertion(SAMLVersion.VERSION_20,
                SAMLEngineUtils.generateNCName(), SAMLEngineUtils.getCurrentTime(), issuerAssertion);

        final Subject subject = SAMLEngineUtils.generateSubject();

        // Mandatory STORK verified
        // String format = NameID.UNSPECIFIED
        // specification: 'SAML:2.0' exist
        // opensaml: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
        // opensaml  "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified"
        final String format = "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified";

        final String nameQualifier = "";

        LOG.debug("Generate NameID");
        final NameID nameId = SAMLEngineUtils.generateNameID(super.getSamlCoreProperties().getResponder(), format,
                nameQualifier);
        nameId.setValue(format);
        subject.setNameID(nameId);

        // Mandatory if urn:oasis:names:tc:SAML:2.0:cm:bearer.
        // Optional in other case.
        LOG.debug("Generate SubjectConfirmationData.");
        final SubjectConfirmationData dataBearer = SAMLEngineUtils
                .generateSubjectConfirmationData(SAMLEngineUtils.getCurrentTime(), assertConsumerURL, inResponseTo);

        // Mandatory if urn:oasis:names:tc:SAML:2.0:cm:bearer.
        // Optional in other case.
        LOG.debug("Generate SubjectConfirmation");
        final SubjectConfirmation subjectConf = SAMLEngineUtils
                .generateSubjectConfirmation(SubjectConfirmation.METHOD_BEARER, dataBearer);

        final ArrayList<SubjectConfirmation> listSubjectConf = new ArrayList<SubjectConfirmation>();
        listSubjectConf.add(subjectConf);

        for (final Iterator<SubjectConfirmation> iter = listSubjectConf.iterator(); iter.hasNext();) {
            final SubjectConfirmation element = iter.next();

            if (SubjectConfirmation.METHOD_BEARER.equals(element.getMethod())) {
                // ipAddress Mandatory if method is Bearer.

                if (StringUtils.isBlank(ipAddress)) {
                    throw new STORKSAMLEngineException("ipAddress is null or empty");
                }
                element.getSubjectConfirmationData().setAddress(ipAddress.trim());
            }

            element.getSubjectConfirmationData().setRecipient(assertConsumerURL);
            element.getSubjectConfirmationData().setNotOnOrAfter(notOnOrAfter);
        }

        // The SAML 2.0 specification allows multiple SubjectConfirmations
        subject.getSubjectConfirmations().addAll(listSubjectConf);

        // Mandatory Stork
        assertion.setSubject(subject);

        // Conditions that MUST be evaluated when assessing the validity of
        // and/or when using the assertion.
        final Conditions conditions = this.generateConditions(SAMLEngineUtils.getCurrentTime(), notOnOrAfter,
                issuer);

        assertion.setConditions(conditions);

        LOG.debug("Generate stork Authentication Statement.");
        final AuthnStatement storkAuthnStat = this.generateStorkAuthStatement(ipAddress);
        assertion.getAuthnStatements().add(storkAuthnStat);

        return assertion;
    }

    private String getAttributeName(final PersonalAttribute attribute) throws STORKSAMLEngineException {
        if (StringUtils.isBlank(attribute.getName())) {
            LOG.error(ATTRIBUTE_EMPTY_LITERAL);
            throw new STORKSAMLEngineException(ATTRIBUTE_EMPTY_LITERAL);
        }

        final String attributeName = super.getSamlCoreProperties().getProperty(attribute.getName());

        if (StringUtils.isBlank(attributeName)) {
            LOG.error("Attribute name: {} it is not known.", attribute.getName());
            throw new STORKSAMLEngineException("Attribute name: " + attribute.getName() + " it is not known.");
        }
        return attributeName;
    }

    /**
     * Generate attribute statement.
     * 
     * @param personalAttrList the personal attribute list
     * @param isHashing the is hashing
     * 
     * @return the attribute statement
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     * @throws IOException
     */
    private AttributeStatement generateAttributeStatement(final IPersonalAttributeList personalAttrList,
            final boolean isHashing) throws STORKSAMLEngineException {
        LOG.debug("Generate attribute statement");

        final AttributeStatement attrStatement = (AttributeStatement) SAMLEngineUtils
                .createSamlObject(AttributeStatement.DEFAULT_ELEMENT_NAME);

        for (PersonalAttribute attribute : personalAttrList) {

            String attributeName = getAttributeName(attribute);

            // Verification that only one value it's permitted, simple or
            // complex, not both.

            final boolean simpleNull = (attribute.getValue() == null);
            final boolean simpleEmpty = (simpleNull || (!simpleNull && attribute.getValue().isEmpty()));

            final boolean complexNull = (attribute.getComplexValue() == null);
            final boolean complexEmpty = (complexNull || (!complexNull && attribute.getComplexValue().isEmpty()));

            if ((!simpleEmpty && !complexEmpty)) {
                throw new STORKSAMLEngineException("Attribute name: " + attribute.getName()
                        + " must be contain one value, simple or complex value.");
            } else {

                if (!simpleEmpty) {
                    attrStatement.getAttributes().add(this.generateAttrSimple(attributeName, attribute.getStatus(),
                            attribute.getValue(), isHashing));
                } else if (!complexEmpty) {
                    attrStatement.getAttributes().add(SAMLEngineUtils.generateAttrComplex(attributeName,
                            attribute.getStatus(), attribute.getComplexValue(), isHashing));
                } else if (!simpleNull) {
                    attrStatement.getAttributes().add(this.generateAttrSimple(attributeName, attribute.getStatus(),
                            new ArrayList<String>(), isHashing));
                } else {
                    // Add attribute complex.
                    attrStatement.getAttributes().add(SAMLEngineUtils.generateAttrComplex(attributeName,
                            attribute.getStatus(), new HashMap<String, String>(), isHashing));
                }
            }
        }
        return attrStatement;
    }

    private XSAny createAttributeValueForSignedDoc(final String value, final boolean isHashing)
            throws STORKSAMLEngineException {
        DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
        domFactory.setNamespaceAware(true);
        Document document = null;
        DocumentBuilder builder;

        // Parse the signedDoc value into an XML DOM Document
        try {
            builder = domFactory.newDocumentBuilder();
            InputStream is;
            is = new ByteArrayInputStream(value.trim().getBytes("UTF-8"));
            document = builder.parse(is);
            is.close();
        } catch (SAXException e1) {
            LOG.error("SAX Error while parsing signModule attribute", e1);
            throw new STORKSAMLEngineRuntimeException(e1);
        } catch (ParserConfigurationException e2) {
            LOG.error("Parser Configuration Error while parsing signModule attribute", e2);
            throw new STORKSAMLEngineRuntimeException(e2);
        } catch (UnsupportedEncodingException e3) {
            LOG.error("Unsupported encoding Error while parsing signModule attribute", e3);
            throw new STORKSAMLEngineRuntimeException(e3);
        } catch (IOException e4) {
            LOG.error("IO Error while parsing signModule attribute", e4);
            throw new STORKSAMLEngineRuntimeException(e4);
        }

        // Create the attribute statement
        final XSAny xmlValue = (XSAny) SAMLEngineUtils.createSamlObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                XSAny.TYPE_NAME);

        //Set the signedDoc XML content to this element
        xmlValue.setDOM(document.getDocumentElement());

        // Create the attribute statement
        final XSAny attrValue = (XSAny) SAMLEngineUtils.createSamlObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                XSAny.TYPE_NAME);

        //Add previous signedDocXML to the AttributeValue Element

        // if it's necessary encode the information.
        if (!isHashing) {
            attrValue.getUnknownXMLObjects().add(xmlValue);
        }
        return attrValue;
    }

    private XSAny createAttributeValueForNonSignedDoc(final String value, final boolean isHashing)
            throws STORKSAMLEngineException {
        // Create the attribute statement
        final XSAny attrValue = (XSAny) SAMLEngineUtils.createSamlObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                XSAny.TYPE_NAME);
        // if it's necessary encode the information.
        if (isHashing) {
            attrValue.setTextContent(SAMLEngineUtils.encode(value, SAMLEngineUtils.SHA_512));
        } else {
            attrValue.setTextContent(value);
        }
        return attrValue;
    }

    /**
     * Generate attribute from a list of values.
     * 
     * @param name the name of the attribute.
     * @param values the value of the attribute.
     * @param isHashing the is hashing with "SHA-512" algorithm.
     * @param status the status of the parameter: "Available", "NotAvailable" or
     *            "Withheld".
     * 
     * @return the attribute
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Attribute generateAttrSimple(final String name, final String status, final List<String> values,
            final boolean isHashing) throws STORKSAMLEngineException {
        LOG.debug("Generate attribute simple: " + name);
        final Attribute attribute = (Attribute) SAMLEngineUtils.createSamlObject(Attribute.DEFAULT_ELEMENT_NAME);

        attribute.setName(name);
        attribute.setNameFormat(Attribute.URI_REFERENCE);

        attribute.getUnknownAttributes().put(
                new QName(SAMLCore.STORK10_NS.getValue(), "AttributeStatus", SAMLCore.STORK10_PREFIX.getValue()),
                status);

        if (values != null) {
            LOG.debug("Add attribute values.");
            for (int i = 0; i < values.size(); i++) {
                final String value = values.get(i);
                if (StringUtils.isNotBlank(value)) {
                    XSAny attrValue = null;
                    if (!name.equals("http://www.stork.gov.eu/1.0/signedDoc")) {
                        // Create the attribute statement
                        attrValue = createAttributeValueForNonSignedDoc(value, isHashing);

                    } else {
                        attrValue = createAttributeValueForSignedDoc(value, isHashing);
                        attribute.getAttributeValues().add(attrValue);
                    }
                    attribute.getAttributeValues().add(attrValue);
                }
            }
        }
        return attribute;
    }

    /**
     * Generate conditions that MUST be evaluated when assessing the validity of
     * and/or when using the assertion.
     * 
     * @param notBefore the not before
     * @param notOnOrAfter the not on or after
     * @param audienceURI the audience URI.
     * 
     * @return the conditions
     */
    private Conditions generateConditions(final DateTime notBefore, final DateTime notOnOrAfter,
            final String audienceURI) {
        LOG.debug("Generate conditions.");
        final Conditions conditions = (Conditions) SAMLEngineUtils
                .createSamlObject(Conditions.DEFAULT_ELEMENT_NAME);
        conditions.setNotBefore(notBefore);
        conditions.setNotOnOrAfter(notOnOrAfter);

        final AudienceRestriction restrictions = (AudienceRestriction) SAMLEngineUtils
                .createSamlObject(AudienceRestriction.DEFAULT_ELEMENT_NAME);

        final Audience audience = (Audience) SAMLEngineUtils.createSamlObject(Audience.DEFAULT_ELEMENT_NAME);
        audience.setAudienceURI(audienceURI);

        restrictions.getAudiences().add(audience);
        conditions.getAudienceRestrictions().add(restrictions);

        if (super.getSamlCoreProperties().isOneTimeUse()) {
            final OneTimeUse oneTimeUse = (OneTimeUse) SAMLEngineUtils
                    .createSamlObject(OneTimeUse.DEFAULT_ELEMENT_NAME);
            conditions.getConditions().add(oneTimeUse);
        }
        return conditions;
    }

    /**
     * Generate personal attribute list.
     * 
     * @param assertion the assertion
     * 
     * @return the personal attribute list
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private IPersonalAttributeList generatePersonalAttributeList(final Assertion assertion)
            throws STORKSAMLEngineException {
        LOG.debug("Generate personal attribute list from XMLObject.");
        final List<XMLObject> listExtensions = assertion.getOrderedChildren();

        boolean find = false;
        AttributeStatement requestedAttr = null;

        // Search the attribute statement.
        for (int i = 0; i < listExtensions.size() && !find; i++) {
            final XMLObject xml = listExtensions.get(i);
            if (xml instanceof AttributeStatement) {
                requestedAttr = (AttributeStatement) xml;
                find = true;
            }
        }

        if (!find) {
            LOG.error("Error: AttributeStatement it's not present.");
            throw new STORKSAMLEngineException("AttributeStatement it's not present.");
        }

        final List<Attribute> reqAttrs = requestedAttr.getAttributes();

        final IPersonalAttributeList personalAttrList = new PersonalAttributeList();
        String attributeName;

        // Process the attributes.
        for (int nextAttribute = 0; nextAttribute < reqAttrs.size(); nextAttribute++) {
            final Attribute attribute = reqAttrs.get(nextAttribute);

            final PersonalAttribute personalAttribute = new PersonalAttribute();

            attributeName = attribute.getName();
            personalAttribute.setName(attributeName.substring(attributeName.lastIndexOf('/') + 1));

            personalAttribute
                    .setStatus(attribute.getUnknownAttributes().get(new QName(SAMLCore.STORK10_NS.getValue(),
                            "AttributeStatus", SAMLCore.STORK10_PREFIX.getValue())));

            final ArrayList<String> simpleValues = new ArrayList<String>();
            final HashMap<String, String> multiValues = new HashMap<String, String>();

            final List<XMLObject> values = attribute.getOrderedChildren();

            // Process the values.
            for (int nextValue = 0; nextValue < values.size(); nextValue++) {

                final XMLObject xmlObject = values.get(nextValue);

                if (xmlObject instanceof XSStringImpl) {

                    simpleValues.add(((XSStringImpl) xmlObject).getValue());

                } else if (xmlObject instanceof XSAnyImpl) {

                    if (attributeName.equals("http://www.stork.gov.eu/1.0/signedDoc")) {

                        final XSAnyImpl xmlString = (XSAnyImpl) values.get(nextValue);

                        TransformerFactory transFactory = TransformerFactory.newInstance();
                        Transformer transformer = null;
                        try {
                            transformer = transFactory.newTransformer();
                            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                        } catch (TransformerConfigurationException e) {
                            LOG.error("Error transformer configuration exception", e);
                        }
                        StringWriter buffer = new StringWriter();
                        try {
                            if (xmlString != null && xmlString.getUnknownXMLObjects() != null
                                    && xmlString.getUnknownXMLObjects().size() > 0) {
                                transformer.transform(
                                        new DOMSource(xmlString.getUnknownXMLObjects().get(0).getDOM()),
                                        new StreamResult(buffer));
                            }
                        } catch (TransformerException e) {
                            LOG.error("Error transformer exception", e);
                        }
                        String str = buffer.toString();

                        simpleValues.add(str);

                    } else if (isComplex(xmlObject)) {
                        LOG.info(attributeName + " found");
                        // Process complex value.
                        final XSAnyImpl complexValue = (XSAnyImpl) xmlObject;

                        for (int nextComplexValue = 0; nextComplexValue < complexValue.getUnknownXMLObjects()
                                .size(); nextComplexValue++) {

                            final XSAnyImpl simple = (XSAnyImpl) complexValue.getUnknownXMLObjects()
                                    .get(nextComplexValue);

                            multiValues.put(simple.getElementQName().getLocalPart(), simple.getTextContent());
                        }

                    } else {
                        // Process simple value.
                        simpleValues.add(((XSAnyImpl) xmlObject).getTextContent());
                    }

                } else {
                    LOG.error("Error: attribute value it's unknown.");
                    throw new STORKSAMLEngineException("Attribute value it's unknown.");
                }
            }

            personalAttribute.setValue(simpleValues);
            personalAttribute.setComplexValue(multiValues);
            personalAttrList.add(personalAttribute);
        }

        return personalAttrList;
    }

    /**
     * Generate stork authentication request.
     * 
     * @param request the request that contain all parameters for generate an
     *            authentication request.
     * 
     * @return the STORK authentication request that has been processed.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnRequest generateSTORKAuthnRequest(final STORKAuthnRequest request)
            throws STORKSAMLEngineException {
        LOG.info("Generate SAMLAuthnRequest.");

        // Validate Parameters mandatories
        validateParamAuthnReq(request);

        final AuthnRequest authnRequestAux = SAMLEngineUtils.generateSAMLAuthnRequest(
                SAMLEngineUtils.generateNCName(), SAMLVersion.VERSION_20, SAMLEngineUtils.getCurrentTime());

        // Set name spaces.
        setNameSpaces(authnRequestAux);

        // Add parameter Mandatory STORK
        authnRequestAux.setForceAuthn(Boolean.TRUE);

        // Add parameter Mandatory STORK
        authnRequestAux.setIsPassive(Boolean.FALSE);

        authnRequestAux.setAssertionConsumerServiceURL(request.getAssertionConsumerServiceURL());

        authnRequestAux.setProviderName(request.getProviderName());

        // Add protocol binding
        authnRequestAux.setProtocolBinding(super.getSamlCoreProperties().getProtocolBinding());

        // Add parameter optional STORK
        // Destination is mandatory if the destination is a C-PEPS
        // The application must to know if the destination is a C-PEPS.
        if (StringUtils.isNotBlank(request.getDestination())) {
            authnRequestAux.setDestination(request.getDestination());
        }

        // Consent is optional. Set from SAMLEngine.xml - consent.
        authnRequestAux.setConsent(super.getSamlCoreProperties().getConsentAuthnRequest());

        final Issuer issuer = SAMLEngineUtils.generateIssuer();

        if (request.getIssuer() != null) {
            issuer.setValue(request.getIssuer());
        } else {
            issuer.setValue(super.getSamlCoreProperties().getRequester());
        }

        // Optional STORK
        final String formatEntity = super.getSamlCoreProperties().getFormatEntity();
        if (StringUtils.isNotBlank(formatEntity)) {
            issuer.setFormat(formatEntity);
        }

        authnRequestAux.setIssuer(issuer);

        // Generate stork extensions.
        final Extensions storkExtensions = this.generateSTORKExtensions(request);
        // add the extensions to the SAMLAuthnRequest
        authnRequestAux.setExtensions(storkExtensions);

        // the result contains an authentication request token (byte[]),
        // identifier of the token, and all parameters from the request.
        final STORKAuthnRequest authRequest = processExtensions(authnRequestAux.getExtensions());

        try {
            authRequest.setTokenSaml(super.signAndMarshall(authnRequestAux));
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        authRequest.setSamlId(authnRequestAux.getID());
        authRequest.setDestination(authnRequestAux.getDestination());
        authRequest.setAssertionConsumerServiceURL(authnRequestAux.getAssertionConsumerServiceURL());

        authRequest.setProviderName(authnRequestAux.getProviderName());
        authRequest.setIssuer(authnRequestAux.getIssuer().getValue());

        return authRequest;
    }

    /**
     * Generate stork authentication response.
     * 
     * @param request the request
     * @param responseAuthReq the response authentication request
     * @param ipAddress the IP address
     * @param isHashing the is hashing
     * 
     * @return the sTORK authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnResponse generateSTORKAuthnResponse(final STORKAuthnRequest request,
            final STORKAuthnResponse responseAuthReq, final String ipAddress, final boolean isHashing)
            throws STORKSAMLEngineException {
        LOG.info("generateSTORKAuthnResponse");

        // Validate parameters
        validateParamResponse(request, responseAuthReq);

        // Mandatory SAML
        LOG.debug("Generate StatusCode");
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(StatusCode.SUCCESS_URI);

        LOG.debug("Generate Status");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        LOG.debug("Generate StatusMessage");
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .generateStatusMessage(StatusCode.SUCCESS_URI);

        status.setStatusMessage(statusMessage);

        LOG.debug("Generate Response");

        // RESPONSE
        final Response response = genAuthnRespBase(status, request.getAssertionConsumerServiceURL(),
                request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, request.getAssertionConsumerServiceURL(),
                request.getSamlId(), request.getIssuer(), notOnOrAfter);

        final AttributeStatement attrStatement = this
                .generateAttributeStatement(responseAuthReq.getPersonalAttributeList(), isHashing);

        assertion.getAttributeStatements().add(attrStatement);

        // Add assertions
        response.getAssertions().add(assertion);

        final STORKAuthnResponse authresponse = new STORKAuthnResponse();

        try {
            authresponse.setTokenSaml(super.signAndMarshall(response));
            authresponse.setSamlId(response.getID());
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }
        return authresponse;
    }

    /**
     * Generate stork authentication response.
     * 
     * @param request the request
     * @param responseAuthReq the response authentication request
     * @param ipAddress the IP address
     * @param isHashing the is hashing
     * 
     * @return the sTORK authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnResponse generateSTORKAuthnResponseAfterQuery(final STORKAuthnRequest request,
            final STORKAuthnResponse responseAuthReq, final String ipAddress, final boolean isHashing,
            List<STORKAttrQueryResponse> res) throws STORKSAMLEngineException {
        LOG.info("generateSTORKAuthnResponse");

        // Validate parameters
        validateParamResponse(request, responseAuthReq);

        // Mandatory SAML
        LOG.debug("Generate StatusCode");
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(StatusCode.SUCCESS_URI);

        LOG.debug("Generate Status");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        LOG.debug("Generate StatusMessage");
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .generateStatusMessage(StatusCode.SUCCESS_URI);

        status.setStatusMessage(statusMessage);

        LOG.debug("Generate Response");

        // RESPONSE
        final Response response = genAuthnRespBase(status, request.getAssertionConsumerServiceURL(),
                request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, request.getAssertionConsumerServiceURL(),
                request.getSamlId(), request.getIssuer(), notOnOrAfter);

        final AttributeStatement attrStatement = this
                .generateAttributeStatement(responseAuthReq.getPersonalAttributeList(), isHashing);

        assertion.getAttributeStatements().add(attrStatement);

        // Add assertions
        response.getAssertions().add(assertion);
        // Check for response queries
        if (res != null && res.size() > 0) {
            //Iterate through them
            for (int i = 0; i < res.size(); i++) {
                //If response contains multiple assertions iterate through them as well
                if (res.get(i).getAssertions().size() > 1) {
                    for (int j = 0; j < res.get(i).getAssertions().size(); j++) {
                        Assertion tempAssertion = res.get(i).getAssertions().get(j);
                        tempAssertion.setParent(response);
                        response.getAssertions().add(tempAssertion);
                    }
                } else {
                    Assertion tempAssertion = res.get(i).getAssertion();
                    tempAssertion.setParent(response);
                    response.getAssertions().add(tempAssertion);
                }
            }
        }

        final STORKAuthnResponse authresponse = new STORKAuthnResponse();

        try {
            authresponse.setTokenSaml(super.signAndMarshall(response));
            authresponse.setSamlId(response.getID());
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }
        return authresponse;
    }

    /**
     * Generate stork authentication response fail.
     * 
     * @param request the request
     * @param response the response
     * @param ipAddress the IP address
     * @param isHashing the is hashing
     * 
     * @return the sTORK authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnResponse generateSTORKAuthnResponseFail(final STORKAuthnRequest request,
            final STORKAuthnResponse response, final String ipAddress, final boolean isHashing)
            throws STORKSAMLEngineException {
        LOG.info("generateSTORKAuthnResponseFail");

        validateParamResponseFail(request, response);

        // Mandatory
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(response.getStatusCode());

        // Mandatory SAML
        LOG.debug("Generate StatusCode.");
        // Subordinate code it's optional in case not covered into next codes:
        // - urn:oasis:names:tc:SAML:2.0:status:AuthnFailed
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy
        // - urn:oasis:names:tc:SAML:2.0:status:RequestDenied
        // - http://www.stork.gov.eu/saml20/statusCodes/QAANotSupported

        if (StringUtils.isNotBlank(response.getSubStatusCode())) {
            final StatusCode newStatusCode = SAMLEngineUtils.generateStatusCode(response.getSubStatusCode());
            statusCode.setStatusCode(newStatusCode);
        }

        LOG.debug("Generate Status.");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        if (StringUtils.isNotBlank(response.getMessage())) {
            final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                    .generateStatusMessage(response.getMessage());

            status.setStatusMessage(statusMessage);
        }

        LOG.debug("Generate Response.");
        // RESPONSE
        final Response responseFail = genAuthnRespBase(status, request.getAssertionConsumerServiceURL(),
                request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, request.getAssertionConsumerServiceURL(),
                request.getSamlId(), request.getIssuer(), notOnOrAfter);

        responseFail.getAssertions().add(assertion);

        LOG.debug("Sign and Marshall ResponseFail.");

        final STORKAuthnResponse storkResponse = new STORKAuthnResponse();

        try {
            storkResponse.setTokenSaml(super.signAndMarshall(responseFail));
            storkResponse.setSamlId(responseFail.getID());
        } catch (SAMLEngineException e) {
            LOG.error("SAMLEngineException.", e);
            throw new STORKSAMLEngineException(e);
        }
        return storkResponse;
    }

    /**
     * Generate stork attribute query request.
     * 
     * @param request the request that contain all parameters for generate an
     *            attribute query request.
     * 
     * @return the STORK attribute query request that has been processed.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryRequest generateSTORKAttrQueryRequest(final STORKAttrQueryRequest request)
            throws STORKSAMLEngineException {
        LOG.info("Generate STORKAttrQueryRequest.");

        // Validate Parameters mandatories
        validateParamAttrQueryReq(request);

        //final AttributeQuery attrQueryRequestAux = SAMLEngineUtils
        final CustomAttributeQuery attrQueryRequestAux = SAMLEngineUtils.generateSAMLAttrQueryRequest(
                SAMLEngineUtils.generateNCName(), SAMLVersion.VERSION_20, SAMLEngineUtils.getCurrentTime());

        // Set name spaces.
        setNameSpaces(attrQueryRequestAux);

        // Add parameter optional STORK
        // Destination is mandatory if the destination is a C-PEPS
        // The application must to know if the destination is a C-PEPS.
        if (StringUtils.isNotBlank(request.getDestination())) {
            attrQueryRequestAux.setDestination(request.getDestination());
        }

        // Add parameter optional STORK
        // Consumer URL is needed if using HTTP-Post
        if (StringUtils.isNotBlank(request.getAssertionConsumerServiceURL())) {
            attrQueryRequestAux.setAssertionConsumerServiceURL(request.getAssertionConsumerServiceURL());
        }

        // Consent is optional. Set from SAMLEngine.xml - consent.
        attrQueryRequestAux.setConsent(super.getSamlCoreProperties().getConsentAuthnRequest());

        final Issuer issuer = SAMLEngineUtils.generateIssuer();

        //Set the subject - needed for attribute query validation
        Subject subject = SAMLEngineUtils.generateSubject();
        SubjectConfirmationBuilder builder = new SubjectConfirmationBuilder();
        SubjectConfirmation subjectConfirmation = builder.buildObject();
        subjectConfirmation.setMethod("urn:oasis:names:tc:SAML:2.0:cm:bearer");
        subject.getSubjectConfirmations().add(subjectConfirmation);
        attrQueryRequestAux.setSubject(subject);

        if (request.getIssuer() != null) {
            issuer.setValue(request.getIssuer());
        } else {
            issuer.setValue(super.getSamlCoreProperties().getRequester());
        }

        // Optional STORK
        final String formatEntity = super.getSamlCoreProperties().getFormatEntity();
        if (StringUtils.isNotBlank(formatEntity)) {
            issuer.setFormat(formatEntity);
        }

        attrQueryRequestAux.setIssuer(issuer);

        // Generate stork extensions.
        final Extensions storkExtensions = this.generateSTORKAttrExtensions(request);
        // add the extensions to the SAMLAuthnRequest
        attrQueryRequestAux.setExtensions(storkExtensions);

        // the result contains an authentication request token (byte[]),
        // identifier of the token, and all parameters from the request.
        final STORKAttrQueryRequest attrQueryRequest = processAttrExtensions(attrQueryRequestAux.getExtensions());

        try {
            attrQueryRequest.setTokenSaml(super.signAndMarshall(attrQueryRequestAux));
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        attrQueryRequest.setSamlId(attrQueryRequestAux.getID());
        attrQueryRequest.setDestination(attrQueryRequestAux.getDestination());
        attrQueryRequest.setAssertionConsumerServiceURL(attrQueryRequestAux.getAssertionConsumerServiceURL());
        attrQueryRequest.setIssuer(attrQueryRequestAux.getIssuer().getValue());

        return attrQueryRequest;
    }

    /**
     * Generate stork attribute query response.
     * 
     * @param request the request
     * @param responseAttrQueryRes the response authentication request
     * @param ipAddress the IP address
     * @param isHashing the hashing of values
     * 
     * @return the sTORK authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryResponse generateSTORKAttrQueryResponse(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse responseAttrQueryRes, final String ipAddress, final String destinationUrl,
            final boolean isHashing) throws STORKSAMLEngineException {
        LOG.info("generateSTORKAttrQueryResponse");

        // Validate parameters
        validateParamAttrQueryResponse(request, responseAttrQueryRes);

        // Mandatory SAML
        LOG.debug("Generate StatusCode");
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(StatusCode.SUCCESS_URI);

        LOG.debug("Generate Status");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        LOG.debug("Generate StatusMessage");
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .generateStatusMessage(StatusCode.SUCCESS_URI);

        status.setStatusMessage(statusMessage);

        LOG.debug("Generate Response");

        // RESPONSE
        final Response response = genAuthnRespBase(status, destinationUrl, request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, "", request.getSamlId(), request.getIssuer(),
                notOnOrAfter);

        final AttributeStatement attrStatement = this
                .generateAttributeStatement(responseAttrQueryRes.getPersonalAttributeList(), isHashing);

        assertion.getAttributeStatements().add(attrStatement);

        // Add assertions
        response.getAssertions().add(assertion);

        final STORKAttrQueryResponse attrQueryResponse = new STORKAttrQueryResponse();

        try {
            attrQueryResponse.setTokenSaml(super.signAndMarshall(response));
            attrQueryResponse.setSamlId(response.getID());
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }
        return attrQueryResponse;
    }

    /**
     * Generate stork attribute query response from multiple assertions 
     * 
     * @param request the request
     * @param responseAttrQueryRes the response to the query request
     * @param responses the responses to include in the response (aggregation)
     * @param ipAddress the IP address
     * @param isHashing the hashing of values
     * 
     * @return the sTORK attribute query response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryResponse generateSTORKAttrQueryResponseWithAssertions(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse responseAttrQueryRes, final List<STORKAttrQueryResponse> responses,
            final String ipAddress, final String destinationUrl, final boolean isHashing)
            throws STORKSAMLEngineException {
        LOG.info("generateSTORKAttrQueryResponse");

        // Validate parameters
        validateParamAttrQueryResponseFromAssertions(request, responseAttrQueryRes);

        // Mandatory SAML
        LOG.debug("Generate StatusCode");
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(StatusCode.SUCCESS_URI);

        LOG.debug("Generate Status");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        LOG.debug("Generate StatusMessage");
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .generateStatusMessage(StatusCode.SUCCESS_URI);

        status.setStatusMessage(statusMessage);

        LOG.debug("Generate Response");

        // RESPONSE
        final Response response = genAuthnRespBase(status, destinationUrl, request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, "", request.getSamlId(), request.getIssuer(),
                notOnOrAfter);

        final AttributeStatement attrStatement = this
                .generateAttributeStatement(responseAttrQueryRes.getPersonalAttributeList(), isHashing);

        assertion.getAttributeStatements().add(attrStatement);

        // Add the assertions from the former Query responses
        response.getAssertions().add(assertion);
        if (responses != null && responses.size() > 0) {
            for (int i = 0; i < responses.size(); i++) {
                Assertion tempAssertion = responses.get(i).getAssertion();
                tempAssertion.setParent(response);
                response.getAssertions().add(tempAssertion);
            }
        }

        final STORKAttrQueryResponse attrQueryResponse = new STORKAttrQueryResponse();

        try {
            attrQueryResponse.setTokenSaml(super.signAndMarshall(response));
            attrQueryResponse.setSamlId(response.getID());
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }
        return attrQueryResponse;
    }

    /**
     * Generate stork attribute query response fail.
     * 
     * @param request the request
     * @param response the response
     * @param ipAddress the IP address
     * @param isHashing the is hashing
     * 
     * @return the STORK attribute query response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryResponse generateSTORKAttrQueryResponseFail(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse response, final String ipAddress, final String destinationUrl,
            final boolean isHashing) throws STORKSAMLEngineException {
        LOG.info("generateSTORKAttrQueryResponseFail");

        validateParamAttrQueryResponseFail(request, response);

        // Mandatory
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(response.getStatusCode());

        // Mandatory SAML
        LOG.debug("Generate StatusCode.");
        // Subordinate code it's optional in case not covered into next codes:
        // - urn:oasis:names:tc:SAML:2.0:status:AuthnFailed
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy
        // - urn:oasis:names:tc:SAML:2.0:status:RequestDenied
        // - http://www.stork.gov.eu/saml20/statusCodes/QAANotSupported

        if (StringUtils.isNotBlank(response.getSubStatusCode())) {
            final StatusCode newStatusCode = SAMLEngineUtils.generateStatusCode(response.getSubStatusCode());
            statusCode.setStatusCode(newStatusCode);
        }

        LOG.debug("Generate Status.");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        if (StringUtils.isNotBlank(response.getMessage())) {
            final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                    .generateStatusMessage(response.getMessage());

            status.setStatusMessage(statusMessage);
        }

        LOG.debug("Generate Response.");
        // RESPONSE
        final Response responseFail = genAuthnRespBase(status, destinationUrl, request.getSamlId());

        DateTime notOnOrAfter = new DateTime();

        notOnOrAfter = notOnOrAfter.plusSeconds(super.getSamlCoreProperties().getTimeNotOnOrAfter());

        final Assertion assertion = this.generateAssertion(ipAddress, "", request.getSamlId(), request.getIssuer(),
                notOnOrAfter);

        responseFail.getAssertions().add(assertion);

        LOG.debug("Sign and Marshall ResponseFail.");

        final STORKAttrQueryResponse storkResponse = new STORKAttrQueryResponse();

        try {
            storkResponse.setTokenSaml(super.signAndMarshall(responseFail));
            storkResponse.setSamlId(responseFail.getID());
        } catch (SAMLEngineException e) {
            LOG.error("SAMLEngineException.", e);
            throw new STORKSAMLEngineException(e);
        }
        return storkResponse;
    }

    /**
     * Generate stork logout request.
     * 
     * @param request the request that contain all parameters for generate an
     *            logout request.
     * 
     * @return the STORK logout request that has been processed.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKLogoutRequest generateSTORKLogoutRequest(final STORKLogoutRequest request)
            throws STORKSAMLEngineException {
        LOG.info("Generate STORKLogoutRequest.");

        // Validate Parameters mandatories
        validateParamLogoutReq(request);

        final LogoutRequest logoutRequestAux = SAMLEngineUtils.generateSAMLLogoutRequest(
                SAMLEngineUtils.generateNCName(), SAMLVersion.VERSION_20, SAMLEngineUtils.getCurrentTime());

        // Set name spaces.
        setNameSpaces(logoutRequestAux);

        // Add parameter optional STORK
        // Destination is mandatory if the destination is a C-PEPS
        // The application must to know if the destination is a C-PEPS.
        if (StringUtils.isNotBlank(request.getDestination())) {
            logoutRequestAux.setDestination(request.getDestination());
        }

        // Consent is optional. Set from SAMLEngine.xml - consent.
        logoutRequestAux.setConsent(super.getSamlCoreProperties().getConsentAuthnRequest());

        final Issuer issuer = SAMLEngineUtils.generateIssuer();

        if (request.getIssuer() != null) {
            issuer.setValue(request.getIssuer());
        } else {
            issuer.setValue(super.getSamlCoreProperties().getRequester());
        }

        // Optional STORK
        final String formatEntity = super.getSamlCoreProperties().getFormatEntity();
        if (StringUtils.isNotBlank(formatEntity)) {
            issuer.setFormat(formatEntity);
        }

        logoutRequestAux.setIssuer(issuer);

        // Set the name ID
        final NameID newNameID = SAMLEngineUtils.generateNameID();
        newNameID.setValue(request.getSpProvidedId());
        logoutRequestAux.setNameID(newNameID);

        // the result contains an authentication request token (byte[]),
        // identifier of the token, and all parameters from the request.
        final STORKLogoutRequest logoutRequest = new STORKLogoutRequest();

        try {
            logoutRequest.setTokenSaml(super.signAndMarshall(logoutRequestAux));
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        logoutRequest.setSamlId(logoutRequestAux.getID());
        logoutRequest.setDestination(logoutRequestAux.getDestination());
        logoutRequest.setIssuer(logoutRequestAux.getIssuer().getValue());
        logoutRequest.setSpProvidedId(logoutRequestAux.getNameID().getValue());

        return logoutRequest;
    }

    /**
     * Generate stork logout response.
     * @param request the request thats being responded to
     * @param response the tesponse that contain all parameters for generate an
     *            logout request.
     * 
     * @return the STORK logout response that has been processed.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKLogoutResponse generateSTORKLogoutResponse(final STORKLogoutRequest request,
            final STORKLogoutResponse response) throws STORKSAMLEngineException {
        LOG.info("Generate STORKLogoutResponse.");

        // Validate Parameters mandatories
        validateParamLogoutRes(request, response);

        // Mandatory SAML
        LOG.debug("Generate StatusCode");
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(StatusCode.SUCCESS_URI);

        LOG.debug("Generate Status");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        LOG.debug("Generate StatusMessage");
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .generateStatusMessage(StatusCode.SUCCESS_URI);

        status.setStatusMessage(statusMessage);

        final LogoutResponse logoutResponseAux = SAMLEngineUtils.generateSAMLLogoutResponse(
                SAMLEngineUtils.generateNCName(), SAMLVersion.VERSION_20, SAMLEngineUtils.getCurrentTime(), status,
                request.getSamlId());

        // Set name spaces.
        setNameSpaces(logoutResponseAux);

        // Add parameter optional STORK
        // Destination is mandatory if the destination is a C-PEPS
        // The application must to know if the destination is a C-PEPS.
        if (StringUtils.isNotBlank(response.getDestination())) {
            logoutResponseAux.setDestination(response.getDestination());
        }

        // Consent is optional. Set from SAMLEngine.xml - consent.
        logoutResponseAux.setConsent(super.getSamlCoreProperties().getConsentAuthnRequest());

        final Issuer issuer = SAMLEngineUtils.generateIssuer();

        if (response.getIssuer() != null) {
            issuer.setValue(response.getIssuer());
        } else {
            issuer.setValue(super.getSamlCoreProperties().getRequester());
        }

        // Optional STORK
        final String formatEntity = super.getSamlCoreProperties().getFormatEntity();
        if (StringUtils.isNotBlank(formatEntity)) {
            issuer.setFormat(formatEntity);
        }

        logoutResponseAux.setIssuer(issuer);

        // the result contains an authentication request token (byte[]),
        // identifier of the token, and all parameters from the request.
        final STORKLogoutResponse logoutResponse = new STORKLogoutResponse();

        try {
            logoutResponse.setTokenSaml(super.signAndMarshall(logoutResponseAux));
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        logoutResponse.setSamlId(logoutResponseAux.getID());
        logoutResponse.setDestination(logoutResponseAux.getDestination());
        logoutResponse.setIssuer(logoutResponseAux.getIssuer().getValue());
        logoutResponse.setStatusCode(logoutResponseAux.getStatus().getStatusCode().toString());
        logoutResponse.setStatusMessage(logoutResponseAux.getStatus().getStatusMessage().toString());

        return logoutResponse;
    }

    /**
     * Generate failed stork logout response.
     * 
     * @param response the response that contain all parameters for generate an
     *            logout request.
     * 
     * @return the STORK logout response that has been processed.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKLogoutResponse generateSTORKLogoutResponseFail(final STORKLogoutRequest request,
            final STORKLogoutResponse response) throws STORKSAMLEngineException {
        LOG.info("Generate STORKLogoutResponse.");

        // Validate Parameters mandatories
        validateParamLogoutResFail(request, response);

        // Mandatory
        final StatusCode statusCode = SAMLEngineUtils.generateStatusCode(response.getStatusCode());

        // Mandatory SAML
        LOG.debug("Generate StatusCode.");
        // Subordinate code it's optional in case not covered into next codes:
        // - urn:oasis:names:tc:SAML:2.0:status:AuthnFailed
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidAttrNameOrValue
        // - urn:oasis:names:tc:SAML:2.0:status:InvalidNameIDPolicy
        // - urn:oasis:names:tc:SAML:2.0:status:RequestDenied
        // - http://www.stork.gov.eu/saml20/statusCodes/QAANotSupported

        if (StringUtils.isNotBlank(response.getSubStatusCode())) {
            final StatusCode newStatusCode = SAMLEngineUtils.generateStatusCode(response.getSubStatusCode());
            statusCode.setStatusCode(newStatusCode);
        }

        LOG.debug("Generate Status.");
        final Status status = SAMLEngineUtils.generateStatus(statusCode);

        if (StringUtils.isNotBlank(response.getStatusMessage())) {
            final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                    .generateStatusMessage(response.getStatusMessage());

            status.setStatusMessage(statusMessage);
        }

        final LogoutResponse logoutResponseAux = SAMLEngineUtils.generateSAMLLogoutResponse(
                SAMLEngineUtils.generateNCName(), SAMLVersion.VERSION_20, SAMLEngineUtils.getCurrentTime(), status,
                request.getSamlId());

        // Set name spaces.
        setNameSpaces(logoutResponseAux);

        // Add parameter optional STORK
        // Destination is mandatory if the destination is a C-PEPS
        // The application must to know if the destination is a C-PEPS.
        if (StringUtils.isNotBlank(response.getDestination())) {
            logoutResponseAux.setDestination(response.getDestination());
        }

        // Consent is optional. Set from SAMLEngine.xml - consent.
        logoutResponseAux.setConsent(super.getSamlCoreProperties().getConsentAuthnRequest());

        final Issuer issuer = SAMLEngineUtils.generateIssuer();

        if (response.getIssuer() != null) {
            issuer.setValue(response.getIssuer());
        } else {
            issuer.setValue(super.getSamlCoreProperties().getRequester());
        }

        // Optional STORK
        final String formatEntity = super.getSamlCoreProperties().getFormatEntity();
        if (StringUtils.isNotBlank(formatEntity)) {
            issuer.setFormat(formatEntity);
        }

        logoutResponseAux.setIssuer(issuer);

        // the result contains an authentication request token (byte[]),
        // identifier of the token, and all parameters from the request.
        final STORKLogoutResponse logoutResponse = new STORKLogoutResponse();

        try {
            logoutResponse.setTokenSaml(super.signAndMarshall(logoutResponseAux));
        } catch (SAMLEngineException e) {
            LOG.error("Sign and Marshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        logoutResponse.setSamlId(logoutResponseAux.getID());
        logoutResponse.setDestination(logoutResponseAux.getDestination());
        logoutResponse.setIssuer(logoutResponseAux.getIssuer().getValue());
        logoutResponse.setStatusCode(logoutResponseAux.getStatus().getStatusCode().toString());
        logoutResponse.setStatusMessage(logoutResponseAux.getStatus().getStatusMessage().toString());

        return logoutResponse;
    }

    /**
     * Generate stork authentication statement for the authentication statement.
     * 
     * @param ipAddress the IP address
     * 
     * @return the authentication statement
     */
    private AuthnStatement generateStorkAuthStatement(final String ipAddress) {
        LOG.debug("Generate stork authenticate statement.");
        final SubjectLocality subjectLocality = SAMLEngineUtils.generateSubjectLocality(ipAddress);

        final AuthnContext authnContext = (AuthnContext) SAMLEngineUtils
                .createSamlObject(AuthnContext.DEFAULT_ELEMENT_NAME);

        final AuthnContextDecl authnContextDecl = (AuthnContextDecl) SAMLEngineUtils
                .createSamlObject(AuthnContextDecl.DEFAULT_ELEMENT_NAME);

        authnContext.setAuthnContextDecl(authnContextDecl);

        final AuthnStatement authnStatement = SAMLEngineUtils.generateAthnStatement(new DateTime(), authnContext);

        // Optional STORK
        authnStatement.setSessionIndex(null);
        authnStatement.setSubjectLocality(subjectLocality);

        return authnStatement;
    }

    /**
     * Generate stork extensions.
     * 
     * @param request the request
     * 
     * @return the extensions
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Extensions generateSTORKExtensions(final STORKAuthnRequest request) throws STORKSAMLEngineException {
        LOG.debug("Generate STORKExtensions");

        final Extensions extensions = SAMLEngineUtils.generateExtension();

        LOG.debug("Generate QAAAttribute");
        final QAAAttribute qaaAttribute = SAMLEngineUtils.generateQAAAttribute(request.getQaa());
        extensions.getUnknownXMLObjects().add(qaaAttribute);

        if (StringUtils.isNotEmpty(request.getSpSector())) {
            // Add information about service provider.
            LOG.debug("Generate SPSector");
            final SPSector sector = SAMLEngineUtils.generateSPSector(request.getSpSector());
            extensions.getUnknownXMLObjects().add(sector);
        }

        //Delete from specification. Kept for compatibility with Provider Name value
        LOG.debug("Generate SPInstitution");
        final SPInstitution institution = SAMLEngineUtils.generateSPInstitution(request.getProviderName());
        extensions.getUnknownXMLObjects().add(institution);

        if (StringUtils.isNotEmpty(request.getSpApplication())) {
            LOG.debug("Generate SPApplication");
            final SPApplication application = SAMLEngineUtils.generateSPApplication(request.getSpApplication());
            extensions.getUnknownXMLObjects().add(application);
        }

        if (StringUtils.isNotEmpty(request.getSpCountry())) {
            LOG.debug("Generate SPCountry");
            final SPCountry country = SAMLEngineUtils.generateSPCountry(request.getSpCountry());
            extensions.getUnknownXMLObjects().add(country);
        }

        //eIDSectorShare: optional; default value: false.
        String valueSectorShare = super.getSamlCoreProperties().iseIDSectorShare();

        if (StringUtils.isNotEmpty(valueSectorShare)) {
            // Add information about the use of the SAML message.
            LOG.debug("Generate EIDSectorShare");
            final EIDSectorShare eIdSectorShare = (EIDSectorShare) SAMLEngineUtils
                    .createSamlObject(EIDSectorShare.DEF_ELEMENT_NAME);

            eIdSectorShare.setEIDSectorShare(String.valueOf(Boolean.valueOf(valueSectorShare)));

            extensions.getUnknownXMLObjects().add(eIdSectorShare);
        }

        String valueCrossSectorShare = super.getSamlCoreProperties().iseIDCrossSectorShare();

        if (StringUtils.isNotEmpty(valueCrossSectorShare)) {
            LOG.debug("Generate EIDCrossSectorShare");
            final EIDCrossSectorShare eIdCrossSecShare = (EIDCrossSectorShare) SAMLEngineUtils
                    .createSamlObject(EIDCrossSectorShare.DEF_ELEMENT_NAME);
            eIdCrossSecShare.setEIDCrossSectorShare(String.valueOf(Boolean.valueOf(valueCrossSectorShare)));
            extensions.getUnknownXMLObjects().add(eIdCrossSecShare);
        }

        String valueCrossBorderShare = super.getSamlCoreProperties().iseIDCrossBorderShare();

        if (StringUtils.isNotEmpty(valueCrossBorderShare)) {
            LOG.debug("Generate EIDCrossBorderShare");
            final EIDCrossBorderShare eIdCrossBordShare = (EIDCrossBorderShare) SAMLEngineUtils
                    .createSamlObject(EIDCrossBorderShare.DEF_ELEMENT_NAME);
            eIdCrossBordShare.setEIDCrossBorderShare(String.valueOf(Boolean.valueOf(valueCrossBorderShare)));
            extensions.getUnknownXMLObjects().add(eIdCrossBordShare);
        }

        // Add information about requested attributes.
        LOG.debug("Generate RequestedAttributes.");
        final RequestedAttributes reqAttributes = (RequestedAttributes) SAMLEngineUtils
                .createSamlObject(RequestedAttributes.DEF_ELEMENT_NAME);

        LOG.debug("SAML Engine configuration properties load.");
        final Iterator<PersonalAttribute> iterator = request.getPersonalAttributeList().iterator();

        while (iterator.hasNext()) {

            final PersonalAttribute attribute = iterator.next();

            if (attribute == null || StringUtils.isBlank(attribute.getName())) {
                LOG.error(ATTRIBUTE_EMPTY_LITERAL);
                throw new STORKSAMLEngineException(ATTRIBUTE_EMPTY_LITERAL);
            }

            // Verified if exits the attribute name.
            final String attributeName = super.getSamlCoreProperties().getProperty(attribute.getName());

            if (StringUtils.isBlank(attributeName)) {
                LOG.debug("Attribute name: {} was not found.", attribute.getName());
                throw new STORKSAMLEngineException("Attribute name: " + attribute.getName() + " was not found.");
            }

            // Friendly name it's an optional attribute.
            String friendlyName = null;

            if (super.getSamlCoreProperties().isFriendlyName()) {
                friendlyName = attribute.getName();
            }

            String isRequired = null;
            if (super.getSamlCoreProperties().isRequired()) {
                isRequired = String.valueOf(attribute.isRequired());
            }

            LOG.debug("Generate requested attribute: " + attributeName);
            final RequestedAttribute requestedAttr = SAMLEngineUtils.generateReqAuthnAttributeSimple(attributeName,
                    friendlyName, isRequired, attribute.getValue());

            // Add requested attribute.
            reqAttributes.getAttributes().add(requestedAttr);
        }

        // Add requested attributes.
        extensions.getUnknownXMLObjects().add(reqAttributes);

        CitizenCountryCode citizenCountryCode = null;
        if (request.getCitizenCountryCode() != null && StringUtils.isNotBlank(request.getCitizenCountryCode())) {
            LOG.debug("Generate CitizenCountryCode");
            citizenCountryCode = (CitizenCountryCode) SAMLEngineUtils
                    .createSamlObject(CitizenCountryCode.DEF_ELEMENT_NAME);

            citizenCountryCode.setCitizenCountryCode(request.getCitizenCountryCode().toUpperCase());
        }

        SPID spid = null;
        if (request.getSPID() != null && StringUtils.isNotBlank(request.getSPID())) {
            LOG.debug("Generate SPID");
            spid = (SPID) SAMLEngineUtils.createSamlObject(SPID.DEF_ELEMENT_NAME);

            spid.setSPID(request.getSPID().toUpperCase());
        }

        AuthenticationAttributes authenticationAttr = (AuthenticationAttributes) SAMLEngineUtils
                .createSamlObject(AuthenticationAttributes.DEF_ELEMENT_NAME);

        final VIDPAuthenticationAttributes vIDPauthenticationAttr = (VIDPAuthenticationAttributes) SAMLEngineUtils
                .createSamlObject(VIDPAuthenticationAttributes.DEF_ELEMENT_NAME);

        final SPInformation spInformation = (SPInformation) SAMLEngineUtils
                .createSamlObject(SPInformation.DEF_ELEMENT_NAME);

        if (citizenCountryCode != null) {
            vIDPauthenticationAttr.setCitizenCountryCode(citizenCountryCode);
        }

        if (spid != null) {
            spInformation.setSPID(spid);
        }

        vIDPauthenticationAttr.setSPInformation(spInformation);

        authenticationAttr.setVIDPAuthenticationAttributes(vIDPauthenticationAttr);
        extensions.getUnknownXMLObjects().add(authenticationAttr);

        return extensions;

    }

    /**
     * Generate stork extensions.
     * 
     * @param request the attribute query request
     * 
     * @return the extensions
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Extensions generateSTORKAttrExtensions(final STORKAttrQueryRequest request)
            throws STORKSAMLEngineException {
        LOG.debug("Generate STORKExtensions");

        final Extensions extensions = SAMLEngineUtils.generateExtension();

        LOG.debug("Generate QAAAttribute");
        final QAAAttribute qaaAttribute = SAMLEngineUtils.generateQAAAttribute(request.getQaa());
        extensions.getUnknownXMLObjects().add(qaaAttribute);

        if (StringUtils.isNotEmpty(request.getSpSector())) {
            // Add information about service provider.
            LOG.debug("Generate SPSector");
            final SPSector sector = SAMLEngineUtils.generateSPSector(request.getSpSector());
            extensions.getUnknownXMLObjects().add(sector);
        }

        if (StringUtils.isNotEmpty(request.getSpApplication())) {
            LOG.debug("Generate SPApplication");
            final SPApplication application = SAMLEngineUtils.generateSPApplication(request.getSpApplication());
            extensions.getUnknownXMLObjects().add(application);
        }

        if (StringUtils.isNotEmpty(request.getSpCountry())) {
            LOG.debug("Generate SPCountry");
            final SPCountry country = SAMLEngineUtils.generateSPCountry(request.getSpCountry());
            extensions.getUnknownXMLObjects().add(country);
        }

        final EIDSectorShare eIdSectorShare = (EIDSectorShare) SAMLEngineUtils
                .createSamlObject(EIDSectorShare.DEF_ELEMENT_NAME);

        eIdSectorShare.setEIDSectorShare(String.valueOf(request.isEIDSectorShare()));

        extensions.getUnknownXMLObjects().add(eIdSectorShare);

        final EIDCrossSectorShare eIdCrossSecShare = (EIDCrossSectorShare) SAMLEngineUtils
                .createSamlObject(EIDCrossSectorShare.DEF_ELEMENT_NAME);
        eIdCrossSecShare.setEIDCrossSectorShare(String.valueOf(request.isEIDCrossSectorShare()));
        extensions.getUnknownXMLObjects().add(eIdCrossSecShare);

        final EIDCrossBorderShare eIdCrossBordShare = (EIDCrossBorderShare) SAMLEngineUtils
                .createSamlObject(EIDCrossBorderShare.DEF_ELEMENT_NAME);
        eIdCrossBordShare.setEIDCrossBorderShare(String.valueOf(request.isEIDCrossBorderShare()));
        extensions.getUnknownXMLObjects().add(eIdCrossBordShare);

        // Add information about requested attributes.
        LOG.debug("Generate RequestedAttributes.");
        final RequestedAttributes reqAttributes = (RequestedAttributes) SAMLEngineUtils
                .createSamlObject(RequestedAttributes.DEF_ELEMENT_NAME);

        LOG.debug("SAML Engine configuration properties load.");
        final Iterator<PersonalAttribute> iterator = request.getPersonalAttributeList().iterator();

        while (iterator.hasNext()) {

            final PersonalAttribute attribute = iterator.next();

            if (attribute == null || StringUtils.isBlank(attribute.getName())) {
                LOG.error(ATTRIBUTE_EMPTY_LITERAL);
                throw new STORKSAMLEngineException(ATTRIBUTE_EMPTY_LITERAL);
            }

            // Verified if exits the attribute name.
            final String attributeName = super.getSamlCoreProperties().getProperty(attribute.getName());

            if (StringUtils.isBlank(attributeName)) {
                LOG.debug("Attribute name: {} was not found.", attribute.getName());
                throw new STORKSAMLEngineException("Attribute name: " + attribute.getName() + " was not found.");
            }

            // Friendly name it's an optional attribute.
            String friendlyName = null;

            if (super.getSamlCoreProperties().isFriendlyName()) {
                friendlyName = attribute.getName();
            }

            String isRequired = null;
            if (super.getSamlCoreProperties().isRequired()) {
                isRequired = String.valueOf(attribute.isRequired());
            }

            LOG.debug("Generate requested attribute: " + attributeName);
            final RequestedAttribute requestedAttr = SAMLEngineUtils.generateReqAuthnAttributeSimple(attributeName,
                    friendlyName, isRequired, attribute.getValue());

            // Add requested attribute.
            reqAttributes.getAttributes().add(requestedAttr);
        }

        // Add requested attributes.
        extensions.getUnknownXMLObjects().add(reqAttributes);

        CitizenCountryCode citizenCountryCode = null;
        if (request.getCitizenCountryCode() != null && StringUtils.isNotBlank(request.getCitizenCountryCode())) {
            LOG.debug("Generate CitizenCountryCode");
            citizenCountryCode = (CitizenCountryCode) SAMLEngineUtils
                    .createSamlObject(CitizenCountryCode.DEF_ELEMENT_NAME);

            citizenCountryCode.setCitizenCountryCode(request.getCitizenCountryCode().toUpperCase());
        }

        SPID spid = null;
        if (request.getSPID() != null && StringUtils.isNotBlank(request.getSPID())) {
            LOG.debug("Generate SPID");
            spid = (SPID) SAMLEngineUtils.createSamlObject(SPID.DEF_ELEMENT_NAME);

            spid.setSPID(request.getSPID().toUpperCase());
        }

        return extensions;

    }

    /**
     * Gets the alias from X.509 Certificate at keystore.
     * 
     * @param keyInfo the key info
     * @param storkOwnKeyStore 
     * @param storkOwnKeyStore 
     * 
     * @return the alias
     */
    private String getAlias(final KeyInfo keyInfo, KeyStore storkOwnKeyStore) {

        LOG.debug("Recover alias information");

        String alias = null;
        try {
            final org.opensaml.xml.signature.X509Certificate xmlCert = keyInfo.getX509Datas().get(0)
                    .getX509Certificates().get(0);

            // Transform the KeyInfo to X509Certificate.
            CertificateFactory certFact;
            certFact = CertificateFactory.getInstance("X.509");

            final ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(xmlCert.getValue()));

            final X509Certificate cert = (X509Certificate) certFact.generateCertificate(bis);

            final String tokenSerialNumber = cert.getSerialNumber().toString(16);
            final X509Principal tokenIssuerDN = new X509Principal(cert.getIssuerDN().getName());

            String aliasCert;
            X509Certificate certificate;
            boolean find = false;

            for (final Enumeration<String> e = storkOwnKeyStore.aliases(); e.hasMoreElements() && !find;) {
                aliasCert = e.nextElement();
                certificate = (X509Certificate) storkOwnKeyStore.getCertificate(aliasCert);

                final String serialNum = certificate.getSerialNumber().toString(16);

                X509Principal issuerDN = new X509Principal(certificate.getIssuerDN().getName());

                if (serialNum.equalsIgnoreCase(tokenSerialNumber)
                        && X509PrincipalUtil.equals2(issuerDN, tokenIssuerDN)) {
                    alias = aliasCert;
                    find = true;
                }

            }

        } catch (KeyStoreException e) {
            LOG.error("Procces getAlias from certificate associated into the signing keystore..", e);
        } catch (CertificateException e) {
            LOG.error("Procces getAlias from certificate associated into the signing keystore..", e);
        } catch (RuntimeException e) {
            LOG.error("Procces getAlias from certificate associated into the signing keystore..", e);
        }
        return alias;
    }

    /**
     * Gets the country from X.509 Certificate.
     * 
     * @param keyInfo the key info
     * 
     * @return the country
     */
    private String getCountry(final KeyInfo keyInfo) {
        LOG.debug("Recover country information.");

        String result = "";
        try {
            final org.opensaml.xml.signature.X509Certificate xmlCert = keyInfo.getX509Datas().get(0)
                    .getX509Certificates().get(0);

            // Transform the KeyInfo to X509Certificate.
            CertificateFactory certFact;
            certFact = CertificateFactory.getInstance("X.509");

            final ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(xmlCert.getValue()));

            final X509Certificate cert = (X509Certificate) certFact.generateCertificate(bis);

            String distName = cert.getSubjectDN().toString();

            distName = StringUtils.deleteWhitespace(StringUtils.upperCase(distName));

            final String countryCode = "C=";
            final int init = distName.indexOf(countryCode);

            if (init > StringUtils.INDEX_NOT_FOUND) { // Exist country code.
                int end = distName.indexOf(',', init);

                if (end <= StringUtils.INDEX_NOT_FOUND) {
                    end = distName.length();
                }

                if (init < end && end > StringUtils.INDEX_NOT_FOUND) {
                    result = distName.substring(init + countryCode.length(), end);
                    //It must be a two characters value
                    if (result.length() > 2) {
                        result = result.substring(0, 2);
                    }
                }
            }

        } catch (CertificateException e) {
            LOG.error("Procces getCountry from certificate.");
        }
        return result.trim();
    }

    /**
     * Process all elements XMLObjects from the extensions.
     * 
     * @param extensions the extensions from the authentication request.
     * 
     * @return the STORK authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private STORKAuthnRequest processExtensions(final Extensions extensions) throws STORKSAMLEngineException {
        LOG.debug("Procces the extensions.");

        final STORKAuthnRequest request = new STORKAuthnRequest();

        final QAAAttribute qaa = (QAAAttribute) extensions.getUnknownXMLObjects(QAAAttribute.DEF_ELEMENT_NAME)
                .get(0);
        request.setQaa(Integer.parseInt(qaa.getQaaLevel()));

        List optionalElements = extensions.getUnknownXMLObjects(SPSector.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPSector sector = (SPSector) extensions.getUnknownXMLObjects(SPSector.DEF_ELEMENT_NAME).get(0);
            request.setSpSector(sector.getSPSector());
        }

        optionalElements = extensions.getUnknownXMLObjects(SPApplication.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPApplication application = (SPApplication) extensions
                    .getUnknownXMLObjects(SPApplication.DEF_ELEMENT_NAME).get(0);
            request.setSpApplication(application.getSPApplication());
        }

        optionalElements = extensions.getUnknownXMLObjects(SPCountry.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPCountry application = (SPCountry) extensions.getUnknownXMLObjects(SPCountry.DEF_ELEMENT_NAME)
                    .get(0);
            request.setSpCountry(application.getSPCountry());
        }

        List listCrossBorderShare = extensions.getUnknownXMLObjects(EIDCrossBorderShare.DEF_ELEMENT_NAME);

        if (!listCrossBorderShare.isEmpty()) {
            final EIDCrossBorderShare crossBorderShare = (EIDCrossBorderShare) listCrossBorderShare.get(0);
            request.setEIDCrossBorderShare(Boolean.parseBoolean(crossBorderShare.getEIDCrossBorderShare()));
        }

        List listCrosSectorShare = extensions.getUnknownXMLObjects(EIDCrossSectorShare.DEF_ELEMENT_NAME);

        if (!listCrosSectorShare.isEmpty()) {
            final EIDCrossSectorShare crossSectorShare = (EIDCrossSectorShare) listCrosSectorShare.get(0);
            request.setEIDCrossSectorShare(Boolean.parseBoolean(crossSectorShare.getEIDCrossSectorShare()));
        }

        List listSectorShareExtension = extensions.getUnknownXMLObjects(EIDSectorShare.DEF_ELEMENT_NAME);
        if (!listSectorShareExtension.isEmpty()) {
            final EIDSectorShare sectorShare = (EIDSectorShare) listSectorShareExtension.get(0);
            request.setEIDSectorShare(Boolean.parseBoolean(sectorShare.getEIDSectorShare()));
        }

        List<XMLObject> authAttrs = extensions.getUnknownXMLObjects(AuthenticationAttributes.DEF_ELEMENT_NAME);

        if (authAttrs != null && !authAttrs.isEmpty()) {

            final AuthenticationAttributes authnAttr = (AuthenticationAttributes) authAttrs.get(0);

            VIDPAuthenticationAttributes vidpAuthnAttr = null;
            if (authnAttr != null && !authAttrs.isEmpty()) {
                vidpAuthnAttr = authnAttr.getVIDPAuthenticationAttributes();
            }

            CitizenCountryCode citizenCountryCodeElement = null;
            SPInformation spInformation = null;
            if (vidpAuthnAttr != null) {
                citizenCountryCodeElement = vidpAuthnAttr.getCitizenCountryCode();
                spInformation = vidpAuthnAttr.getSPInformation();
            }

            String citizenCountryCode = null;
            if (citizenCountryCodeElement != null) {
                citizenCountryCode = citizenCountryCodeElement.getCitizenCountryCode();
            }

            if (citizenCountryCode != null && StringUtils.isNotBlank(citizenCountryCode)) {
                request.setCitizenCountryCode(citizenCountryCode);
            }

            SPID spidElement = null;
            if (spInformation != null) {
                spidElement = spInformation.getSPID();
            }

            String spid = null;
            if (spidElement != null) {
                spid = spidElement.getSPID();
            }

            if (spid != null && StringUtils.isNotBlank(spid)) {
                request.setSPID(spid);
            }
        }

        if (extensions.getUnknownXMLObjects(RequestedAttributes.DEF_ELEMENT_NAME) == null) {
            LOG.error("Extensions not contains any requested attribute.");
            throw new STORKSAMLEngineException("Extensions not contains any requested attribute.");
        }

        final RequestedAttributes requestedAttr = (RequestedAttributes) extensions
                .getUnknownXMLObjects(RequestedAttributes.DEF_ELEMENT_NAME).get(0);

        final List<RequestedAttribute> reqAttrs = requestedAttr.getAttributes();

        final IPersonalAttributeList personalAttrList = new PersonalAttributeList();

        String attributeName;
        for (int nextAttribute = 0; nextAttribute < reqAttrs.size(); nextAttribute++) {
            final RequestedAttribute attribute = reqAttrs.get(nextAttribute);
            final PersonalAttribute personalAttribute = new PersonalAttribute();
            personalAttribute.setIsRequired(Boolean.valueOf(attribute.isRequired()));
            personalAttribute.setFriendlyName(attribute.getFriendlyName());
            attributeName = attribute.getName();

            // recover the last name from the string.
            personalAttribute.setName(attributeName.substring(attributeName.lastIndexOf('/') + 1));

            final ArrayList<String> valores = new ArrayList<String>();
            final List<XMLObject> values = attribute.getOrderedChildren();

            for (int nextSimpleValue = 0; nextSimpleValue < values.size(); nextSimpleValue++) {

                // Process attributes simples. An AuthenticationRequest only
                // must contains simple values.

                final XMLObject xmlObject = values.get(nextSimpleValue);

                if (xmlObject instanceof XSStringImpl) {

                    final XSStringImpl xmlString = (XSStringImpl) values.get(nextSimpleValue);
                    valores.add(xmlString.getValue());

                } else {

                    if (attributeName.equals("http://www.stork.gov.eu/1.0/signedDoc")) {

                        final XSAnyImpl xmlString = (XSAnyImpl) values.get(nextSimpleValue);

                        TransformerFactory transFactory = TransformerFactory.newInstance();
                        Transformer transformer = null;
                        try {
                            transformer = transFactory.newTransformer();
                            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                        } catch (TransformerConfigurationException e) {
                            LOG.error("Error transformer configuration exception", e);
                        }
                        StringWriter buffer = new StringWriter();
                        try {
                            if (xmlString != null && xmlString.getUnknownXMLObjects() != null
                                    && xmlString.getUnknownXMLObjects().size() > 0) {
                                transformer.transform(
                                        new DOMSource(xmlString.getUnknownXMLObjects().get(0).getDOM()),
                                        new StreamResult(buffer));
                            }
                        } catch (TransformerException e) {
                            LOG.error("Error transformer exception", e);
                        }
                        String str = buffer.toString();

                        valores.add(str);

                    } else {

                        final XSAnyImpl xmlString = (XSAnyImpl) values.get(nextSimpleValue);
                        valores.add(xmlString.getTextContent());
                    }

                }
            }
            personalAttribute.setValue(valores);
            personalAttrList.add(personalAttribute);
        }

        request.setPersonalAttributeList(personalAttrList);

        return request;
    }

    /**
     * Process all elements XMLObjects from the extensions.
     * 
     * @param extensions the extensions from the authentication request.
     * 
     * @return the STORK authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private STORKAttrQueryRequest processAttrExtensions(final Extensions extensions)
            throws STORKSAMLEngineException {
        LOG.debug("Procces the atribute query extensions.");

        final STORKAttrQueryRequest request = new STORKAttrQueryRequest();

        final QAAAttribute qaa = (QAAAttribute) extensions.getUnknownXMLObjects(QAAAttribute.DEF_ELEMENT_NAME)
                .get(0);
        request.setQaa(Integer.parseInt(qaa.getQaaLevel()));

        List optionalElements = extensions.getUnknownXMLObjects(SPSector.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPSector sector = (SPSector) extensions.getUnknownXMLObjects(SPSector.DEF_ELEMENT_NAME).get(0);
            request.setSpSector(sector.getSPSector());
        }

        optionalElements = extensions.getUnknownXMLObjects(SPApplication.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPApplication application = (SPApplication) extensions
                    .getUnknownXMLObjects(SPApplication.DEF_ELEMENT_NAME).get(0);
            request.setSpApplication(application.getSPApplication());
        }

        optionalElements = extensions.getUnknownXMLObjects(SPCountry.DEF_ELEMENT_NAME);

        if (!optionalElements.isEmpty()) {
            final SPCountry application = (SPCountry) extensions.getUnknownXMLObjects(SPCountry.DEF_ELEMENT_NAME)
                    .get(0);
            request.setSpCountry(application.getSPCountry());
        }

        List listCrossBorderShare = extensions.getUnknownXMLObjects(EIDCrossBorderShare.DEF_ELEMENT_NAME);

        if (!listCrossBorderShare.isEmpty()) {
            final EIDCrossBorderShare crossBorderShare = (EIDCrossBorderShare) listCrossBorderShare.get(0);
            request.setEIDCrossBorderShare(Boolean.parseBoolean(crossBorderShare.getEIDCrossBorderShare()));
        }

        List listCrosSectorShare = extensions.getUnknownXMLObjects(EIDCrossSectorShare.DEF_ELEMENT_NAME);

        if (!listCrosSectorShare.isEmpty()) {
            final EIDCrossSectorShare crossSectorShare = (EIDCrossSectorShare) listCrosSectorShare.get(0);
            request.setEIDCrossSectorShare(Boolean.parseBoolean(crossSectorShare.getEIDCrossSectorShare()));
        }

        List listSectorShareExtension = extensions.getUnknownXMLObjects(EIDSectorShare.DEF_ELEMENT_NAME);
        if (!listSectorShareExtension.isEmpty()) {
            final EIDSectorShare sectorShare = (EIDSectorShare) listSectorShareExtension.get(0);
            request.setEIDSectorShare(Boolean.parseBoolean(sectorShare.getEIDSectorShare()));
        }

        List<XMLObject> authAttrs = extensions.getUnknownXMLObjects(AuthenticationAttributes.DEF_ELEMENT_NAME);

        if (authAttrs != null && !authAttrs.isEmpty()) {

            final AuthenticationAttributes authnAttr = (AuthenticationAttributes) authAttrs.get(0);

            VIDPAuthenticationAttributes vidpAuthnAttr = null;
            if (authnAttr != null && !authAttrs.isEmpty()) {
                vidpAuthnAttr = authnAttr.getVIDPAuthenticationAttributes();
            }

            CitizenCountryCode citizenCountryCodeElement = null;
            SPInformation spInformation = null;
            if (vidpAuthnAttr != null) {
                citizenCountryCodeElement = vidpAuthnAttr.getCitizenCountryCode();
                spInformation = vidpAuthnAttr.getSPInformation();
            }

            String citizenCountryCode = null;
            if (citizenCountryCodeElement != null) {
                citizenCountryCode = citizenCountryCodeElement.getCitizenCountryCode();
            }

            if (citizenCountryCode != null && StringUtils.isNotBlank(citizenCountryCode)) {
                request.setCitizenCountryCode(citizenCountryCode);
            }

            SPID spidElement = null;
            if (spInformation != null) {
                spidElement = spInformation.getSPID();
            }

            String spid = null;
            if (spidElement != null) {
                spid = spidElement.getSPID();
            }

            if (spid != null && StringUtils.isNotBlank(spid)) {
                request.setSPID(spid);
            }
        }

        if (extensions.getUnknownXMLObjects(RequestedAttributes.DEF_ELEMENT_NAME) == null) {
            LOG.error("Extensions not contains any requested attribute.");
            throw new STORKSAMLEngineException("Extensions not contains any requested attribute.");
        }

        final RequestedAttributes requestedAttr = (RequestedAttributes) extensions
                .getUnknownXMLObjects(RequestedAttributes.DEF_ELEMENT_NAME).get(0);

        final List<RequestedAttribute> reqAttrs = requestedAttr.getAttributes();

        final IPersonalAttributeList personalAttrList = new PersonalAttributeList();

        String attributeName;
        for (int nextAttribute = 0; nextAttribute < reqAttrs.size(); nextAttribute++) {
            final RequestedAttribute attribute = reqAttrs.get(nextAttribute);
            final PersonalAttribute personalAttribute = new PersonalAttribute();
            personalAttribute.setIsRequired(Boolean.valueOf(attribute.isRequired()));
            personalAttribute.setFriendlyName(attribute.getFriendlyName());
            attributeName = attribute.getName();

            // recover the last name from the string.
            personalAttribute.setName(attributeName.substring(attributeName.lastIndexOf('/') + 1));

            final ArrayList<String> valores = new ArrayList<String>();
            final List<XMLObject> values = attribute.getOrderedChildren();

            for (int nextSimpleValue = 0; nextSimpleValue < values.size(); nextSimpleValue++) {

                // Process attributes simples. An AuthenticationRequest only
                // must contains simple values.

                final XMLObject xmlObject = values.get(nextSimpleValue);

                if (xmlObject instanceof XSStringImpl) {

                    final XSStringImpl xmlString = (XSStringImpl) values.get(nextSimpleValue);
                    valores.add(xmlString.getValue());

                } else {

                    if (attributeName.equals("http://www.stork.gov.eu/1.0/signedDoc")) {

                        final XSAnyImpl xmlString = (XSAnyImpl) values.get(nextSimpleValue);

                        TransformerFactory transFactory = TransformerFactory.newInstance();
                        Transformer transformer = null;
                        try {
                            transformer = transFactory.newTransformer();
                            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
                        } catch (TransformerConfigurationException e) {
                            LOG.error("Error transformer configuration exception", e);
                        }
                        StringWriter buffer = new StringWriter();
                        try {
                            if (xmlString != null && xmlString.getUnknownXMLObjects() != null
                                    && xmlString.getUnknownXMLObjects().size() > 0) {
                                transformer.transform(
                                        new DOMSource(xmlString.getUnknownXMLObjects().get(0).getDOM()),
                                        new StreamResult(buffer));
                            }
                        } catch (TransformerException e) {
                            LOG.error("Error transformer exception", e);
                        }
                        String str = buffer.toString();

                        valores.add(str);

                    } else {

                        final XSAnyImpl xmlString = (XSAnyImpl) values.get(nextSimpleValue);
                        valores.add(xmlString.getTextContent());
                    }

                }
            }
            personalAttribute.setValue(valores);
            personalAttrList.add(personalAttribute);
        }

        request.setPersonalAttributeList(personalAttrList);

        return request;
    }

    /**
     * Sets the name spaces.
     * 
     * @param tokenSaml the new name spaces
     */
    private void setNameSpaces(final XMLObject tokenSaml) {
        LOG.debug("Set namespaces.");

        final Namespace saml2 = new Namespace(SAMLConstants.SAML20_NS, SAMLConstants.SAML20_PREFIX);
        tokenSaml.addNamespace(saml2);

        final Namespace digSig = new Namespace("http://www.w3.org/2000/09/xmldsig#", "ds");
        tokenSaml.addNamespace(digSig);

        final Namespace storkp = new Namespace(SAMLCore.STORK10P_NS.getValue(),
                SAMLCore.STORK10P_PREFIX.getValue());
        tokenSaml.addNamespace(storkp);

        final Namespace stork = new Namespace(SAMLCore.STORK10_NS.getValue(), SAMLCore.STORK10_PREFIX.getValue());

        tokenSaml.addNamespace(stork);
    }

    /**
     * Validate parameters from authentication request.
     * 
     * @param request the request.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamAuthnReq(final STORKAuthnRequest request) throws STORKSAMLEngineException {
        LOG.info("Validate parameters from authentication request.");

        // URL to which Authentication Response must be sent.
        if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: Assertion Consumer Service URL it's mandatory.");
        }

        // the name of the original service provider requesting the
        // authentication.
        if (StringUtils.isBlank(request.getProviderName())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: Service Provider it's mandatory.");
        }

        // object that contain all attributes requesting.
        if (request.getPersonalAttributeList() == null || request.getPersonalAttributeList().isEmpty()) {
            throw new STORKSAMLEngineException("attributeQueries is null or empty.");
        }

        // Quality authentication assurance level.
        if ((request.getQaa() < QAAAttribute.MIN_VALUE) || (request.getQaa() > QAAAttribute.MAX_VALUE)) {
            throw new STORKSAMLEngineException("Qaal: " + request.getQaa() + ", is invalid.");
        }

    }

    /**
     * Validate parameters from attribute query request.
     * 
     * @param request the request.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamAttrQueryReq(final STORKAttrQueryRequest request) throws STORKSAMLEngineException {
        LOG.info("Validate parameters from attribute query request.");

        // URL to which AP Response must be sent.
        if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: Assertion Consumer Service URL it's mandatory.");
        }

        // Destination of the request - not mandatory
        /*if (StringUtils.isBlank(request.getDestination())) {
           throw new STORKSAMLEngineException(
           "StorkSamlEngine: Destination is mandatory.");
        }*/

        // SP country is empty
        if (StringUtils.isBlank(request.getSpCountry())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: SP country is mandatory.");
        }

        // object that contain all attributes requesting.
        if (request.getPersonalAttributeList() == null || request.getPersonalAttributeList().isEmpty()) {
            throw new STORKSAMLEngineException("attributeQueries is null or empty.");
        }

        // Quality authentication assurance level.
        if ((request.getQaa() < QAAAttribute.MIN_VALUE) || (request.getQaa() > QAAAttribute.MAX_VALUE)) {
            throw new STORKSAMLEngineException("Qaal: " + request.getQaa() + ", is invalid.");
        }
    }

    /**
     * Validate parameters from logout request.
     * 
     * @param request the request.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamLogoutReq(final STORKLogoutRequest request) throws STORKSAMLEngineException {
        LOG.info("Validate parameters from logout request.");

        // URL to which AP Response must be sent.
        /*if (StringUtils.isBlank(request.get())) {
           throw new STORKSAMLEngineException(
           "StorkSamlEngine: Assertion Consumer Service URL it's mandatory.");
        }*/

        // Destination of the request
        if (StringUtils.isBlank(request.getDestination())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: Destination is mandatory.");
        }

        // SP Provided Id
        if (StringUtils.isBlank(request.getSpProvidedId())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: SP provided Id is mandatory.");
        }
    }

    /**
     * Validate parameters from logout response.
     * 
     * @param response the response.
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamLogoutRes(final STORKLogoutRequest request, final STORKLogoutResponse response)
            throws STORKSAMLEngineException {
        LOG.info("Validate parameters from logout request.");

        // Issuer is mandatory
        if (StringUtils.isBlank(request.getIssuer())) {
            throw new STORKSAMLEngineException("Issuer must be not empty or null.");
        }

        // Destination of the request
        if (StringUtils.isBlank(response.getDestination())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: Destination is mandatory.");
        }

        // SP Provided Id
        if (StringUtils.isBlank(request.getSpProvidedId())) {
            throw new STORKSAMLEngineException("StorkSamlEngine: SP provided Id is mandatory.");
        }

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameters from response.
     * 
     * @param request the request
     * @param responseAuthReq the response authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamResponse(final STORKAuthnRequest request, final STORKAuthnResponse responseAuthReq)
            throws STORKSAMLEngineException {
        LOG.info("Validate parameters response.");
        if (StringUtils.isBlank(request.getIssuer())) {
            throw new STORKSAMLEngineException("Issuer must be not empty or null.");
        }

        if (responseAuthReq.getPersonalAttributeList() == null
                || responseAuthReq.getPersonalAttributeList().isEmpty()) {
            LOG.error("PersonalAttributeList is null or empty.");
            throw new STORKSAMLEngineException("PersonalAttributeList is null or empty.");
        }

        if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
            throw new STORKSAMLEngineException("assertionConsumerServiceURL is null or empty.");
        }

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameters from response.
     * 
     * @param request the request
     * @param responseAttrQueryReq the response authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamAttrQueryResponse(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse responseAttrQueryReq) throws STORKSAMLEngineException {
        LOG.info("Validate attribute querey parameters response.");
        if (StringUtils.isBlank(request.getIssuer())) {
            throw new STORKSAMLEngineException("Issuer must be not empty or null.");
        }

        if (responseAttrQueryReq.getPersonalAttributeList() == null
                || responseAttrQueryReq.getPersonalAttributeList().isEmpty()) {
            LOG.error("PersonalAttributeList is null or empty.");
            throw new STORKSAMLEngineException("PersonalAttributeList is null or empty.");
        }

        /*if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
           throw new STORKSAMLEngineException(
           "assertionConsumerServiceURL is null or empty.");
        }*/

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameters from response.
     * 
     * @param request the request
     * @param responseAttrQueryReq the response authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamAttrQueryResponseFromAssertions(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse responseAttrQueryReq) throws STORKSAMLEngineException {
        LOG.info("Validate attribute querey parameters response.");
        if (StringUtils.isBlank(request.getIssuer())) {
            throw new STORKSAMLEngineException("Issuer must be not empty or null.");
        }

        /*if (responseAttrQueryReq.getPersonalAttributeList() == null
        || responseAttrQueryReq.getPersonalAttributeList().isEmpty()) {
           LOG.error("PersonalAttributeList is null or empty.");
           throw new STORKSAMLEngineException(
           "PersonalAttributeList is null or empty.");
        }*/

        /*if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
           throw new STORKSAMLEngineException(
           "assertionConsumerServiceURL is null or empty.");
        }*/

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameter from response fail.
     * 
     * @param request the request
     * @param response the response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamResponseFail(final STORKAuthnRequest request, final STORKAuthnResponse response)
            throws STORKSAMLEngineException {
        LOG.info("Validate parameters response fail.");
        if (StringUtils.isBlank(response.getStatusCode())) {
            throw new STORKSAMLEngineException("Code error it's null or empty.");
        }

        if (StringUtils.isBlank(request.getAssertionConsumerServiceURL())) {
            throw new STORKSAMLEngineException("assertionConsumerServiceURL is null or empty.");
        }

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameter from response fail.
     * 
     * @param request the request
     * @param response the response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamAttrQueryResponseFail(final STORKAttrQueryRequest request,
            final STORKAttrQueryResponse response) throws STORKSAMLEngineException {
        LOG.info("Validate parameters response fail.");
        if (StringUtils.isBlank(response.getStatusCode())) {
            throw new STORKSAMLEngineException("Code error it's null or empty.");
        }

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate parameter from response fail.
     * 
     * @param request the request
     * @param response the response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private void validateParamLogoutResFail(final STORKLogoutRequest request, final STORKLogoutResponse response)
            throws STORKSAMLEngineException {
        LOG.info("Validate parameters response fail.");

        if (StringUtils.isBlank(request.getIssuer())) {
            throw new STORKSAMLEngineException("Issuer must be not empty or null.");
        }

        if (StringUtils.isBlank(response.getStatusCode())) {
            throw new STORKSAMLEngineException("Code error it's null or empty.");
        }

        if (StringUtils.isBlank(request.getSamlId())) {
            throw new STORKSAMLEngineException("request ID is null or empty.");
        }
    }

    /**
     * Validate stork authentication request.
     * 
     * @param tokenSaml the token SAML
     * 
     * @return the sTORK authentication request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnRequest validateSTORKAuthnRequest(final byte[] tokenSaml) throws STORKSAMLEngineException {
        LOG.info("validateSTORKAuthnRequest");

        final AuthnRequest samlRequest = (AuthnRequest) validateStorkSaml(tokenSaml);

        LOG.debug("Validate Extensions.");
        final Validator<Extensions> validatorExt = new ExtensionsSchemaValidator();
        try {
            validatorExt.validate(samlRequest.getExtensions());
        } catch (ValidationException e) {
            LOG.error("ValidationException: validate Extensions.", e);
            throw new STORKSAMLEngineException(e);
        }

        LOG.debug("Generate STORKAuthnRequest.");
        final STORKAuthnRequest authnRequest = processExtensions(samlRequest.getExtensions());

        authnRequest.setCountry(this.getCountry(samlRequest.getSignature().getKeyInfo()));

        authnRequest.setAlias(
                this.getAlias(samlRequest.getSignature().getKeyInfo(), super.getSigner().getTrustStore()));

        authnRequest.setSamlId(samlRequest.getID());
        authnRequest.setDestination(samlRequest.getDestination());
        authnRequest.setAssertionConsumerServiceURL(samlRequest.getAssertionConsumerServiceURL());

        authnRequest.setProviderName(samlRequest.getProviderName());
        authnRequest.setIssuer(samlRequest.getIssuer().getValue());

        //Delete unknown elements from requested ones
        final Iterator<PersonalAttribute> iterator = authnRequest.getPersonalAttributeList().iterator();
        IPersonalAttributeList cleanPerAttrList = (PersonalAttributeList) authnRequest.getPersonalAttributeList();
        while (iterator.hasNext()) {

            final PersonalAttribute attribute = iterator.next();

            // Verify if the attribute name exits.
            final String attributeName = super.getSamlCoreProperties().getProperty(attribute.getName());

            if (StringUtils.isBlank(attributeName)) {
                LOG.info("Attribute name: {} was not found. It will be removed from the request object",
                        attribute.getName());
                cleanPerAttrList.remove(attribute.getName());
            }

        }
        authnRequest.setPersonalAttributeList(cleanPerAttrList);

        return authnRequest;

    }

    /**
     * Validate stork attribute query request.
     * 
     * @param tokenSaml the token SAML
     * 
     * @return the STORK attribute query request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryRequest validateSTORKAttrQueryRequest(final byte[] tokenSaml)
            throws STORKSAMLEngineException {
        LOG.info("validateSTORKAttrQueryRequest");

        //final AttributeQuery samlRequest = (AttributeQuery) validateStorkSaml(tokenSaml);
        final CustomRequestAbstractType samlRequest = (CustomRequestAbstractType) validateStorkSaml(tokenSaml);

        LOG.debug("Validate Extensions.");
        final Validator<Extensions> validatorExt = new ExtensionsSchemaValidator();
        try {
            validatorExt.validate(samlRequest.getExtensions());
        } catch (ValidationException e) {
            LOG.error("ValidationException: validate Extensions.", e);
            throw new STORKSAMLEngineException(e);
        }

        LOG.debug("Generate STORKAttrQueryRequest.");
        final STORKAttrQueryRequest attrRequest = processAttrExtensions(samlRequest.getExtensions());

        attrRequest.setCountry(this.getCountry(samlRequest.getSignature().getKeyInfo()));

        attrRequest.setAlias(
                this.getAlias(samlRequest.getSignature().getKeyInfo(), super.getSigner().getTrustStore()));

        attrRequest.setSamlId(samlRequest.getID());
        attrRequest.setDestination(samlRequest.getDestination());
        attrRequest.setAssertionConsumerServiceURL(samlRequest.getAssertionConsumerServiceURL());

        /*authnRequest.setProviderName(samlRequest.getProviderName());*/
        attrRequest.setIssuer(samlRequest.getIssuer().getValue());

        //Delete unknown elements from requested ones
        final Iterator<PersonalAttribute> iterator = attrRequest.getPersonalAttributeList().iterator();
        IPersonalAttributeList cleanPerAttrList = (PersonalAttributeList) attrRequest.getPersonalAttributeList();
        while (iterator.hasNext()) {

            final PersonalAttribute attribute = iterator.next();

            // Verify if the attribute name exits.
            final String attributeName = super.getSamlCoreProperties().getProperty(attribute.getName());

            if (StringUtils.isBlank(attributeName)) {
                LOG.info("Attribute name: {} was not found. It will be removed from the request object",
                        attribute.getName());
                cleanPerAttrList.remove(attribute.getName());
            }

        }
        attrRequest.setPersonalAttributeList(cleanPerAttrList);

        return attrRequest;

    }

    /**
     * Validate stork logout request.
     * 
     * @param tokenSaml the token SAML
     * 
     * @return the STORK logout request
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKLogoutRequest validateSTORKLogoutRequest(final byte[] tokenSaml) throws STORKSAMLEngineException {
        LOG.info("validateSTORKLogoutRequest");

        final LogoutRequest samlRequest = (LogoutRequest) validateStorkSaml(tokenSaml);

        LOG.debug("Validate Extensions.");
        final Validator<Extensions> validatorExt = new ExtensionsSchemaValidator();
        try {
            validatorExt.validate(samlRequest.getExtensions());
        } catch (ValidationException e) {
            LOG.error("ValidationException: validate Extensions.", e);
            throw new STORKSAMLEngineException(e);
        }

        LOG.debug("Generate STORKLogoutRequest.");
        final STORKLogoutRequest logoutRequest = new STORKLogoutRequest();

        logoutRequest.setCountry(this.getCountry(samlRequest.getSignature().getKeyInfo()));

        logoutRequest.setAlias(
                this.getAlias(samlRequest.getSignature().getKeyInfo(), super.getSigner().getTrustStore()));

        logoutRequest.setSamlId(samlRequest.getID());
        logoutRequest.setDestination(samlRequest.getDestination());

        logoutRequest.setIssuer(samlRequest.getIssuer().getValue());

        logoutRequest.setSpProvidedId(samlRequest.getNameID().getValue());

        return logoutRequest;

    }

    /**
     * Validate stork authentication response.
     * 
     * @param tokenSaml the token SAML
     * @param userIP the user IP
     * 
     * @return the Stork authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnResponse validateSTORKAuthnResponse(final byte[] tokenSaml, final String userIP)
            throws STORKSAMLEngineException {

        LOG.info("validateSTORKAuthnResponse");
        final Response samlResponse = (Response) validateStorkSaml(tokenSaml);

        LOG.debug("Create StorkAuthResponse.");
        final STORKAuthnResponse authnResponse = new STORKAuthnResponse();

        authnResponse.setCountry(this.getCountry(samlResponse.getSignature().getKeyInfo()));

        LOG.debug("Set ID.");
        authnResponse.setSamlId(samlResponse.getID());
        LOG.debug("Set InResponseTo.");
        authnResponse.setInResponseTo(samlResponse.getInResponseTo());
        LOG.debug("Set statusCode.");
        authnResponse.setStatusCode(samlResponse.getStatus().getStatusCode().getValue());

        // Subordinate code.
        if (samlResponse.getStatus().getStatusCode().getStatusCode() != null) {
            authnResponse.setSubStatusCode(samlResponse.getStatus().getStatusCode().getStatusCode().getValue());
        }

        if (samlResponse.getStatus().getStatusMessage() != null) {
            LOG.debug("Set statusMessage.");
            authnResponse.setMessage(samlResponse.getStatus().getStatusMessage().getMessage());
        }

        LOG.debug("validateStorkResponse");
        final Assertion assertion = (Assertion) validateStorkResponse(samlResponse, userIP);

        if (assertion != null) {
            final DateTime serverDate = new DateTime();

            if (assertion.getConditions().getNotOnOrAfter().isBefore(serverDate)) {
                LOG.error("Token date expired (getNotOnOrAfter =  " + assertion.getConditions().getNotOnOrAfter()
                        + ", server_date: " + serverDate + ")");
                throw new STORKSAMLEngineException("Token date expired (getNotOnOrAfter =  "
                        + assertion.getConditions().getNotOnOrAfter() + " ), server_date: " + serverDate);
            }

            LOG.debug("Set notOnOrAfter.");
            authnResponse.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter());

            LOG.debug("Set notBefore.");
            authnResponse.setNotBefore(assertion.getConditions().getNotBefore());

            authnResponse.setNotBefore(assertion.getConditions().getNotBefore());

            authnResponse.setAudienceRestriction(
                    ((AudienceRestriction) assertion.getConditions().getAudienceRestrictions().get(0))
                            .getAudiences().get(0).getAudienceURI());
            authnResponse.setAssertions(samlResponse.getAssertions());
        }

        // Case no error.
        if (assertion != null && StatusCode.SUCCESS_URI.equalsIgnoreCase(authnResponse.getStatusCode())) {
            LOG.debug("Status Success. Set PersonalAttributeList.");
            authnResponse.setPersonalAttributeList(generatePersonalAttributeList(assertion));
            authnResponse.setFail(false);
        } else {
            LOG.debug("Status Fail.");
            authnResponse.setFail(true);
        }
        LOG.debug("Return result.");
        return authnResponse;

    }

    /**
     * Validate stork authentication response.
     * 
     * @param tokenSaml the token SAML
     * @param userIP the user IP
     * 
     * @return the Stork authentication response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAuthnResponse validateSTORKAuthnResponseWithQuery(final byte[] tokenSaml, final String userIP)
            throws STORKSAMLEngineException {

        LOG.info("validateSTORKAuthnResponse");
        final Response samlResponse = (Response) validateStorkSaml(tokenSaml);

        LOG.debug("Create StorkAuthResponse.");
        final STORKAuthnResponse authnResponse = new STORKAuthnResponse();

        authnResponse.setCountry(this.getCountry(samlResponse.getSignature().getKeyInfo()));

        LOG.debug("Set ID.");
        authnResponse.setSamlId(samlResponse.getID());
        LOG.debug("Set InResponseTo.");
        authnResponse.setInResponseTo(samlResponse.getInResponseTo());
        LOG.debug("Set statusCode.");
        authnResponse.setStatusCode(samlResponse.getStatus().getStatusCode().getValue());

        // Subordinate code.
        if (samlResponse.getStatus().getStatusCode().getStatusCode() != null) {
            authnResponse.setSubStatusCode(samlResponse.getStatus().getStatusCode().getStatusCode().getValue());
        }

        if (samlResponse.getStatus().getStatusMessage() != null) {
            LOG.debug("Set statusMessage.");
            authnResponse.setMessage(samlResponse.getStatus().getStatusMessage().getMessage());
        }

        LOG.debug("validateStorkResponse");
        final Assertion assertion = (Assertion) validateStorkResponse(samlResponse, userIP);

        if (assertion != null) {
            final DateTime serverDate = new DateTime();

            if (assertion.getConditions().getNotOnOrAfter().isBefore(serverDate)) {
                LOG.error("Token date expired (getNotOnOrAfter =  " + assertion.getConditions().getNotOnOrAfter()
                        + ", server_date: " + serverDate + ")");
                throw new STORKSAMLEngineException("Token date expired (getNotOnOrAfter =  "
                        + assertion.getConditions().getNotOnOrAfter() + " ), server_date: " + serverDate);
            }

            LOG.debug("Set notOnOrAfter.");
            authnResponse.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter());

            LOG.debug("Set notBefore.");
            authnResponse.setNotBefore(assertion.getConditions().getNotBefore());

            authnResponse.setNotBefore(assertion.getConditions().getNotBefore());

            authnResponse.setAudienceRestriction(
                    ((AudienceRestriction) assertion.getConditions().getAudienceRestrictions().get(0))
                            .getAudiences().get(0).getAudienceURI());
        }

        // Case no error.
        if (assertion != null && StatusCode.SUCCESS_URI.equalsIgnoreCase(authnResponse.getStatusCode())) {
            LOG.debug("Status Success. Set PersonalAttributeList.");
            authnResponse.setPersonalAttributeList(generatePersonalAttributeList(assertion));
            authnResponse.setFail(false);
        } else {
            LOG.debug("Status Fail.");
            authnResponse.setFail(true);
        }

        authnResponse.setAssertions(samlResponse.getAssertions());
        if (samlResponse.getAssertions().size() > 1) {
            PersonalAttributeList total = new PersonalAttributeList();
            List<IPersonalAttributeList> attrList = new ArrayList();
            for (int i = 0; i < samlResponse.getAssertions().size(); i++) {
                Assertion tempAssertion = (Assertion) samlResponse.getAssertions().get(i);
                IPersonalAttributeList temp = generatePersonalAttributeList(tempAssertion);
                if (temp != null) {
                    attrList.add(temp);
                    for (PersonalAttribute attribute : temp) {
                        PersonalAttribute attr = (PersonalAttribute) attribute.clone();
                        attr.setName(attr.getName() + tempAssertion.getID());
                        total.add(attr);
                    }
                }
            }
            authnResponse.setPersonalAttributeLists(attrList);
            authnResponse.setTotalPersonalAttributeList(total);
        }

        LOG.debug("Return result.");
        return authnResponse;

    }

    /**
     * Validate stork attribute query response.
     * 
     * @param tokenSaml the token SAML
     * @param userIP the user IP
     * 
     * @return the Stork attribute query response
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    public STORKAttrQueryResponse validateSTORKAttrQueryResponse(final byte[] tokenSaml, final String userIP)
            throws STORKSAMLEngineException {

        LOG.info("validateSTORKAttrQueryResponse");
        final Response samlResponse = (Response) validateStorkSaml(tokenSaml);

        LOG.debug("Create StorkAttrQueryResponse.");
        final STORKAttrQueryResponse attrQueryResponse = new STORKAttrQueryResponse();

        attrQueryResponse.setCountry(this.getCountry(samlResponse.getSignature().getKeyInfo()));

        LOG.debug("Set ID.");
        attrQueryResponse.setSamlId(samlResponse.getID());
        LOG.debug("Set InResponseTo.");
        attrQueryResponse.setInResponseTo(samlResponse.getInResponseTo());
        LOG.debug("Set statusCode.");
        attrQueryResponse.setStatusCode(samlResponse.getStatus().getStatusCode().getValue());

        // Subordinate code.
        if (samlResponse.getStatus().getStatusCode().getStatusCode() != null) {
            attrQueryResponse.setSubStatusCode(samlResponse.getStatus().getStatusCode().getStatusCode().getValue());
        }

        if (samlResponse.getStatus().getStatusMessage() != null) {
            LOG.debug("Set statusMessage.");
            attrQueryResponse.setMessage(samlResponse.getStatus().getStatusMessage().getMessage());
        }

        LOG.debug("validateStorkResponse");
        final Assertion assertion = (Assertion) validateStorkResponse(samlResponse, userIP);

        if (assertion != null) {
            final DateTime serverDate = new DateTime();

            attrQueryResponse.setAssertion(assertion);

            if (assertion.getConditions().getNotOnOrAfter().isBefore(serverDate)) {
                LOG.error("Token date expired (getNotOnOrAfter =  " + assertion.getConditions().getNotOnOrAfter()
                        + ", server_date: " + serverDate + ")");
                throw new STORKSAMLEngineException("Token date expired (getNotOnOrAfter =  "
                        + assertion.getConditions().getNotOnOrAfter() + " ), server_date: " + serverDate);
            }

            LOG.debug("Set notOnOrAfter.");
            attrQueryResponse.setNotOnOrAfter(assertion.getConditions().getNotOnOrAfter());

            LOG.debug("Set notBefore.");
            attrQueryResponse.setNotBefore(assertion.getConditions().getNotBefore());

            attrQueryResponse.setNotBefore(assertion.getConditions().getNotBefore());

            attrQueryResponse.setAudienceRestriction(
                    ((AudienceRestriction) assertion.getConditions().getAudienceRestrictions().get(0))
                            .getAudiences().get(0).getAudienceURI());
        }

        // Case no error.
        if (assertion != null && StatusCode.SUCCESS_URI.equalsIgnoreCase(attrQueryResponse.getStatusCode())) {
            LOG.debug("Status Success. Set PersonalAttributeList.");
            attrQueryResponse.setPersonalAttributeList(generatePersonalAttributeList(assertion));
            attrQueryResponse.setFail(false);
        } else {
            LOG.debug("Status Fail.");
            attrQueryResponse.setFail(true);
        }

        attrQueryResponse.setAssertions(samlResponse.getAssertions());
        if (samlResponse.getAssertions().size() > 1) {
            PersonalAttributeList total = new PersonalAttributeList();
            List<IPersonalAttributeList> attrList = new ArrayList();
            for (int i = 0; i < samlResponse.getAssertions().size(); i++) {
                Assertion tempAssertion = (Assertion) samlResponse.getAssertions().get(i);
                IPersonalAttributeList temp = generatePersonalAttributeList(tempAssertion);
                if (temp != null) {
                    attrList.add(temp);
                    for (PersonalAttribute attribute : temp) {
                        PersonalAttribute attr = (PersonalAttribute) attribute.clone();
                        attr.setName(attr.getName() + tempAssertion.getID());
                        total.add(attr);
                    }
                }
            }
            attrQueryResponse.setPersonalAttributeLists(attrList);
            attrQueryResponse.setTotalPersonalAttributeList(total);
        }

        LOG.debug("Return result.");
        return attrQueryResponse;

    }

    /**
     * Validate stork response.
     * 
     * @param samlResponse the SAML response
     * @param userIP the user IP
     * 
     * @return the assertion
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private Assertion validateStorkResponse(final Response samlResponse, final String userIP)
            throws STORKSAMLEngineException {
        // Exist only one Assertion

        if (samlResponse.getAssertions() == null || samlResponse.getAssertions().isEmpty()) {
            LOG.info("Assertion is null or empty."); //in replace of throw new STORKSAMLEngineException("Assertion is null or empty.")
            return null;
        }

        final Assertion assertion = (Assertion) samlResponse.getAssertions().get(0);

        LOG.debug("Verified method Bearer");
        for (final Iterator<SubjectConfirmation> iter = assertion.getSubject().getSubjectConfirmations()
                .iterator(); iter.hasNext();) {
            final SubjectConfirmation element = iter.next();
            final boolean isBearer = SubjectConfirmation.METHOD_BEARER.equals(element.getMethod());

            final boolean ipValidate = super.getSamlCoreProperties().isIpValidation();

            if (ipValidate) {
                if (isBearer) {
                    if (StringUtils.isBlank(userIP)) {
                        LOG.error("browser_ip is null or empty.");
                        throw new STORKSAMLEngineException("browser_ip is null or empty.");
                    } else if (StringUtils.isBlank(element.getSubjectConfirmationData().getAddress())) {
                        LOG.error("token_ip attribute is null or empty.");
                        throw new STORKSAMLEngineException("token_ip attribute is null or empty.");
                    }
                }

                final boolean ipEqual = element.getSubjectConfirmationData().getAddress().equals(userIP);

                // Validation ipUser
                if (!ipEqual && ipValidate) {
                    LOG.error("SubjectConfirmation BEARER: ");
                    throw new STORKSAMLEngineException("IPs doesn't match : token_ip ("
                            + element.getSubjectConfirmationData().getAddress() + ") browser_ip (" + userIP + ")");
                }
            }

        }
        return assertion;
    }

    /**
     * Validate stork SAML.
     * 
     * @param tokenSaml the token SAML
     * 
     * @return the signable SAML object
     * 
     * @throws STORKSAMLEngineException the STORKSAML engine exception
     */
    private SignableSAMLObject validateStorkSaml(final byte[] tokenSaml) throws STORKSAMLEngineException {

        LOG.info("Validate StorkSaml message.");

        if (tokenSaml == null) {
            LOG.error("Saml authentication request is null.");
            throw new STORKSAMLEngineException("Saml authentication request is null.");
        }

        LOG.debug("Generate AuthnRequest from request.");
        SignableSAMLObject samlObject;

        try {
            samlObject = (SignableSAMLObject) super.unmarshall(tokenSaml);
        } catch (SAMLEngineException e) {
            LOG.error("SAMLEngineException unmarshall.", e);
            throw new STORKSAMLEngineException(e);
        }

        boolean validateSign = true;

        if (StringUtils.isNotBlank(super.getSamlCoreProperties().getProperty("validateSignature"))) {
            validateSign = Boolean.valueOf(super.getSamlCoreProperties().getProperty("validateSignature"));
        }

        if (validateSign) {
            LOG.debug("Validate Signature.");
            try {
                super.validateSignature(samlObject);
            } catch (SAMLEngineException e) {
                LOG.error("SAMLEngineException validateSignature.", e);
                throw new STORKSAMLEngineException(e);
            }
        }

        LOG.debug("Validate Schema.");
        final ValidatorSuite validatorSuite = Configuration.getValidatorSuite("saml2-core-schema-validator");
        try {
            if (samlObject.getElementQName().toString().endsWith(CustomAttributeQuery.DEFAULT_ELEMENT_LOCAL_NAME)) {
                CustomAttributeQueryValidator val = new CustomAttributeQueryValidator();
                val.validate((CustomAttributeQuery) samlObject);
            } else if (samlObject instanceof Response && ((Response) samlObject).getAssertions().size() > 1) {
                MultipleAssertionResponseValidator val = new MultipleAssertionResponseValidator();
                val.validate((Response) samlObject);
            } else
                validatorSuite.validate(samlObject);
        } catch (ValidationException e) {
            LOG.error("ValidationException.", e);
            throw new STORKSAMLEngineException(e);
        }

        return samlObject;
    }

    private boolean isComplex(XMLObject xmlObject) {
        boolean isComplex = false;

        final XSAnyImpl complexValue = (XSAnyImpl) xmlObject;

        for (int nextComplexValue = 0; nextComplexValue < complexValue.getUnknownXMLObjects()
                .size(); nextComplexValue++) {

            final XSAnyImpl simple = (XSAnyImpl) complexValue.getUnknownXMLObjects().get(nextComplexValue);

            if (simple.getElementQName().getLocalPart() != null) {
                isComplex = true;
                break;
            }
        }

        return isComplex;
    }
}