Package org.opensaml.saml2.binding.encoding

Source Code of org.opensaml.saml2.binding.encoding.HTTPPostSimpleSignEncoder

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

package org.opensaml.saml2.binding.encoding;

import java.io.UnsupportedEncodingException;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.opensaml.common.binding.SAMLMessageContext;
import org.opensaml.common.xml.SAMLConstants;
import org.opensaml.ws.message.encoder.MessageEncodingException;
import org.opensaml.ws.transport.http.HTTPTransportUtils;
import org.opensaml.xml.Configuration;
import org.opensaml.xml.io.Marshaller;
import org.opensaml.xml.io.MarshallingException;
import org.opensaml.xml.security.SecurityConfiguration;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.SecurityHelper;
import org.opensaml.xml.security.SigningUtil;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.keyinfo.KeyInfoGenerator;
import org.opensaml.xml.signature.KeyInfo;
import org.opensaml.xml.util.Base64;
import org.opensaml.xml.util.DatatypeHelper;
import org.opensaml.xml.util.XMLHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* SAML 2.0 HTTP-POST-SimpleSign binding message encoder.
*
* <p>
* The spec does not preclude the SAML 2 protocol message from being signed using the XML Signature method, in addition
* to the SimpleSign method specified by this binding. Signing via XML Signature over the SAML request and response
* payload may be toggled by the <code>signXMLProtocolMessage</code> parameter to the constructor
* {@link HTTPPostSimpleSignEncoder#HTTPPostSimpleSignEncoder(VelocityEngine, String, boolean)}. If this constructor
* variant is not used, the flag defaults to <code>false</code>.
* </p>
*/
public class HTTPPostSimpleSignEncoder extends HTTPPostEncoder {

    /** Class logger. */
    private final Logger log = LoggerFactory.getLogger(HTTPPostSimpleSignEncoder.class);

    /**
     * Flag to indicate whether the SAML 2 protocol message should additionally be signed using the XML Signature, in
     * addition to SimpleSign.
     */
    private boolean signProtocolMessageWithXMLDSIG;

    /**
     * Constructor.
     *
     * @param engine Velocity engine instance used to create POST body
     * @param templateId ID of the template used to create POST body
     */
    public HTTPPostSimpleSignEncoder(VelocityEngine engine, String templateId) {
        super(engine, templateId);
        signProtocolMessageWithXMLDSIG = false;
    }

    /**
     * Constructor.
     *
     * @param engine Velocity engine instance used to create POST body
     * @param templateId ID of the template used to create POST body
     * @param signXMLProtocolMessage if true, the protocol message will be signed according to the XML Signature
     *            specification, in addition to the HTTP-POST-SimpleSign binding specification
     */
    public HTTPPostSimpleSignEncoder(VelocityEngine engine, String templateId, boolean signXMLProtocolMessage) {
        super(engine, templateId);
        signProtocolMessageWithXMLDSIG = signXMLProtocolMessage;
    }

    /** {@inheritDoc} */
    public String getBindingURI() {
        return SAMLConstants.SAML2_POST_SIMPLE_SIGN_BINDING_URI;
    }

    /** {@inheritDoc} */
    protected void signMessage(SAMLMessageContext messageContext) throws MessageEncodingException {
        if (signProtocolMessageWithXMLDSIG) {
            super.signMessage(messageContext);
        }
    }

    /** {@inheritDoc} */
    protected void populateVelocityContext(VelocityContext velocityContext, SAMLMessageContext messageContext,
            String endpointURL) throws MessageEncodingException {

        super.populateVelocityContext(velocityContext, messageContext, endpointURL);

        Credential signingCredential = messageContext.getOuboundSAMLMessageSigningCredential();
        if (signingCredential == null) {
            log.debug("No signing credential was supplied, skipping HTTP-Post simple signing");
            return;
        }

        // TODO pull SecurityConfiguration from SAMLMessageContext? needs to be added
        // TODO pull binding-specific keyInfoGenName from encoder setting, etc?
        String sigAlgURI = getSignatureAlgorithmURI(signingCredential, null);
        velocityContext.put("SigAlg", sigAlgURI);

        String formControlData = buildFormDataToSign(velocityContext, sigAlgURI);
        velocityContext.put("Signature", generateSignature(signingCredential, sigAlgURI, formControlData));

        KeyInfoGenerator kiGenerator = SecurityHelper.getKeyInfoGenerator(signingCredential, null, null);
        if (kiGenerator != null) {
            String kiBase64 = buildKeyInfo(signingCredential, kiGenerator);
            if (!DatatypeHelper.isEmpty(kiBase64)) {
                velocityContext.put("KeyInfo", kiBase64);
            }
        }
    }

    /**
     * Build the {@link KeyInfo} from the signing credential.
     *
     * @param signingCredential the credential used for signing
     * @param kiGenerator the generator for the KeyInfo
     * @throws MessageEncodingException thrown if there is an error generating or marshalling the KeyInfo
     * @return the marshalled, serialized and base64-encoded KeyInfo, or null if none was generated
     */
    protected String buildKeyInfo(Credential signingCredential, KeyInfoGenerator kiGenerator)
            throws MessageEncodingException {

        try {
            KeyInfo keyInfo = kiGenerator.generate(signingCredential);
            if (keyInfo != null) {
                Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(keyInfo);
                if (marshaller == null) {
                    log.error("No KeyInfo marshaller available from configuration");
                    throw new MessageEncodingException("No KeyInfo marshaller was configured");
                }
                String kiXML = XMLHelper.nodeToString(marshaller.marshall(keyInfo));
                String kiBase64 = Base64.encodeBytes(kiXML.getBytes(), Base64.DONT_BREAK_LINES);
                return kiBase64;
            } else {
                return null;
            }
        } catch (SecurityException e) {
            log.error("Error generating KeyInfo from signing credential", e);
            throw new MessageEncodingException("Error generating KeyInfo from signing credential", e);
        } catch (MarshallingException e) {
            log.error("Error marshalling KeyInfo based on signing credential", e);
            throw new MessageEncodingException("Error marshalling KeyInfo based on signing credential", e);
        }
    }

    /**
     * Build the form control data string over which the signature is computed.
     *
     * @param velocityContext the Velocity context which is already populated with the values for SAML message and relay
     *            state
     * @param sigAlgURI the signature algorithm URI
     *
     * @return the form control data string for signature computation
     */
    protected String buildFormDataToSign(VelocityContext velocityContext, String sigAlgURI) {
        StringBuilder builder = new StringBuilder();

        boolean isRequest = false;
        if (velocityContext.get("SAMLRequest") != null) {
            isRequest = true;
        }

        String msgB64;
        if (isRequest) {
            msgB64 = (String) velocityContext.get("SAMLRequest");
        } else {
            msgB64 = (String) velocityContext.get("SAMLResponse");
        }

        String msg = null;
        try {
            msg = new String(Base64.decode(msgB64), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // All JVM's required to support UTF-8
        }

        if (isRequest) {
            builder.append("SAMLRequest=" + msg);
        } else {
            builder.append("SAMLResponse=" + msg);
        }

        if (velocityContext.get("RelayState") != null) {
            builder.append("&RelayState=" + HTTPTransportUtils.urlDecode((String) velocityContext.get("RelayState")));
        }

        builder.append("&SigAlg=" + sigAlgURI);

        return builder.toString();
    }

    /**
     * Gets the signature algorithm URI to use with the given signing credential.
     *
     * @param credential the credential that will be used to sign the message
     * @param config the SecurityConfiguration to use (may be null)
     *
     * @return signature algorithm to use with the given signing credential
     *
     * @throws MessageEncodingException thrown if the algorithm URI could not be derived from the supplied credential
     */
    protected String getSignatureAlgorithmURI(Credential credential, SecurityConfiguration config)
            throws MessageEncodingException {

        SecurityConfiguration secConfig;
        if (config != null) {
            secConfig = config;
        } else {
            secConfig = Configuration.getGlobalSecurityConfiguration();
        }

        String signAlgo = secConfig.getSignatureAlgorithmURI(credential);

        if (signAlgo == null) {
            throw new MessageEncodingException("The signing credential's algorithm URI could not be derived");
        }

        return signAlgo;
    }

    /**
     * Generates the signature over the string of concatenated form control data as indicated by the SimpleSign spec.
     *
     * @param signingCredential credential that will be used to sign
     * @param algorithmURI algorithm URI of the signing credential
     * @param formData form control data to be signed
     *
     * @return base64 encoded signature of form control data
     *
     * @throws MessageEncodingException there is an error computing the signature
     */
    protected String generateSignature(Credential signingCredential, String algorithmURI, String formData)
            throws MessageEncodingException {

        log.debug(String.format(
                "Generating signature with key type '%s', algorithm URI '%s' over form control string '%s'",
                SecurityHelper.extractSigningKey(signingCredential).getAlgorithm(), algorithmURI, formData));

        String b64Signature = null;
        try {
            byte[] rawSignature = SigningUtil.signWithURI(signingCredential, algorithmURI, formData.getBytes("UTF-8"));
            b64Signature = Base64.encodeBytes(rawSignature, Base64.DONT_BREAK_LINES);
            log.debug("Generated digital signature value (base64-encoded) {}", b64Signature);
        } catch (SecurityException e) {
            log.error("Error during URL signing process", e);
            throw new MessageEncodingException("Unable to sign form control string", e);
        } catch (UnsupportedEncodingException e) {
            // UTF-8 encoding is required to be supported by all JVMs
        }

        return b64Signature;
    }

}
TOP

Related Classes of org.opensaml.saml2.binding.encoding.HTTPPostSimpleSignEncoder

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.