com.alfaariss.oa.util.saml2.profile.AbstractSAML2Profile.java Source code

Java tutorial

Introduction

Here is the source code for com.alfaariss.oa.util.saml2.profile.AbstractSAML2Profile.java

Source

/*
 * Asimba Server
 * 
 * Copyright (C) 2012 Asimba
 * Copyright (C) 2007-2010 Alfa & Ariss B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see www.gnu.org/licenses
 * 
 * Asimba - Serious Open Source SSO - More information on www.asimba.org
 * 
 */
package com.alfaariss.oa.util.saml2.profile;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opensaml.Configuration;
import org.opensaml.common.SAMLObject;
import org.opensaml.common.SignableSAMLObject;
import org.opensaml.common.binding.BasicSAMLMessageContext;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.common.impl.SAMLObjectContentReference;
import org.opensaml.saml2.binding.security.SAML2HTTPPostSimpleSignRule;
import org.opensaml.saml2.binding.security.SAML2HTTPRedirectDeflateSignatureRule;
import org.opensaml.saml2.metadata.Endpoint;
import org.opensaml.saml2.metadata.EntityDescriptor;
import org.opensaml.saml2.metadata.provider.MetadataProvider;
import org.opensaml.security.MetadataCredentialResolver;
import org.opensaml.security.MetadataCriteria;
import org.opensaml.security.SAMLSignatureProfileValidator;
import org.opensaml.ws.security.SecurityPolicyException;
import org.opensaml.ws.transport.http.HTTPInTransport;
import org.opensaml.ws.transport.http.HTTPOutTransport;
import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
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.parse.BasicParserPool;
import org.opensaml.xml.security.CriteriaSet;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.credential.ChainingCredentialResolver;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.credential.StaticCredentialResolver;
import org.opensaml.xml.security.credential.UsageType;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import org.opensaml.xml.security.criteria.UsageCriteria;
import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver;
import org.opensaml.xml.security.x509.X509Credential;
import org.opensaml.xml.signature.Signature;
import org.opensaml.xml.signature.SignatureTrustEngine;
import org.opensaml.xml.signature.Signer;
import org.opensaml.xml.signature.impl.ExplicitKeySignatureTrustEngine;
import org.opensaml.xml.signature.impl.SignatureBuilder;
import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLHelper;
import org.opensaml.xml.validation.ValidationException;
import org.w3c.dom.Element;

import com.alfaariss.oa.OAException;
import com.alfaariss.oa.RequestorEvent;
import com.alfaariss.oa.SystemErrors;
import com.alfaariss.oa.api.configuration.IConfigurationManager;
import com.alfaariss.oa.api.logging.IAuthority;
import com.alfaariss.oa.api.requestor.IRequestor;
import com.alfaariss.oa.api.session.ISession;
import com.alfaariss.oa.engine.core.Engine;
import com.alfaariss.oa.engine.core.crypto.CryptoException;
import com.alfaariss.oa.engine.core.crypto.CryptoManager;
import com.alfaariss.oa.engine.core.requestor.RequestorPool;
import com.alfaariss.oa.engine.core.requestor.factory.IRequestorPoolFactory;
import com.alfaariss.oa.engine.core.session.factory.ISessionFactory;
import com.alfaariss.oa.engine.core.tgt.factory.ITGTFactory;
import com.alfaariss.oa.util.saml2.SAML2IssueInstantWindow;
import com.alfaariss.oa.util.saml2.SAML2Requestor;
import com.alfaariss.oa.util.saml2.ISAML2Requestors;
import com.alfaariss.oa.util.saml2.SAML2SecurityException;
import com.alfaariss.oa.util.saml2.crypto.SAML2CryptoUtils;

/**
 * Abstract SAML2 profile implementation.
 *
 * @author MHO
 * @author Alfa & Ariss
 */
public abstract class AbstractSAML2Profile implements ISAML2Profile, IAuthority {
    /** Configuration element names */
    public static final String EL_IDPPROXY = "idpproxy";
    public static final String ATTR_ENABLE_SHADOWED_ENTITYID = "enableShadowedEntityId";

    /** Profile id */
    protected String _sID;
    /** OA Profile ID */
    protected String _sOAProfileID;
    /** Session factory */
    protected ISessionFactory _sessionFactory;
    /** requestor pool factory */
    protected IRequestorPoolFactory _requestorPoolFactory;
    /** crypto manager */
    protected CryptoManager _cryptoManager;
    /** TGT factory */
    protected ITGTFactory _tgtFactory;
    /** Entity ID to be used in response */
    protected String _sEntityID;
    /** URL to the SAML profile */
    protected String _sProfileURL;
    /** Event logger */
    protected Log _eventLogger;
    /** Path to the OA WebSSO profile*/
    protected String _sWebSSOPath;
    /** EntityDescriptor */
    protected EntityDescriptor _entityDescriptor;
    /** Requestors object */
    protected ISAML2Requestors _requestors;
    /** Signing is enabled in OA */
    protected boolean _signingEnabled;
    /** IssueInstant accept window object */
    protected SAML2IssueInstantWindow _issueInstantWindow;
    /** The XML parser pool */
    protected BasicParserPool _pool;
    /** SAMLSignatureProfileValidator */
    protected SAMLSignatureProfileValidator _profileValidator;
    /** KeyInfoCredentialResolver */
    protected KeyInfoCredentialResolver _keyInfoCredResolver;

    /** Configurable option to enable SAML2 IDP proxy, that shadows
     * the entityId of the proxied IDP; defaults to 'false' */
    protected boolean _bEnableProxiedEntityId = false;

    private final static String AUTHORITY_NAME = "SAML2 Profile";

    private Log _logger;

    /**
     * @see com.alfaariss.oa.util.saml2.profile.ISAML2Profile#init(com.alfaariss.oa.api.configuration.IConfigurationManager, org.w3c.dom.Element, org.opensaml.saml2.metadata.EntityDescriptor, java.lang.String, java.lang.String, com.alfaariss.oa.util.saml2.SAML2Requestors, com.alfaariss.oa.util.saml2.SAML2IssueInstantWindow, java.lang.String)
     */
    @Override
    public void init(IConfigurationManager configurationManager, Element config, EntityDescriptor entityDescriptor,
            String sBaseUrl, String sWebSSOPath, ISAML2Requestors requestors,
            SAML2IssueInstantWindow issueInstantWindow, String sProfileID) throws OAException {
        try {
            _logger = LogFactory.getLog(this.getClass());
            _eventLogger = LogFactory.getLog(Engine.EVENT_LOGGER);
            _profileValidator = new SAMLSignatureProfileValidator();
            _pool = new BasicParserPool();
            _pool.setNamespaceAware(true);

            //TODO EVB, MHO: DefaultKeyInfoCredentialResolver sufficient?
            _keyInfoCredResolver = Configuration.getGlobalSecurityConfiguration()
                    .getDefaultKeyInfoCredentialResolver();

            _entityDescriptor = entityDescriptor;
            _sWebSSOPath = sWebSSOPath;
            _requestors = requestors;
            _issueInstantWindow = issueInstantWindow;
            _sOAProfileID = sProfileID;
            _sEntityID = _entityDescriptor.getEntityID();

            Engine engine = Engine.getInstance();
            _sessionFactory = engine.getSessionFactory();
            _tgtFactory = engine.getTGTFactory();
            _requestorPoolFactory = engine.getRequestorPoolFactory();
            _cryptoManager = engine.getCryptoManager();

            _sID = configurationManager.getParam(config, "id");
            if (_sID == null) {
                _logger.error("No 'id' item found in 'profile' section in configuration");
                throw new OAException(SystemErrors.ERROR_CONFIG_READ);
            }

            //create profile url
            StringBuffer sbProfileURL = new StringBuffer(sBaseUrl);
            if (!sBaseUrl.endsWith("/"))
                sbProfileURL.append("/");
            sbProfileURL.append(_sID);
            _sProfileURL = sbProfileURL.toString();

            try {
                _signingEnabled = false;
                SAML2CryptoUtils.retrieveMySigningCredentials(_cryptoManager, _sEntityID);
                SAML2CryptoUtils.getXMLSignatureURI(_cryptoManager);
                SAML2CryptoUtils.getXMLDigestMethodURI(_cryptoManager.getMessageDigest());
                _signingEnabled = true;
                _logger.info("Signing enabled");
            } catch (OAException e) {
                //Logged in SAML2CryptoUtils
                _logger.info("Signing disabled");
            }

            // initialize IDP Proxy mode
            Element elIDPProxy = configurationManager.getSection(config, EL_IDPPROXY);
            if (elIDPProxy == null) {
                _bEnableProxiedEntityId = false;
                _logger.info("No optional '" + EL_IDPPROXY + "' section found; disabled by default.");
            } else {
                String sEnabled = configurationManager.getParam(elIDPProxy, ATTR_ENABLE_SHADOWED_ENTITYID);
                if ("true".equalsIgnoreCase(sEnabled)) {
                    _bEnableProxiedEntityId = true;
                } else if (!"false".equalsIgnoreCase(sEnabled)) {
                    if (sEnabled != null)
                        _logger.warn("Invalid value for " + EL_IDPPROXY + "@" + ATTR_ENABLE_SHADOWED_ENTITYID
                                + ": '" + sEnabled + "'; allowed: {'true', 'false'}");
                }
                _logger.info("IDPProxy Shadowed Entity Id mode enabled: " + _bEnableProxiedEntityId);
            }

        } catch (OAException e) {
            throw e;
        } catch (Exception e) {
            _logger.fatal("Internal error during initialize", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * @see ISAML2Profile#destroy()
     */
    @Override
    public void destroy() {

    }

    /**
     * @see com.alfaariss.oa.util.saml2.profile.ISAML2Profile#getID()
     */
    @Override
    public String getID() {
        return _sID;
    }

    /**
     * @see com.alfaariss.oa.api.logging.IAuthority#getAuthority()
     */
    @Override
    public String getAuthority() {
        return AUTHORITY_NAME;
    }

    /**
     * Returns the URL to this profile.
     * 
     * @return The URL as String.
     */
    protected String getProfileURL() {
        return _sProfileURL;
    }

    /**
     * Build an endpoint.
     * 
     * @param elementName The type of service for the endpoint.
     * @param sBinding The binding to be used.
     * @param sLocation The endpoint location.
     * @param sResponseLocation The optional response location. 
     * @return The constructed endpoint.
     */
    protected Endpoint buildMetadataEndpoint(QName elementName, String sBinding, String sLocation,
            String sResponseLocation) {
        XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();

        XMLObjectBuilder endpointBuilder = builderFactory.getBuilder(elementName);

        Endpoint samlEndpoint = (Endpoint) endpointBuilder.buildObject(elementName);
        samlEndpoint.setLocation(sLocation);
        samlEndpoint.setBinding(sBinding);

        if (sResponseLocation != null)
            samlEndpoint.setResponseLocation(sResponseLocation);

        return samlEndpoint;
    }

    /**
     * Sign the suplied saml object.
     * @param samlObject saml object to be signed
     * @throws OAException If the crypto configuration in OA is unsupported
     */
    protected void signSAMLObject(SignableSAMLObject samlObject) throws OAException {
        try {
            XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory();
            //Build signature
            SignatureBuilder builder = (SignatureBuilder) builderFactory.getBuilder(Signature.DEFAULT_ELEMENT_NAME);
            Signature signature = builder.buildObject(Signature.DEFAULT_ELEMENT_NAME);

            signature.setSignatureAlgorithm(SAML2CryptoUtils.getXMLSignatureURI(_cryptoManager));

            //Get signing credentials
            X509Credential signingX509Cred = SAML2CryptoUtils.retrieveMySigningCredentials(_cryptoManager,
                    _sEntityID);

            signature.setSigningCredential(signingX509Cred);

            SecurityHelper.prepareSignatureParams(signature, signingX509Cred, null, null);

            samlObject.setSignature(signature);

            //update digest algorithm
            SAMLObjectContentReference contentReference = ((SAMLObjectContentReference) signature
                    .getContentReferences().get(0));
            contentReference
                    .setDigestAlgorithm(SAML2CryptoUtils.getXMLDigestMethodURI(_cryptoManager.getMessageDigest()));

            //Marshall 
            Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(samlObject);
            if (marshaller == null) {
                _logger.error("No marshaller registered for " + samlObject.getElementQName()
                        + ", unable to marshall assertion");
                throw new OAException(SystemErrors.ERROR_INTERNAL);
            }
            if (samlObject.getDOM() == null)
                marshaller.marshall(samlObject);

            Signer.signObject(signature);
        } catch (OAException e) {
            throw e;
        } catch (MarshallingException e) {
            _logger.warn("Marshalling error while signing object", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        } catch (Exception e) {
            _logger.error("Could not sign object", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * Forwards the user to the OA WebSSO Servlet.
     * <br>
     * Before forwarding the session.setProfileURL() is set and the session is 
     * added as an attribute in the request .
     * @param request The servlet request.
     * @param response The servlet response.
     * @param session The user session
     * @throws OAException If the user can't be forwarded.
     */
    protected void forwardUser(HttpServletRequest request, HttpServletResponse response, ISession session)
            throws OAException {
        try {
            StringBuffer sbProfileUrl = new StringBuffer();
            sbProfileUrl.append(_sProfileURL);
            sbProfileUrl.append("?");
            sbProfileUrl.append(ISession.ID_NAME);
            sbProfileUrl.append("=");
            sbProfileUrl.append(session.getId());

            session.setProfileURL(sbProfileUrl.toString());

            request.setAttribute(ISession.ID_NAME, session);

            RequestDispatcher oDispatcher = request.getRequestDispatcher(_sWebSSOPath);
            if (oDispatcher == null) {
                _logger.warn("There is no requestor dispatcher supported with name: " + _sWebSSOPath);
                throw new OAException(SystemErrors.ERROR_INTERNAL);
            }

            oDispatcher.forward(request, response);
        } catch (OAException e) {
            throw e;
        } catch (Exception e) {
            _logger.fatal("Could not forward user", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
    }

    /**
     * Caller should perform debug enabled check
     * Logs the XML object as debug logging. 
     * @param xmlObject The XML object that must be logged.
     */
    protected void logXML(XMLObject xmlObject) {
        assert _logger.isDebugEnabled() : "Logger debug state not checked";
        Element eDOM = xmlObject.getDOM();
        if (eDOM == null) {
            Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(xmlObject);
            if (marshaller != null) {
                try {
                    eDOM = marshaller.marshall(xmlObject);
                } catch (MarshallingException e) {
                    _logger.debug("Could not prettyPrint XML object", e);
                }
            }
        }

        if (eDOM != null) {
            String sXML = XMLHelper.prettyPrintXML(eDOM);
            _logger.debug(sXML);
        }

    }

    /**
     * Creates a default empty SAML context object.
     *  
     * @param request Servlet request.
     * @param response Servlet response.
     * @return Default SAML context object.
     */
    protected SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> createEncodingContext(
            HttpServletRequest request, HttpServletResponse response) {
        HTTPInTransport inTransport = new HttpServletRequestAdapter(request);

        HTTPOutTransport outTransport = new HttpServletResponseAdapter(response, request.isSecure());

        SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> context = new BasicSAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject>();

        context.setInboundMessageTransport(inTransport);
        context.setOutboundMessageTransport(outTransport);

        return context;
    }

    /**
     * Validate the decoded SAML2 request message.
     *
     * Performs basic message verification:  
     * <dl>
     *  <dd>{@link AbstractSAML2Profile#validateRequestor(String)}</dd>
     *      <dt>Validate requestor</dt>
     *  <dd>{@link AbstractSAML2Profile#validateSignature(SAMLMessageContext, 
     *      SAML2Requestor, String)}
     *  </dd>
     *      <dt>Validate signature</dt>
     *  <dd>Mandatory signing verification</dd>
     *      <dt>Verify if request MUST be signed</dt>
     * <dl>     
     * 
     * @param context The message context containing decoded message
     * @param role The peer entity role.
     * @return SAML2Requestor if available, else <code>null</code>.
     * @throws SAML2SecurityException If message should be rejected.
     * @throws OAException If an internal error occurs
     */
    protected SAML2Requestor validateRequest(
            SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> context, QName role)
            throws SAML2SecurityException, OAException {
        context.setPeerEntityRole(role);
        String sIssuer = context.getInboundMessageIssuer();
        return validateMessage(context, sIssuer);
    }

    /**
     * Validate the decoded SAML2 response message.
     *
     * Performs basic message verification:  
     * <dl>
     *  <dd>{@link AbstractSAML2Profile#validateRequestor(String)}</dd>
     *      <dt>Validate requestor</dt>
     *  <dd>{@link AbstractSAML2Profile#validateSignature(SAMLMessageContext, 
     *      SAML2Requestor, String)}
     *  </dd>
     *      <dt>Validate signature</dt>
     *  <dd>Mandatory signing verification</dd>
     *      <dt>Verify if request MUST be signed</dt>
     * <dl>     
     * 
     * @param context The message context containing decoded message
     * @param role The peer entity role.
     * @return SAML2Requestor if available, else <code>null</code>.
     * @throws SAML2SecurityException If message should be rejected.
     * @throws OAException If an internal error occurs
     */
    protected SAML2Requestor validateResponse(
            SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> context, QName role)
            throws SAML2SecurityException, OAException {
        context.setPeerEntityRole(role);
        String sIssuer = context.getInboundMessageIssuer();
        return validateMessage(context, sIssuer);
    }

    /**
     * Business rule and profile validation of the issuer.
     *
     * @param sRequestorID The inbound issuer.
     * @return The requestor object.
     * @throws SAML2SecurityException If the requestor is invalid.
     * @throws OAException If validation fails due to internal error.
     */
    protected IRequestor validateRequestor(String sRequestorID) throws SAML2SecurityException, OAException {
        if (sRequestorID == null) {
            _logger.debug("Missing issuer");
            throw new SAML2SecurityException(RequestorEvent.REQUEST_INVALID);
        }

        IRequestor oRequestor = _requestorPoolFactory.getRequestor(sRequestorID);
        if (oRequestor == null) {
            StringBuffer sbError = new StringBuffer("Unknown requestor found in request: ");
            sbError.append(sRequestorID);
            _logger.debug(sbError.toString());
            throw new SAML2SecurityException(RequestorEvent.REQUEST_INVALID);
        }

        if (!oRequestor.isEnabled()) {
            _logger.debug("Disabled requestor found in request: " + sRequestorID);
            throw new SAML2SecurityException(RequestorEvent.REQUEST_INVALID);
        }

        RequestorPool oRequestorPool = _requestorPoolFactory.getRequestorPool(oRequestor.getID());
        if (oRequestorPool == null) {
            _logger.warn("Requestor not available in a pool: " + oRequestor.getID());
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }

        if (!oRequestorPool.isEnabled()) {
            StringBuffer sbError = new StringBuffer("Requestor '");
            sbError.append(oRequestor.getID());
            sbError.append("' is found in a disabled requestor pool: ");
            sbError.append(oRequestorPool.getID());
            _logger.warn(sbError.toString());
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }

        return oRequestor;
    }

    /**
     * Validate a inbound message signature and/or simple signature.
     * @param context The message context.
     * @param requestor The requestor. 
     * @param issuer The inbound message issuer
     * @return <code>true</code> if signature is valid, otherwise <code>false</code>. 
     * @throws OAException If validation fails due to internal error.
     */
    protected boolean validateSignature(
            SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> context,
            SAML2Requestor requestor, String issuer) throws OAException {
        boolean bValid = false;
        try {
            // requestMessage == null is checked
            SignableSAMLObject message = context.getInboundSAMLMessage();

            Signature signature = message.getSignature();
            if (message.isSigned()) {
                //Validate against profile in order to prevent certain types 
                //of denial-of-service attacks associated with signature verification
                _profileValidator.validate(signature);
            }

            //Create ChainingCredentialResolver
            ChainingCredentialResolver chainingCredentialResolver = new ChainingCredentialResolver();

            //TODO EVB, JRE, RDV: define order of credential resolvers and test

            //Metadata credentials
            if (requestor != null) {
                MetadataProvider mdProvider = requestor.getMetadataProvider();
                if (mdProvider != null) //Metadata provider available
                {
                    _logger.debug("Metadata provider found for issuer: " + issuer);
                    MetadataCredentialResolver mdCredResolver = new MetadataCredentialResolver(mdProvider);
                    chainingCredentialResolver.getResolverChain().add(mdCredResolver);
                }
            }

            //OA Engine credentials
            try {
                if (_signingEnabled) //OA Signing enabled
                {
                    Credential signingCred = SAML2CryptoUtils.retrieveSigningCredentials(_cryptoManager, issuer);
                    StaticCredentialResolver oaResolver = new StaticCredentialResolver(signingCred);
                    chainingCredentialResolver.getResolverChain().add(oaResolver);
                }
            } catch (CryptoException e) //No certificate found
            {
                _logger.debug("No trusted certificate found for issuer: " + issuer);
                //Ignore
            }

            //TODO EVB, JRE, RDV: define order of credential resolvers and test                        
            if (chainingCredentialResolver.getResolverChain().isEmpty()) {
                _logger.warn("No trusted certificate or metadata found for issuer: " + issuer);
                //bValid = false already    
            } else {
                //Create trust engine                
                //TODO EVB: trust engine and resolver creation can be placed in one-time init code (e.g. SAML2Requestor)
                SignatureTrustEngine sigTrustEngine = new ExplicitKeySignatureTrustEngine(
                        chainingCredentialResolver, _keyInfoCredResolver);

                if (message.isSigned()) //Validate XML signature (if applicable)
                {
                    //create criteria set            
                    CriteriaSet criteriaSet = new CriteriaSet();
                    criteriaSet.add(new EntityIDCriteria(issuer));
                    MetadataCriteria mdCriteria = new MetadataCriteria(context.getPeerEntityRole(),
                            context.getInboundSAMLProtocol());
                    criteriaSet.add(mdCriteria);
                    criteriaSet.add(new UsageCriteria(UsageType.SIGNING));
                    bValid = sigTrustEngine.validate(signature, criteriaSet);
                } else
                    bValid = true; //Message itself not signed

                if (bValid) //Message not signed or valid signature
                {
                    //Validate simple signature for GET (if applicable)
                    SAML2HTTPRedirectDeflateSignatureRule ruleGET = new SAML2HTTPRedirectDeflateSignatureRule(
                            sigTrustEngine);
                    ruleGET.evaluate(context);
                    //Validate simple signature for POST (if applicable)
                    SAML2HTTPPostSimpleSignRule rulePOST = new SAML2HTTPPostSimpleSignRule(sigTrustEngine, _pool,
                            _keyInfoCredResolver);
                    rulePOST.evaluate(context);
                }
            }
        } catch (SecurityPolicyException e) {
            // Indicates signature was not cryptographically valid, or possibly a processing error
            _logger.debug("Invalid signature", e);
            bValid = false;
        } catch (ValidationException e) {
            // Indicates signature was not cryptographically valid, or possibly a processing error
            _logger.debug("Invalid signature", e);
            bValid = false;
        } catch (SecurityException e) //Internal processing error
        {
            _logger.error("Processing error evaluating the signature", e);
            throw new OAException(SystemErrors.ERROR_INTERNAL);
        }
        return bValid;
    }

    // Validate the decoded SAML2 message.     
    private SAML2Requestor validateMessage(
            SAMLMessageContext<SignableSAMLObject, SignableSAMLObject, SAMLObject> context, String issuer)
            throws SAML2SecurityException, OAException {
        // requestMessage == null is checked
        SignableSAMLObject message = context.getInboundSAMLMessage();

        //Validate requestor
        IRequestor oaRequestor = validateRequestor(issuer);

        //Retrieve requestor info
        boolean mandatorySigning = _requestors.isDefaultSigningEnabled();
        SAML2Requestor requestor = _requestors.getRequestor(oaRequestor);
        if (requestor != null)
            mandatorySigning = requestor.isSigningEnabled();

        //Validate signature  
        HTTPInTransport inTransport = (HTTPInTransport) context.getInboundMessageTransport();
        String sigParam = inTransport.getParameterValue("Signature");
        boolean bSignatureParam = !DatatypeHelper.isEmpty(sigParam);
        if (bSignatureParam || message.isSigned()) {
            if (!validateSignature(context, requestor, issuer)) {
                _logger.debug("Invalid XML signature received for message from issuer: " + issuer);
                throw new SAML2SecurityException(RequestorEvent.REQUEST_INVALID);
            }

            _logger.debug("XML signature validation okay");

        } else if (mandatorySigning) //Verify mandatory signing
        {
            _logger.debug("No mandatory signature received from issuer: " + issuer);
            throw new SAML2SecurityException(RequestorEvent.REQUEST_INVALID);
        }

        return requestor;
    }
}