eu.eidas.auth.engine.SAMLEngineUtils.java Source code

Java tutorial

Introduction

Here is the source code for eu.eidas.auth.engine.SAMLEngineUtils.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.eidas.auth.engine;

import eu.eidas.auth.commons.DocumentBuilderFactoryUtil;
import eu.eidas.auth.commons.EidasLoaCompareType;
import eu.eidas.auth.commons.EIDASErrors;
import eu.eidas.auth.commons.PersonalAttribute;
import eu.eidas.auth.commons.EIDASAuthnRequest;
import eu.eidas.auth.engine.core.*;
import eu.eidas.auth.engine.core.eidas.EidasConstants;
import eu.eidas.auth.engine.core.stork.*;
import eu.eidas.auth.engine.core.stork.RequestedAttribute;
import eu.eidas.auth.engine.core.validator.eidas.EIDASAttributes;
import eu.eidas.engine.exceptions.SAMLEngineException;
import eu.eidas.engine.exceptions.EIDASSAMLEngineException;
import eu.eidas.engine.exceptions.EIDASSAMLEngineRuntimeException;

import org.apache.commons.lang.StringUtils;
import org.bouncycastle.asn1.x500.X500Name;
import org.joda.time.DateTime;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLVersion;
import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.saml2.common.Extensions;
import org.opensaml.saml2.common.impl.ExtensionsBuilder;
import org.opensaml.saml2.core.*;
import org.opensaml.saml2.core.impl.AssertionBuilder;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.samlext.saml2mdattr.EntityAttributes;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.xml.XMLObject;
import org.opensaml.xml.XMLObjectBuilder;
import org.opensaml.xml.XMLObjectBuilderFactory;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.schema.XSAny;
import org.opensaml.xml.schema.XSString;
import org.opensaml.xml.security.BasicSecurityConfiguration;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.trust.ExplicitKeyTrustEvaluator;
import org.opensaml.xml.security.x509.BasicX509Credential;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.signature.SignableXMLObject;
import org.opensaml.xml.signature.SignatureConstants;
import org.opensaml.xml.signature.SignatureValidator;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

/**
 * The Class SAMLEngineUtils.
 * 
 * @author fjquevedo
 * @author iinigo
 */
public final class SAMLEngineUtils {

    /** The Constant UTF_8. */
    public static final String UTF_8 = "UTF-8";

    /** The Constant SHA_512. */
    public static final String SHA_512 = "SHA-512";

    /** The generator. */
    private static SecureRandomIdentifierGenerator generator;

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

    /**
     * Method that generates a random value according to NCName grammar.
     *
     * NCName ::= NCNameStartChar NCNameChar* NCNameChar ::= NameChar - ':'
     * NCNameStartChar ::= Letter | '_' NameStartChar ::= ":" | [A-Z] | "_" |
     * [a-z] | [#xC0-#xD6] | [#xD8-#xF6] | [#xF8-#x2FF] | [#x370-#x37D] |
     * [#x37F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] |
     * [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
     * NameChar ::= NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] |
     * [#x203F-#x2040] Name ::= NameStartChar (NameChar)* Letter ::= BaseChar |
     * Ideographic BaseChar ::= [#x0041-#x005A] | [#x0061-#x007A] |
     * [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x00FF] | [#x0100-#x0131] |
     * [#x0134-#x013E] | [#x0141-#x0148] | [#x014A-#x017E] | [#x0180-#x01C3] |
     * [#x01CD-#x01F0] | [#x01F4-#x01F5] | [#x01FA-#x0217] | [#x0250-#x02A8] |
     * [#x02BB-#x02C1] | #x0386 | [#x0388-#x038A] | #x038C | [#x038E-#x03A1] |
     * [#x03A3-#x03CE] | [#x03D0-#x03D6] | #x03DA | #x03DC | #x03DE | #x03E0 |
     * [#x03E2-#x03F3] | [#x0401-#x040C] | [#x040E-#x044F] | [#x0451-#x045C] |
     * [#x045E-#x0481] | [#x0490-#x04C4] | [#x04C7-#x04C8] | [#x04CB-#x04CC] |
     * [#x04D0-#x04EB] | [#x04EE-#x04F5] | [#x04F8-#x04F9] | [#x0531-#x0556] |
     * #x0559 | [#x0561-#x0586] | [#x05D0-#x05EA] | [#x05F0-#x05F2] |
     * [#x0621-#x063A] | [#x0641-#x064A] | [#x0671-#x06B7] | [#x06BA-#x06BE] |
     * [#x06C0-#x06CE] | [#x06D0-#x06D3] | #x06D5 | [#x06E5-#x06E6] |
     * [#x0905-#x0939] | #x093D | [#x0958-#x0961] | [#x0985-#x098C] |
     * [#x098F-#x0990] | [#x0993-#x09A8] | [#x09AA-#x09B0] | #x09B2 |
     * [#x09B6-#x09B9] | [#x09DC-#x09DD] | [#x09DF-#x09E1] | [#x09F0-#x09F1] |
     * [#x0A05-#x0A0A] | [#x0A0F-#x0A10] | [#x0A13-#x0A28] | [#x0A2A-#x0A30] |
     * [#x0A32-#x0A33] | [#x0A35-#x0A36] | [#x0A38-#x0A39] | [#x0A59-#x0A5C] |
     * #x0A5E | [#x0A72-#x0A74] | [#x0A85-#x0A8B] | #x0A8D | [#x0A8F-#x0A91] |
     * [#x0A93-#x0AA8] | [#x0AAA-#x0AB0] | [#x0AB2-#x0AB3] | [#x0AB5-#x0AB9] |
     * #x0ABD | #x0AE0 | [#x0B05-#x0B0C] | [#x0B0F-#x0B10] | [#x0B13-#x0B28] |
     * [#x0B2A-#x0B30] | [#x0B32-#x0B33] | [#x0B36-#x0B39] | #x0B3D |
     * [#x0B5C-#x0B5D] | [#x0B5F-#x0B61] | [#x0B85-#x0B8A] | [#x0B8E-#x0B90] |
     * [#x0B92-#x0B95] | [#x0B99-#x0B9A] | #x0B9C | [#x0B9E-#x0B9F] |
     * [#x0BA3-#x0BA4] | [#x0BA8-#x0BAA] | [#x0BAE-#x0BB5] | [#x0BB7-#x0BB9] |
     * [#x0C05-#x0C0C] | [#x0C0E-#x0C10] | [#x0C12-#x0C28] | [#x0C2A-#x0C33] |
     * [#x0C35-#x0C39] | [#x0C60-#x0C61] | [#x0C85-#x0C8C] | [#x0C8E-#x0C90] |
     * [#x0C92-#x0CA8] | [#x0CAA-#x0CB3] | [#x0CB5-#x0CB9] | #x0CDE |
     * [#x0CE0-#x0CE1] | [#x0D05-#x0D0C] | [#x0D0E-#x0D10] | [#x0D12-#x0D28] |
     * [#x0D2A-#x0D39] | [#x0D60-#x0D61] | [#x0E01-#x0E2E] | #x0E30 |
     * [#x0E32-#x0E33] | [#x0E40-#x0E45] | [#x0E81-#x0E82] | #x0E84 |
     * [#x0E87-#x0E88] | #x0E8A | #x0E8D | [#x0E94-#x0E97] | [#x0E99-#x0E9F] |
     * [#x0EA1-#x0EA3] | #x0EA5 | #x0EA7 | [#x0EAA-#x0EAB] | [#x0EAD-#x0EAE] |
     * #x0EB0 | [#x0EB2-#x0EB3] | #x0EBD | [#x0EC0-#x0EC4] | [#x0F40-#x0F47] |
     * [#x0F49-#x0F69] | [#x10A0-#x10C5] | [#x10D0-#x10F6] | #x1100 |
     * [#x1102-#x1103] | [#x1105-#x1107] | #x1109 | [#x110B-#x110C] |
     * [#x110E-#x1112] | #x113C | #x113E | #x1140 | #x114C | #x114E | #x1150 |
     * [#x1154-#x1155] | #x1159 | [#x115F-#x1161] | #x1163 | #x1165 | #x1167 |
     * #x1169 | [#x116D-#x116E] | [#x1172-#x1173] | #x1175 | #x119E | #x11A8 |
     * #x11AB | [#x11AE-#x11AF] | [#x11B7-#x11B8] | #x11BA | [#x11BC-#x11C2] |
     * #x11EB | #x11F0 | #x11F9 | [#x1E00-#x1E9B] | [#x1EA0-#x1EF9] |
     * [#x1F00-#x1F15] | [#x1F18-#x1F1D] | [#x1F20-#x1F45] | [#x1F48-#x1F4D] |
     * [#x1F50-#x1F57] | #x1F59 | #x1F5B | #x1F5D | [#x1F5F-#x1F7D] |
     * [#x1F80-#x1FB4] | [#x1FB6-#x1FBC] | #x1FBE | [#x1FC2-#x1FC4] |
     * [#x1FC6-#x1FCC] | [#x1FD0-#x1FD3] | [#x1FD6-#x1FDB] | [#x1FE0-#x1FEC] |
     * [#x1FF2-#x1FF4] | [#x1FF6-#x1FFC] | #x2126 | [#x212A-#x212B] | #x212E |
     * [#x2180-#x2182] | [#x3041-#x3094] | [#x30A1-#x30FA] | [#x3105-#x312C] |
     * [#xAC00-#xD7A3] Ideographic ::= [#x4E00-#x9FA5] | #x3007 |
     * [#x3021-#x3029]
     *
     * @return Random ID value
     */

    //Initialization of a generator of identifiers for all token SAML.

    static {
        loadRandomIdentifierGenerator();
    }

    /**
     * Load random identifier generator.
     *
     *@throws EIDASSAMLEngineRuntimeException the EIDASSAML engine runtime exception
     */
    private static void loadRandomIdentifierGenerator() {

        try {
            generator = new SecureRandomIdentifierGenerator();
        } catch (NoSuchAlgorithmException ex) {
            LOG.error("Error init SecureRandomIdentifierGenerator", ex);
            throw new EIDASSAMLEngineRuntimeException(ex);
        }

    }

    /**
     * Creates the SAML object.
     *
     * @param qname the QName
     *
     * @return the XML object
     */
    public static XMLObject createSamlObject(final QName qname) {
        XMLObjectBuilder builder = Configuration.getBuilderFactory().getBuilder(qname);
        return builder == null ? null : builder.buildObject(qname);
    }

    /**
     * Creates the SAML object.
     *
     * @param qname the quality name
     * @param qname1 the qname1
     *
     * @return the xML object
     */
    public static XMLObject createSamlObject(final QName qname, final QName qname1) {
        return Configuration.getBuilderFactory().getBuilder(qname1).buildObject(qname, qname1);
    }

    /**
     * Encode value with an specific algorithm.
     *
     * @param value the value
     * @param alg the algorithm
     *
     * @return the string
     *
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static String encode(final String value, final String alg) throws EIDASSAMLEngineException {
        LOG.debug("Encode value with  " + alg + " algorithm.");
        byte[] buffer;

        final StringBuilder hash = new StringBuilder("");
        try {
            buffer = value.getBytes(UTF_8);
            MessageDigest msgDig;
            msgDig = MessageDigest.getInstance(alg);

            msgDig.update(buffer);
            final byte[] digest = msgDig.digest();

            final int signedByte = 0xff;
            for (byte aux : digest) {
                final int byt = aux & signedByte;
                if (Integer.toHexString(byt).length() == 1) {
                    hash.append('0');
                }
                hash.append(Integer.toHexString(byt));
            }

        } catch (UnsupportedEncodingException e1) {
            LOG.info("ERROR : UnsupportedEncodingException: " + UTF_8);
            throw new EIDASSAMLEngineException(EIDASErrors.INTERNAL_ERROR.errorCode(),
                    EIDASErrors.INTERNAL_ERROR.errorMessage(), e1);
        } catch (NoSuchAlgorithmException e) {
            LOG.info("ERROR : NoSuchAlgorithmException: " + alg);
            throw new EIDASSAMLEngineException(EIDASErrors.INTERNAL_ERROR.errorCode(),
                    EIDASErrors.INTERNAL_ERROR.errorMessage(), e);
        }

        return hash.toString();
    }

    /**
     * Generate assertion.
     *
     * @param version the version
     * @param identifier the identifier
     * @param issueInstant the issue instant
     * @param issuer the issuer
     *
     * @return the assertion
     */
    public static Assertion generateAssertion(final SAMLVersion version, final String identifier,
            final DateTime issueInstant, final Issuer issuer) {
        final AssertionBuilder assertionBuilder = new AssertionBuilder();
        final Assertion assertion = assertionBuilder.buildObject();
        assertion.setVersion(version);
        assertion.setID(identifier);
        assertion.setIssueInstant(issueInstant);

        // <saml:Issuer>
        assertion.setIssuer(issuer);
        return assertion;
    }

    /**
     * Generate authentication statement.
     *
     * @param authnInstant the authentication instant
     * @param authnContext the authentication context
     *
     * @return the authentication statement
     */
    public static AuthnStatement generateAthnStatement(final DateTime authnInstant,
            final AuthnContext authnContext) {
        // <saml:AuthnStatement>
        final AuthnStatement authnStatement = (AuthnStatement) SAMLEngineUtils
                .createSamlObject(AuthnStatement.DEFAULT_ELEMENT_NAME);

        authnStatement.setAuthnInstant(authnInstant);
        authnStatement.setAuthnContext(authnContext);

        return authnStatement;
    }

    /**
     * Generate attribute from a list of values.
     *
     * @param name the name of the attribute.
     * @param status the status of the parameter: "Available", "NotAvailable" or
     * "Withheld".
     * @param values the value of the attribute.
     * @param isHashing the is hashing with "SHA-512" algorithm.
     * @return the attribute
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static Attribute generateAttrComplex(final EIDASSAMLEngine engine, final String name,
            final String status, final Map<String, String> values, final boolean isHashing)
            throws EIDASSAMLEngineException {
        LOG.debug("Generate attribute complex: " + name);
        final Attribute attribute = (Attribute) SAMLEngineUtils.createSamlObject(Attribute.DEFAULT_ELEMENT_NAME);

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

        attribute.getUnknownAttributes().put(new QName(engine.getExtensionProcessor().getFormat().getAssertionNS(),
                "AttributeStatus", engine.getExtensionProcessor().getFormat().getAssertionPrefix()), status);

        if (!values.isEmpty()) {
            LOG.debug("Add attribute values.");

            // Create an attribute that contains all XSAny elements.
            final XSAny attrValue = (XSAny) SAMLEngineUtils.createSamlObject(AttributeValue.DEFAULT_ELEMENT_NAME,
                    XSAny.TYPE_NAME);

            final Iterator<Entry<String, String>> iterator = values.entrySet().iterator();
            while (iterator.hasNext()) {
                final Map.Entry<String, String> pairs = iterator.next();

                final String value = pairs.getValue();

                if (StringUtils.isNotBlank(value)) {
                    // Create the attribute statement
                    final XSAny attrValueSimple = (XSAny) SAMLEngineUtils
                            .createSamlObject(new QName(SAMLCore.STORK10_NS.getValue(), pairs.getKey().toString(),
                                    SAMLCore.STORK10_PREFIX.getValue()), XSAny.TYPE_NAME);

                    // if it's necessary encode the information.
                    if (isHashing) {
                        attrValueSimple.setTextContent(encode(value, SHA_512));
                    } else {
                        attrValueSimple.setTextContent(value);
                    }

                    attrValue.getUnknownXMLObjects().add(attrValueSimple);
                    attribute.getAttributeValues().add(attrValue);
                }
            }

        }
        return attribute;
    }

    /**
     * Generate extension.
     *
     * @return the extensions
     */
    public static Extensions generateExtension() {
        final ExtensionsBuilder extensionsBuilder = new ExtensionsBuilder();
        return extensionsBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol", "Extensions", "saml2p");
    }

    /**
     * Generate issuer.
     *
     * @return the issuer
     */
    public static Issuer generateIssuer() {
        return (Issuer) SAMLEngineUtils.createSamlObject(Issuer.DEFAULT_ELEMENT_NAME);
    }

    /**
     * Generate key info.
     *
     * @return the key info
     */
    public static KeyInfo generateKeyInfo() {
        return (KeyInfo) SAMLEngineUtils.createSamlObject(KeyInfo.DEFAULT_ELEMENT_NAME);
    }

    /**
     * Generate name id.
     *
     * @return the name id
     */
    public static NameID generateNameID() {
        return (NameID) SAMLEngineUtils.createSamlObject(NameID.DEFAULT_ELEMENT_NAME);
    }

    /**
     * Generate name id.
     *
     * @param nameQualifier the name qualifier
     * @param format the format
     * @param spNameQualifier the sP name qualifier
     *
     * @return the name id
     */
    public static NameID generateNameID(final String nameQualifier, final String format,
            final String spNameQualifier) {
        // <saml:NameID>
        final NameID nameId = (NameID) Configuration.getBuilderFactory().getBuilder(NameID.DEFAULT_ELEMENT_NAME)
                .buildObject(NameID.DEFAULT_ELEMENT_NAME);

        // optional
        nameId.setNameQualifier(nameQualifier);

        // optional
        nameId.setFormat(format);

        // optional
        nameId.setSPNameQualifier(spNameQualifier);

        return nameId;
    }

    /**
     * Generate NCName.
     *
     * @return the string
     */
    public static String generateNCName() {
        return generator.generateIdentifier();
    }

    /**
     * Generate requested attribute.
     *
     * @param name the name
     * @param friendlyName the friendly name
     * @param isRequired the is required
     * @param value the value
     *
     * @return the requested attribute
     */
    public static RequestedAttribute generateReqAuthnAttributeSimple(final EIDASSAMLEngine engine,
            final String name, final String friendlyName, final String isRequired, final List<String> value) {
        LOG.debug("Generate the requested attribute.");

        final RequestedAttribute requested = (RequestedAttribute) SAMLEngineUtils
                .createSamlObject(RequestedAttribute.DEF_ELEMENT_NAME);
        requested.setName(name);
        requested.setNameFormat(RequestedAttribute.URI_REFERENCE);

        requested.setFriendlyName(friendlyName);

        requested.setIsRequired(isRequired);
        generateDocument(engine, name, value, requested.getAttributeValues());
        // The value is optional in an authentication request.

        return requested;
    }

    public static void generateDocument(final EIDASSAMLEngine engine, final String name, List<String> value,
            List<XMLObject> attributeValues) {
        if (!value.isEmpty()) {
            final String nameSpace = engine.getExtensionProcessor().getFormat().getAssertionNS();
            final String prefix = engine.getExtensionProcessor().getFormat().getAssertionPrefix();
            for (int nextValue = 0; nextValue < value.size(); nextValue++) {
                final String valor = value.get(nextValue);
                if (StringUtils.isNotBlank(valor)) {

                    if (!(engine.getExtensionProcessor().getFormat().getBaseURI() + "signedDoc").equals(name)) {

                        // Create the attribute statement
                        final XSAny attrValue = (XSAny) SAMLEngineUtils
                                .createSamlObject(new QName(nameSpace, "AttributeValue", prefix), XSAny.TYPE_NAME);

                        attrValue.setTextContent(valor.trim());
                        attributeValues.add(attrValue);

                    } else {
                        parseSignedDoc(attributeValues, nameSpace, prefix, valor);

                    }

                }
            }
        }

    }

    private static void parseSignedDoc(List<XMLObject> attributeValues, String nameSpace, String prefix,
            String value) {
        DocumentBuilderFactory domFactory = EIDASSAMLEngine.newDocumentBuilderFactory();
        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.info("ERROR : SAX Error while parsing signModule attribute", e1.getMessage());
            LOG.debug("ERROR : SAX Error while parsing signModule attribute", e1);
            throw new EIDASSAMLEngineRuntimeException(e1);
        } catch (ParserConfigurationException e2) {
            LOG.info("ERROR : Parser Configuration Error while parsing signModule attribute", e2.getMessage());
            LOG.debug("ERROR : Parser Configuration Error while parsing signModule attribute", e2);
            throw new EIDASSAMLEngineRuntimeException(e2);
        } catch (UnsupportedEncodingException e3) {
            LOG.info("ERROR : Unsupported encoding Error while parsing signModule attribute", e3.getMessage());
            LOG.debug("ERROR : Unsupported encoding Error while parsing signModule attribute", e3);
            throw new EIDASSAMLEngineRuntimeException(e3);
        } catch (IOException e4) {
            LOG.info("ERROR : IO Error while parsing signModule attribute", e4.getMessage());
            LOG.debug("ERROR : IO Error while parsing signModule attribute", e4);
            throw new EIDASSAMLEngineRuntimeException(e4);
        }

        // Create the XML statement(this will be overwritten with the previous DOM structure)
        final XSAny xmlValue = (XSAny) SAMLEngineUtils.createSamlObject(new QName(nameSpace, "XMLValue", prefix),
                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(new QName(nameSpace, "AttributeValue", prefix), XSAny.TYPE_NAME);

        //Add previous signedDocXML to the AttributeValue Element
        attrValue.getUnknownXMLObjects().add(xmlValue);

        attributeValues.add(attrValue);
    }

    /**
     * Generate response.
     *
     * @param identifier the identifier
     * @param issueInstant the issue instant
     * @param status the status
     *
     * @return the response
     */
    public static Response generateResponse(final String identifier, final DateTime issueInstant,
            final Status status) {
        final Response response = (Response) SAMLEngineUtils.createSamlObject(Response.DEFAULT_ELEMENT_NAME);
        response.setID(identifier);
        response.setIssueInstant(issueInstant);
        response.setStatus(status);
        return response;
    }

    /**
     * Method that generates a SAML Authentication Request basing on the
     * provided information.
     *
     * @param identifier the identifier
     * @param version the version
     * @param issueInstant the issue instant
     *
     * @return the authentication request
     */
    public static AuthnRequest generateSAMLAuthnRequest(final String identifier, final SAMLVersion version,
            final DateTime issueInstant) {
        LOG.debug("Generate basic authentication request.");
        final AuthnRequest authnRequest = (AuthnRequest) SAMLEngineUtils
                .createSamlObject(AuthnRequest.DEFAULT_ELEMENT_NAME);

        authnRequest.setID(identifier);
        authnRequest.setVersion(version);
        authnRequest.setIssueInstant(issueInstant);
        return authnRequest;
    }

    /**
     * Generate service provider application.
     *
     * @param spApplication the service provider application
     *
     * @return the sP application
     *
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static SPApplication generateSPApplication(final String spApplication) throws EIDASSAMLEngineException {
        LOG.debug("Generate SPApplication.");

        final SPApplication applicationAttr = (SPApplication) SAMLEngineUtils
                .createSamlObject(SPApplication.DEF_ELEMENT_NAME);
        applicationAttr.setSPApplication(spApplication);
        return applicationAttr;
    }

    /**
     * Generate service provider country.
     *
     * @param spCountry the service provider country
     *
     * @return the service provider country
     *
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static SPCountry generateSPCountry(final String spCountry) throws EIDASSAMLEngineException {
        LOG.debug("Generate SPApplication.");

        final SPCountry countryAttribute = (SPCountry) SAMLEngineUtils.createSamlObject(SPCountry.DEF_ELEMENT_NAME);
        countryAttribute.setSPCountry(spCountry);
        return countryAttribute;
    }

    /**
     * Generate service provider institution.
     *
     * @param spInstitution the service provider institution
     *
     * @return the service provider institution
     *
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static SPInstitution generateSPInstitution(final String spInstitution) throws EIDASSAMLEngineException {
        LOG.debug("Generate SPInstitution.");

        final SPInstitution institutionAttr = (SPInstitution) SAMLEngineUtils
                .createSamlObject(SPInstitution.DEF_ELEMENT_NAME);
        institutionAttr.setSPInstitution(spInstitution);
        return institutionAttr;
    }

    /**
     * Generate service provider sector.
     *
     * @param spSector the service provider sector
     *
     * @return the service provider sector
     *
     * @throws EIDASSAMLEngineException the EIDASSAML engine exception
     */
    public static SPSector generateSPSector(final String spSector) throws EIDASSAMLEngineException {
        LOG.debug("Generate SPSector.");

        final SPSector sectorAttribute = (SPSector) SAMLEngineUtils.createSamlObject(SPSector.DEF_ELEMENT_NAME);
        sectorAttribute.setSPSector(spSector);
        return sectorAttribute;
    }

    /**
     * Generate status.
     *
     * @param statusCode the status code
     *
     * @return the status
     */
    public static Status generateStatus(final StatusCode statusCode) {
        final Status status = (Status) SAMLEngineUtils.createSamlObject(Status.DEFAULT_ELEMENT_NAME);
        status.setStatusCode(statusCode);
        return status;
    }

    /**
     * Generate status code.
     *
     * @param value the value
     *
     * @return the status code
     */
    public static StatusCode generateStatusCode(final String value) {
        final StatusCode statusCode = (StatusCode) SAMLEngineUtils
                .createSamlObject(StatusCode.DEFAULT_ELEMENT_NAME);
        statusCode.setValue(value);
        return statusCode;
    }

    /**
     * Generate status message.
     *
     * @param message the message
     *
     * @return the status message
     */
    public static StatusMessage generateStatusMessage(final String message) {
        final StatusMessage statusMessage = (StatusMessage) SAMLEngineUtils
                .createSamlObject(StatusMessage.DEFAULT_ELEMENT_NAME);
        statusMessage.setMessage(message);
        return statusMessage;
    }

    /**
     * Generate subject.
     *
     * @return the subject
     */
    public static Subject generateSubject() {
        return (Subject) SAMLEngineUtils.createSamlObject(Subject.DEFAULT_ELEMENT_NAME);
    }

    /**
     * Generate subject confirmation.
     *
     * @param method the method
     * @param data the data
     *
     * @return the subject confirmation
     */
    public static SubjectConfirmation generateSubjectConfirmation(final String method,
            final SubjectConfirmationData data) {
        final SubjectConfirmation subjectConf = (SubjectConfirmation) Configuration.getBuilderFactory()
                .getBuilder(SubjectConfirmation.DEFAULT_ELEMENT_NAME)
                .buildObject(SubjectConfirmation.DEFAULT_ELEMENT_NAME);

        subjectConf.setMethod(method);

        subjectConf.setSubjectConfirmationData(data);

        return subjectConf;
    }

    /**
     * Generate subject confirmation data.
     *
     * @param notOnOrAfter the not on or after
     * @param recipient the recipient
     * @param inResponseTo the in response to
     *
     * @return the subject confirmation data
     */
    public static SubjectConfirmationData generateSubjectConfirmationData(final DateTime notOnOrAfter,
            final String recipient, final String inResponseTo) {
        final SubjectConfirmationData subjectConfData = (SubjectConfirmationData) SAMLEngineUtils
                .createSamlObject(SubjectConfirmationData.DEFAULT_ELEMENT_NAME);
        subjectConfData.setNotOnOrAfter(notOnOrAfter);
        subjectConfData.setRecipient(recipient);
        subjectConfData.setInResponseTo(inResponseTo);
        return subjectConfData;
    }

    /**
     * Generate subject locality.
     *
     * @param address the address
     *
     * @return the subject locality
     */
    public static SubjectLocality generateSubjectLocality(final String address) {
        final SubjectLocality subjectLocality = (SubjectLocality) SAMLEngineUtils
                .createSamlObject(SubjectLocality.DEFAULT_ELEMENT_NAME);
        subjectLocality.setAddress(address);
        return subjectLocality;
    }

    /**
     * Method that returns the current time.
     *
     * @return the current time
     */
    public static DateTime getCurrentTime() {
        return new DateTime();
    }

    /**
     * Instantiates a new SAML engine utilities.
     */
    private SAMLEngineUtils() {
    }

    public static BasicSecurityConfiguration getEidasGlobalSecurityConfiguration() {
        return (BasicSecurityConfiguration) Configuration.getGlobalSecurityConfiguration();
    }

    public static String validateSigningAlgorithm(String signatureAlgorithmName) {
        if (SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256.equalsIgnoreCase(signatureAlgorithmName)) {
            return SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256;
        } else if (SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384.equalsIgnoreCase(signatureAlgorithmName)) {
            return SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384;
        } else if (!SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512.equalsIgnoreCase(signatureAlgorithmName)) {
            LOG.info("BUSINESS EXCEPTION : Invalid signing algorithm, defaulting to ALGO_ID_SIGNATURE_RSA_SHA512");
        }
        return SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512;
    }

    public static String validateDigestAlgorithm(String signatureAlgorithmName) {
        if (SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256.equalsIgnoreCase(signatureAlgorithmName)) {
            return SignatureConstants.ALGO_ID_DIGEST_SHA256;
        } else if (SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA384.equalsIgnoreCase(signatureAlgorithmName)) {
            return SignatureConstants.ALGO_ID_DIGEST_SHA384;
        }
        return SignatureConstants.ALGO_ID_DIGEST_SHA512;
    }

    /**
     *
     * @param certificate
     * @param privateKey
     * @return a credential based on the provided elements
     */
    public static Credential createCredential(X509Certificate certificate, PrivateKey privateKey) {
        final BasicX509Credential credential = new BasicX509Credential();
        credential.setEntityCertificate(certificate);
        credential.setPrivateKey(privateKey);
        return credential;
    }

    public static <T> T createSAMLObject(final Class<T> clazz) throws NoSuchFieldException, IllegalAccessException {
        XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();

        QName defaultElementName = (QName) clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
        XMLObjectBuilder builder = builderFactory.getBuilder(defaultElementName);
        T object = (T) builder.buildObject(defaultElementName);

        return object;
    }

    /**
     *
     * @param encryptionKeyStore
     * @param serialNumber
     * @param issuer
     * @param keyPassword
     * @return the credential of the private key of the certificate having the given serialnumber and issuer
     * @throws NoSuchAlgorithmException
     * @throws KeyStoreException
     * @throws UnrecoverableKeyException
     * @throws SAMLEngineException
     */

    public static Credential getCredential(KeyStore encryptionKeyStore, String serialNumber, String issuer,
            char[] keyPassword)
            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, SAMLEngineException {
        CertificateAliasPair pair = getCertificatePair(encryptionKeyStore, serialNumber, issuer);
        final PrivateKey privateKey = (PrivateKey) encryptionKeyStore.getKey(pair.getAlias(), keyPassword);
        return SAMLEngineUtils.createCredential(pair.getCertificate(), privateKey);
    }

    public static Credential getEncryptionCredential(KeyStore encryptionKeyStore, String serialNumber,
            String issuer)
            throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, SAMLEngineException {
        CertificateAliasPair pair = getCertificatePair(encryptionKeyStore, serialNumber, issuer);
        // Create basic credential and set the EntityCertificate
        BasicX509Credential credential = new BasicX509Credential();
        credential.setEntityCertificate(pair.getCertificate());
        return credential;
    }

    /**
     *
     * @param keystore
     * @param serialNumber
     * @param issuer
     * @return a certificate/alias pair from the keystore, having the given issuer and serialNumber
     * @throws KeyStoreException
     * @throws SAMLEngineException
     */
    public static CertificateAliasPair getCertificatePair(KeyStore keystore, String serialNumber, String issuer)
            throws KeyStoreException, SAMLEngineException {
        String alias = null;
        String aliasCert;
        X509Certificate certificate;
        boolean find = false;
        LOG.debug("cherche dans " + keystore.toString() + " numSerie=" + serialNumber + " issuer=" + issuer);
        for (final Enumeration<String> e = keystore.aliases(); e.hasMoreElements() && !find;) {
            aliasCert = e.nextElement();
            certificate = (X509Certificate) keystore.getCertificate(aliasCert);

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

            Principal p = certificate.getIssuerDN();
            String name = p.getName();

            X500Name issuerDN = new X500Name(name);
            X500Name issuerDNConf = new X500Name(issuer);

            if (serialNum.equalsIgnoreCase(serialNumber)
                    && X500PrincipalUtil.principalEquals(issuerDN, issuerDNConf)) {
                alias = aliasCert;
                find = true;
            } else {
                LOG.debug("pas pareil numSerie=" + serialNum + " ou issuer=" + name);
            }
        }
        if (!find) {
            throw new SAMLEngineException(
                    "Certificate " + issuer + "/" + serialNumber + " cannot be found in keystore ");
        }
        certificate = (X509Certificate) keystore.getCertificate(alias);
        return new CertificateAliasPair(certificate, alias);

    }

    public static void checkTrust(Credential entityX509Cred, List<Credential> trustCred)
            throws SAMLEngineException {
        final ExplicitKeyTrustEvaluator keyTrustEvaluator = new ExplicitKeyTrustEvaluator();
        LOG.debug("entityX509Cred.getEntityId=" + entityX509Cred.getEntityId());
        if (entityX509Cred instanceof BasicX509Credential) {
            LOG.debug("IssuerDNname="
                    + ((BasicX509Credential) entityX509Cred).getEntityCertificate().getIssuerDN().getName());
            LOG.debug("NotAfter=" + ((BasicX509Credential) entityX509Cred).getEntityCertificate().getNotAfter());
            LOG.debug("SerialNumber="
                    + ((BasicX509Credential) entityX509Cred).getEntityCertificate().getSerialNumber());
        }
        if (trustCred.isEmpty()) {
            throw new SAMLEngineException(EIDASErrors.SAML_ENGINE_UNTRUSTED_CERTIFICATE.errorCode(),
                    "pas de certif de reference");
        }
        for (Credential refCred : trustCred) {
            LOG.debug("refCred.getEntityId=" + refCred.getEntityId());
            if (entityX509Cred instanceof BasicX509Credential) {
                LOG.debug("refCred.IssuerDNname="
                        + ((BasicX509Credential) refCred).getEntityCertificate().getIssuerDN().getName());
                LOG.debug(
                        "refCred.NotAfter=" + ((BasicX509Credential) refCred).getEntityCertificate().getNotAfter());
                LOG.debug("refCred.SerialNumber="
                        + ((BasicX509Credential) refCred).getEntityCertificate().getSerialNumber());
            }
        }
        if (!keyTrustEvaluator.validate(entityX509Cred, trustCred)) {
            throw new SAMLEngineException(EIDASErrors.SAML_ENGINE_UNTRUSTED_CERTIFICATE.errorCode(),
                    EIDASErrors.SAML_ENGINE_UNTRUSTED_CERTIFICATE.errorMessage());
        }
    }

    public static Credential getKeyCredential(SAMLEngineModuleI module, KeyInfo keyInfo)
            throws SAMLEngineException {
        Credential credential;
        try {
            final org.opensaml.xml.signature.X509Certificate xmlCert = keyInfo.getX509Datas().get(0)
                    .getX509Certificates().get(0);

            final CertificateFactory certFact = CertificateFactory.getInstance("X.509");
            final ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(xmlCert.getValue()));
            final X509Certificate cert = (X509Certificate) certFact.generateCertificate(bis);

            credential = new BasicX509Credential();
            ((BasicX509Credential) credential).setEntityCertificate(cert);
            if (module != null) {
                module.checkCertificateIssuer(cert);
                module.checkCertificateValidityPeriod(cert);
            }
        } catch (CertificateException ce) {
            throw new SAMLEngineException(EIDASErrors.SAML_ENGINE_INVALID_CERTIFICATE.errorCode(),
                    EIDASErrors.SAML_ENGINE_INVALID_CERTIFICATE.errorMessage(), ce);
        }
        return credential;
    }

    /**
     * @param cert
     * @return true when the certificate is self signed
     */
    public static boolean isCertificateSelfSigned(X509Certificate cert) {
        try {
            PublicKey publicKey = cert.getPublicKey();
            cert.verify(publicKey);
            return true;
        } catch (java.security.SignatureException sigEx) {
            LOG.info("ERROR : SignatureException {}", sigEx.getMessage());
            LOG.debug("ERROR : SignatureException {}", sigEx);
            return false;
        } catch (InvalidKeyException keyEx) {
            // Invalid key --> not self-signed
            LOG.info("ERROR : InvalidKeyException {}", keyEx.getMessage());
            LOG.debug("ERROR : InvalidKeyException {}", keyEx);
            return false;
        } catch (CertificateException certExc) {
            LOG.info("ERROR : CertificateException {}", certExc.getMessage());
            LOG.debug("ERROR : CertificateException {}", certExc);
            return false;
        } catch (NoSuchAlgorithmException nsaExc) {
            LOG.info("ERROR : Bad algorithm: " + nsaExc.getMessage());
            LOG.debug("ERROR : Bad algorithm: " + nsaExc);
            return false;
        } catch (NoSuchProviderException nspExc) {
            LOG.info("ERROR : Bad provider: " + nspExc.getMessage());
            LOG.debug("ERROR : Bad provider: " + nspExc);
            return false;
        }
    }

    public static List<Credential> getListOfCredential(KeyStore keyStore) throws SAMLEngineException {
        final List<Credential> trustCred = new ArrayList<Credential>();
        try {
            String aliasCert = null;
            X509Certificate certificate;
            for (final Enumeration<String> e = keyStore.aliases(); e.hasMoreElements();) {
                aliasCert = e.nextElement();
                final BasicX509Credential credential = new BasicX509Credential();
                certificate = (X509Certificate) keyStore.getCertificate(aliasCert);
                credential.setEntityCertificate(certificate);
                trustCred.add(credential);
            }
        } catch (KeyStoreException e) {
            LOG.warn("ERROR : KeyStoreException.", e.getMessage());
            LOG.debug("ERROR : KeyStoreException.", e);
            throw new SAMLEngineException(e);
        }
        return trustCred;

    }

    public static void checkTrust(Credential entityX509Cred, KeyStore trustStore) throws SAMLEngineException {
        checkTrust(entityX509Cred, getListOfCredential(trustStore));
    }

    /**
     * validates a metadata entitydescriptor's signature against a trustkeystore
     * @param ed
     * @param trustKeyStore
     * @throws SAMLEngineException
     */
    public static void validateEntityDescriptorSignature(SignableXMLObject ed, KeyStore trustKeyStore)
            throws SAMLEngineException {
        if (ed == null) {
            throw new SAMLEngineException("invalid entity descriptor");
        }
        try {
            SAMLSignatureProfileValidator sigProfValidator = new SAMLSignatureProfileValidator();
            org.opensaml.xml.signature.Signature signature = ed.getSignature();
            sigProfValidator.validate(signature);
            //check that EntityDescriptor matches the signature
            final KeyInfo keyInfo = ed.getSignature().getKeyInfo();

            final org.opensaml.xml.signature.X509Certificate xmlCert = keyInfo.getX509Datas().get(0)
                    .getX509Certificates().get(0);

            final CertificateFactory certFact = CertificateFactory.getInstance("X.509");
            final ByteArrayInputStream bis = new ByteArrayInputStream(Base64.decode(xmlCert.getValue()));
            final X509Certificate cert = (X509Certificate) certFact.generateCertificate(bis);

            final BasicX509Credential entityX509Cred = new BasicX509Credential();
            entityX509Cred.setEntityCertificate(cert);
            final SignatureValidator sigValidator = new SignatureValidator(entityX509Cred);
            sigValidator.validate(signature);
            if (trustKeyStore != null) {
                SAMLEngineUtils.checkTrust(entityX509Cred, trustKeyStore);
            }
        } catch (ValidationException exc) {
            throw new SAMLEngineException(EIDASErrors.INVALID_SIGNATURE_ALGORITHM.errorCode(), exc);
        } catch (CertificateException exc) {
            throw new SAMLEngineException(EIDASErrors.INVALID_SIGNATURE_ALGORITHM.errorCode(), exc);
        }

    }

    /**
     * validates a metadata entitydescriptor's signature against a trustkeystore
     * @param ed
     * @param samlEngine
     * @throws SAMLEngineException
     */
    public static void validateEntityDescriptorSignature(SignableXMLObject ed, EIDASSAMLEngine samlEngine)
            throws SAMLEngineException {
        if (ed == null || samlEngine == null) {
            throw new SAMLEngineException("invalid metadata context");
        }
        validateEntityDescriptorSignature(ed, samlEngine.getSigner().getTrustStore());
    }

    /**
     * identified whether a request is eIDAS format or not(ie Stork format)
     * @param authRequest
     * @return
     */
    public static boolean isEidasFormat(EIDASAuthnRequest authRequest) {
        if (authRequest == null || authRequest.getPersonalAttributeList() == null) {
            return false;
        }
        for (PersonalAttribute pa : authRequest.getPersonalAttributeList()) {
            if (EIDASAttributes.getAttributeType(pa.getFullName()) != null) {
                return true;
            }
        }
        return false;
    }

    private static final String ALLOWED_METADATA_SCHEMES[] = { "https://", "http://" };

    /**
     * validates the issuer to be an url of a known scheme
     * @param value
     * @return the validated value
     * @throws SAMLEngineException
     */
    public static String getValidIssuerValue(String value) throws EIDASSAMLEngineException {
        if (value != null) {
            for (String scheme : ALLOWED_METADATA_SCHEMES) {
                if (value.toLowerCase().startsWith(scheme)) {
                    return value;
                }
            }
        }
        LOG.error("CONFIGURATION ERROR - Issuer error, configuration entry " + value
                + " is not valid (HTTP and HTTPS are the only metadata scheme are supported)");
        throw new EIDASSAMLEngineException(EIDASErrors.SAML_ENGINE_INVALID_METADATA.errorCode(),
                EIDASErrors.SAML_ENGINE_INVALID_METADATA.errorMessage());
    }

    /**
     * @param binding SAML binding
     * @return http method(either POST or GET)
     */
    public static String getBindingMethod(String binding) {
        if (SAMLConstants.SAML2_REDIRECT_BINDING_URI.equals(binding)) {
            return EIDASAuthnRequest.BINDING_REDIRECT;
        } else {
            return EIDASAuthnRequest.BINDING_POST;
        }
    }

    /**
     * maps saml supported values for comparison type to those in opensaml
     * @param loaCompareType
     * @return
     */
    public static AuthnContextComparisonTypeEnumeration getAuthnCtxtComparisonType(
            EidasLoaCompareType loaCompareType) {
        return AuthnContextComparisonTypeEnumeration.MINIMUM;
    }

    /**
     * return the service LoA of a node
     * @param idp
     * @return
     */
    public static String getServiceLoA(EntityDescriptor idp) {
        String retrievedLoA = "";
        if (idp == null) {
            return retrievedLoA;
        }
        for (XMLObject xmlObj : idp.getExtensions().getUnknownXMLObjects()) {
            if (xmlObj instanceof EntityAttributes) {
                EntityAttributes eas = (EntityAttributes) xmlObj;
                for (Attribute attr : eas.getAttributes()) {
                    if (EidasConstants.LEVEL_OF_ASSURANCE_NAME.equalsIgnoreCase(attr.getName())
                            && !attr.getAttributeValues().isEmpty()) {
                        XSString val = (XSString) attr.getAttributeValues().get(0);
                        retrievedLoA = val.getValue();
                        break;
                    }
                }
                if (!StringUtils.isEmpty(retrievedLoA)) {
                    break;
                }
            }
        }
        return retrievedLoA;
    }

    /**
    *
    * @param xmlObj
    * @return a string containing the xml representation of the entityDescriptor
    */
    public static String serializeObject(XMLObject xmlObj) {
        StringWriter stringWriter = new StringWriter();
        String stringRepresentation = "";
        try {
            DocumentBuilder builder;
            DocumentBuilderFactory factory = DocumentBuilderFactoryUtil.getSecureDocumentBuilderFactory();

            builder = factory.newDocumentBuilder();
            Document document = builder.newDocument();
            Marshaller out = Configuration.getMarshallerFactory().getMarshaller(xmlObj);
            out.marshall(xmlObj, document);

            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            StreamResult streamResult = new StreamResult(stringWriter);
            DOMSource source = new DOMSource(document);
            transformer.transform(source, streamResult);
        } catch (ParserConfigurationException pce) {
            LOG.info("ERROR : parser error", pce.getMessage());
            LOG.debug("ERROR : parser error", pce);
        } catch (TransformerConfigurationException tce) {
            LOG.info("ERROR : transformer configuration error", tce.getMessage());
            LOG.debug("ERROR : transformer configuration error", tce);
        } catch (TransformerException te) {
            LOG.info("ERROR : transformer error", te.getMessage());
            LOG.debug("ERROR : transformer error", te);
        } catch (MarshallingException me) {
            LOG.info("ERROR : marshalling error", me.getMessage());
            LOG.debug("ERROR : marshalling error", me);
        } finally {
            try {
                stringWriter.close();
                stringRepresentation = stringWriter.toString();
            } catch (IOException ioe) {
                LOG.warn("ERROR when closing the marshalling stream {}", ioe);
            }
        }
        return stringRepresentation;
    }

    public static boolean isErrorSamlResponse(Response response) {
        return response != null && !StatusCode.SUCCESS_URI.equals(response.getStatus().getStatusCode().getValue());
    }

    private static final String NO_ASSERTION = "no assertion found";
    private static final String ASSERTION_XPATH = "//*[local-name()='Assertion']";

    /**
     *
     * @param samlMsg the saml response as a string
     * @return a string representing the Assertion
     */
    public static String extractAssertionAsString(String samlMsg) {
        String assertion = NO_ASSERTION;
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            Document doc = dbf.newDocumentBuilder().parse(new InputSource(new StringReader(samlMsg)));

            XPath xPath = XPathFactory.newInstance().newXPath();
            Node node = (Node) xPath.evaluate(ASSERTION_XPATH, doc, XPathConstants.NODE);
            if (node != null) {
                assertion = domnodeToString(node);
            }
        } catch (ParserConfigurationException pce) {
            LOG.error("cannot parse response {}", pce);
        } catch (SAXException saxe) {
            LOG.error("cannot parse response {}", saxe);

        } catch (IOException ioe) {
            LOG.error("cannot parse response {}", ioe);

        } catch (XPathExpressionException xpathe) {
            LOG.error("cannot find the assertion {}", xpathe);

        } catch (TransformerException trfe) {
            LOG.error("cannot output the assertion {}", trfe);

        }

        return assertion;
    }

    private static String domnodeToString(Node node) throws TransformerException {
        StringWriter buf = new StringWriter();
        Transformer xForm = TransformerFactory.newInstance().newTransformer();
        xForm.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
        xForm.transform(new DOMSource(node), new StreamResult(buf));
        return (buf.toString());
    }

}