Package org.opensaml.common.binding.security

Source Code of org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule

/*
* Licensed to the University Corporation for Advanced Internet Development,
* Inc. (UCAID) under one or more contributor license agreements.  See the
* NOTICE file distributed with this work for additional information regarding
* copyright ownership. The UCAID licenses this file to You under the Apache
* License, Version 2.0 (the "License"); you may not use this file except in
* compliance with the License.  You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.opensaml.common.binding.security;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.security.MetadataCriteria;
import org.opensaml.ws.message.MessageContext;
import org.opensaml.ws.security.SecurityPolicyException;
import org.opensaml.ws.security.SecurityPolicyRule;
import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
import org.opensaml.xml.security.CriteriaSet;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.credential.Credential;
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.signature.SignatureTrustEngine;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Base class for security rules which verify simple "blob" signatures computed over some components of a request.
*/
public abstract class BaseSAMLSimpleSignatureSecurityPolicyRule implements SecurityPolicyRule {

    /** Logger. */
    private final Logger log = LoggerFactory.getLogger(BaseSAMLSimpleSignatureSecurityPolicyRule.class);

    /** Signature trust engine used to validate raw signatures. */
    private SignatureTrustEngine trustEngine;

    /**
     * Constructor.
     *
     * @param engine the signature trust engine to use for signature validataion
     */
    protected BaseSAMLSimpleSignatureSecurityPolicyRule(SignatureTrustEngine engine) {
        trustEngine = engine;
    }

    /** {@inheritDoc} */
    public void evaluate(MessageContext messageContext) throws SecurityPolicyException {
        log.debug("Evaluating simple signature rule of type: {}", getClass().getName());
        if (!(messageContext instanceof SAMLMessageContext)) {
            log.debug("Invalid message context type, this policy rule only supports SAMLMessageContext");
            return;
        }

        if (!(messageContext.getInboundMessageTransport() instanceof HttpServletRequestAdapter)) {
            log.debug("Invalid inbound message transport type, this rule only supports HttpServletRequestAdapter");
            return;
        }

        SAMLMessageContext samlMsgCtx = (SAMLMessageContext) messageContext;
        HttpServletRequestAdapter requestAdapter = (HttpServletRequestAdapter) messageContext
                .getInboundMessageTransport();
        HttpServletRequest request = requestAdapter.getWrappedRequest();

        if (!ruleHandles(request, samlMsgCtx)) {
            log.debug("Rule can not handle this request, skipping processing");
            return;
        }

        byte[] signature = getSignature(request);
        if (signature == null || signature.length == 0) {
            log.debug("HTTP request was not signed via simple signature mechanism, skipping");
            return;
        }

        String sigAlg = getSignatureAlgorithm(request);
        if (DatatypeHelper.isEmpty(sigAlg)) {
            log.warn("Signature algorithm could not be extracted from request, can not validate simple signature");
            return;
        }

        byte[] signedContent = getSignedContent(request);
        if (signedContent == null || signedContent.length == 0) {
            log.warn("Signed content could not be extracted from HTTP request, can not validate");
            return;
        }

        doEvaluate(signature, signedContent, sigAlg, request, samlMsgCtx);
    }

    /**
     * Evaluate the simple signature based on information in the request and/or message context.
     *
     * @param signature the signature value
     * @param signedContent the content that was signed
     * @param algorithmURI the signature algorithm URI which was used to sign the content
     * @param request the HTTP servlet request being processed
     * @param samlMsgCtx the SAML message context being processed
     *
     * @throws SecurityPolicyException thrown if there are errors during the signature validation process
     *
     */
    private void doEvaluate(byte[] signature, byte[] signedContent, String algorithmURI, HttpServletRequest request,
            SAMLMessageContext samlMsgCtx) throws SecurityPolicyException {

        List<Credential> candidateCredentials = getRequestCredentials(request, samlMsgCtx);

        String contextIssuer = samlMsgCtx.getInboundMessageIssuer();

        if (contextIssuer != null) {
            log.debug("Attempting to validate SAML protocol message simple signature using context issuer: {}",
                    contextIssuer);
            CriteriaSet criteriaSet = buildCriteriaSet(contextIssuer, samlMsgCtx);
            if (validateSignature(signature, signedContent, algorithmURI, criteriaSet, candidateCredentials)) {
                log.info("Validation of request simple signature succeeded");
                if (!samlMsgCtx.isInboundSAMLMessageAuthenticated()) {
                    log.info("Authentication via request simple signature succeeded for context issuer entity ID {}",
                            contextIssuer);
                    samlMsgCtx.setInboundSAMLMessageAuthenticated(true);
                }
                return;
            } else {
                log.warn("Validation of request simple signature failed for context issuer: {}", contextIssuer);
                throw new SecurityPolicyException("Validation of request simple signature failed for context issuer");
            }
        }
           
        String derivedIssuer = deriveSignerEntityID(samlMsgCtx);
        if (derivedIssuer != null) {
            log.debug("Attempting to validate SAML protocol message simple signature using derived issuer: {}",
                    derivedIssuer);
            CriteriaSet criteriaSet = buildCriteriaSet(derivedIssuer, samlMsgCtx);
            if (validateSignature(signature, signedContent, algorithmURI, criteriaSet, candidateCredentials)) {
                log.info("Validation of request simple signature succeeded");
                if (!samlMsgCtx.isInboundSAMLMessageAuthenticated()) {
                    log.info("Authentication via request simple signature succeeded for derived issuer {}",
                            derivedIssuer);
                    samlMsgCtx.setInboundMessageIssuer(derivedIssuer);
                    samlMsgCtx.setInboundSAMLMessageAuthenticated(true);
                }
                return;
            } else {
                log.warn("Validation of request simple signature failed for derived issuer: {}", derivedIssuer);
                throw new SecurityPolicyException("Validation of request simple signature failed for derived issuer");
            }
        }
       
        log.warn("Neither context nor derived issuer available, can not attempt SAML simple signature validation");
        throw new SecurityPolicyException("No message issuer available, can not attempt simple signature validation");
    }

    /**
     * Validate the simple signature.
     *
     * @param signature the signature value
     * @param signedContent the content that was signed
     * @param algorithmURI the signature algorithm URI which was used to sign the content
     * @param criteriaSet criteria used to describe and/or resolve the information which serves as the basis for trust
     *            evaluation
     * @param candidateCredentials the request-derived candidate credential(s) containing the validation key for the
     *            signature (optional)
     * @return true if signature can be verified successfully, false otherwise
     *
     * @throws SecurityPolicyException thrown if there are errors during the signature validation process
     *
     */
    protected boolean validateSignature(byte[] signature, byte[] signedContent, String algorithmURI,
            CriteriaSet criteriaSet, List<Credential> candidateCredentials) throws SecurityPolicyException {

        SignatureTrustEngine engine = getTrustEngine();

        // Some bindings allow candidate signing credentials to be supplied (e.g. via ds:KeyInfo), some do not.
        // So have 2 slightly different cases.
        try {
            if (candidateCredentials == null || candidateCredentials.isEmpty()) {
                if (engine.validate(signature, signedContent, algorithmURI, criteriaSet, null)) {
                    log.debug("Simple signature validation (with no request-derived credentials) was successful");
                    return true;
                } else {
                    log.warn("Simple signature validation (with no request-derived credentials) failed");
                    return false;
                }
            } else {
                for (Credential cred : candidateCredentials) {
                    if (engine.validate(signature, signedContent, algorithmURI, criteriaSet, cred)) {
                        log.debug("Simple signature validation succeeded with a request-derived credential");
                        return true;
                    }
                }
                log.warn("Signature validation using request-derived credentials failed");
                return false;
            }
        } catch (SecurityException e) {
            log.warn("There was an error evaluating the request's simple signature using the trust engine", e);
            throw new SecurityPolicyException("Error during trust engine evaluation of the simple signature", e);
        }
    }

    /**
     * Extract any candidate validation credentials from the request and/or message context.
     *
     * Some bindings allow validataion keys for the simple signature to be supplied, and others do not.
     *
     * @param request the HTTP servlet request being processed
     * @param samlContext the SAML message context being processed
     * @return a list of candidate validation credentials in the request, or null if none were present
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected List<Credential> getRequestCredentials(HttpServletRequest request, SAMLMessageContext samlContext)
            throws SecurityPolicyException {
        // This will be specific to the binding and message types, so no default.
        return null;
    }

    /**
     * Gets the engine used to validate the signature.
     *
     * @return engine engine used to validate the signature
     */
    protected SignatureTrustEngine getTrustEngine() {
        return trustEngine;
    }

    /**
     * Extract the signature value from the request, in the form suitable for input into
     * {@link SignatureTrustEngine#validate(byte[], byte[], String, CriteriaSet, Credential)}.
     *
     * Defaults to the Base64-decoded value of the HTTP request parameter named <code>Signature</code>.
     *
     * @param request the HTTP servlet request
     * @return the signature value
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected byte[] getSignature(HttpServletRequest request) throws SecurityPolicyException {
        String signature = request.getParameter("Signature");
        if (DatatypeHelper.isEmpty(signature)) {
            return null;
        }
        return Base64.decode(signature);
    }

    /**
     * Extract the signature algorithm URI value from the request.
     *
     * Defaults to the HTTP request parameter named <code>SigAlg</code>.
     *
     * @param request the HTTP servlet request
     * @return the signature algorithm URI value
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected String getSignatureAlgorithm(HttpServletRequest request) throws SecurityPolicyException {
        return request.getParameter("SigAlg");
    }

    /**
     * Derive the signer's entity ID from the message context.
     *
     * This is implementation-specific and there is no default. This is primarily an extension point for subclasses.
     *
     * @param samlContext the SAML message context being processed
     * @return the signer's derived entity ID
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected String deriveSignerEntityID(SAMLMessageContext samlContext) throws SecurityPolicyException {
        // No default
        return null;
    }

    /**
     * Build a criteria set suitable for input to the trust engine.
     *
     * @param entityID the candidate issuer entity ID which is being evaluated
     * @param samlContext the message context which is being evaluated
     * @return a newly constructly set of criteria suitable for the configured trust engine
     * @throws SecurityPolicyException thrown if criteria set can not be constructed
     */
    protected CriteriaSet buildCriteriaSet(String entityID, SAMLMessageContext samlContext)
            throws SecurityPolicyException {

        CriteriaSet criteriaSet = new CriteriaSet();
        if (!DatatypeHelper.isEmpty(entityID)) {
            criteriaSet.add(new EntityIDCriteria(entityID));
        }

        MetadataCriteria mdCriteria = new MetadataCriteria(samlContext.getPeerEntityRole(), samlContext
                .getInboundSAMLProtocol());
        criteriaSet.add(mdCriteria);

        criteriaSet.add(new UsageCriteria(UsageType.SIGNING));

        return criteriaSet;
    }

    /**
     * Get the content over which to validate the signature, in the form suitable for input into
     * {@link SignatureTrustEngine#validate(byte[], byte[], String, CriteriaSet, Credential)}.
     *
     * @param request the HTTP servlet request being processed
     * @return the signed content extracted from the request, in the format suitable for input to the trust engine.
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected abstract byte[] getSignedContent(HttpServletRequest request) throws SecurityPolicyException;

    /**
     * Determine whether the rule should handle the request, based on the unwrapped HTTP servlet request and/or message
     * context.
     *
     * @param request the HTTP servlet request being processed
     * @param samlMsgCtx the SAML message context being processed
     * @return true if the rule should attempt to process the request, otherwise false
     * @throws SecurityPolicyException thrown if there is an error during request processing
     */
    protected abstract boolean ruleHandles(HttpServletRequest request, SAMLMessageContext samlMsgCtx)
            throws SecurityPolicyException;

}
TOP

Related Classes of org.opensaml.common.binding.security.BaseSAMLSimpleSignatureSecurityPolicyRule

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.