be.fedict.eid.dss.protocol.simple.client.SignatureResponseProcessorServlet.java Source code

Java tutorial

Introduction

Here is the source code for be.fedict.eid.dss.protocol.simple.client.SignatureResponseProcessorServlet.java

Source

/*
 * eID Digital Signature Service Project.
 * Copyright (C) 2009-2010 FedICT.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version
 * 3.0 as published by the Free Software Foundation.
 *
 * This software 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, see 
 * http://www.gnu.org/licenses/.
 */

package be.fedict.eid.dss.protocol.simple.client;

import java.io.IOException;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import be.fedict.eid.dss.client.DigitalSignatureServiceClient;
import be.fedict.eid.dss.client.DocumentNotFoundException;

/**
 * Processes the response from the eID DSS simple protocol.
 * <p/>
 * <p>
 * The following init-params are required:
 * </p>
 * <ul>
 * <li><tt>NextPage</tt>: indicates the page where the flow continues.</li>
 * <li><tt>ErrorPage</tt>: indicates the page to be shown in case of errors.</li>
 * <li><tt>ErrorMessageSessionAttribute</tt>: indicates which session attribute
 * to use for reporting an error. This session attribute can be used on the
 * error page.</li>
 * </ul>
 * <p/>
 * <p>
 * In case the eID DSS puts a service signature on the DSS response, the
 * following init-params become required for validation of the service
 * signature:
 * <ul>
 * <li><tt>TargetSessionAttribute</tt>: refers to the session attribute
 * containing the target page of the DSS signature request.</li>
 * <li><tt>SignatureRequestSessionAttribute</tt> or
 * <tt>SignatureRequestIdSessionAttribute</tt>: refers to session attribute
 * containing the base64 encoded signature request respectively the signature
 * request ID case artifact binding is used.</li>
 * </ul>
 * </p>
 * <p/>
 * <p>
 * The following init-params are optional:
 * </p>
 * <ul>
 * <li><tt>SignedDocumentSessionAttribute</tt>: indicates which session
 * attribute to use to push in the signed document as byte array as returned by
 * the eID DSS. If not specified, it assumer a response for the DSS Artifact
 * Binding where the application wishes to manually call the eID DSS Web Service
 * for retrieval of the signed document. This implies the
 * <tt>SignatureResponseIdSessionAttribute</tt> param to be required, which will
 * hold the id of the signed document, temporarily stored at the eID DSS web
 * service.</li>
 * <li><tt>SignatureCertificateSessionAttribute</tt>: indicates which session
 * attribute to use to push in the signature certificate as returned by the eID
 * DSS.</li>
 * <li><tt>ServiceFingerprint</tt>: contains the hexadecimal encoded SHA1
 * fingerprint of the service certificate used to sign the DSS response. Use
 * this parameter when a very simple trust model is sufficient.</li>
 * <li><tt>CancelPage</tt>: the page to be shown in case the user cancelled the
 * eID DSS signature ceremony. If not present the user gets redirected towards
 * the error page.</li>
 * </ul>
 * 
 * @author Frank Cornelis
 */
public class SignatureResponseProcessorServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static final Log LOG = LogFactory.getLog(SignatureResponseProcessorServlet.class);

    // Flow
    public static final String IFRAME_INIT_PARAM = "IFrame";
    public static final String NEXT_PAGE_INIT_PARAM = "NextPage";
    public static final String ERROR_PAGE_INIT_PARAM = "ErrorPage";
    public static final String CANCEL_PAGE_INIT_PARAM = "CancelPage";
    public static final String ERROR_MESSAGE_SESSION_ATTRIBUTE_INIT_PARAM = "ErrorMessageSessionAttribute";

    // Response
    public static final String SIGNED_DOCUMENT_SESSION_ATTRIBUTE_INIT_PARAM = "SignedDocumentSessionAttribute";
    public static final String SIGNATURE_RESPONSE_ID_SESSION_ATTRIBUTE_INIT_PARAM = "SignatureResponseIdSessionAttribute";
    public static final String SIGNATURE_CERTIFICATE_SESSION_ATTRIBUTE_INIT_PARAM = "SignatureCertificateSessionAttribute";
    public static final String SERVICE_FINGERPRINT_INIT_PARAM = "ServiceFingerprint";

    // WS Client
    public static final String DSS_WS_URL_INIT_PARAM = "DssWSUrl";
    public static final String DSS_WS_PROXY_HOST_INIT_PARAM = "DssWSProxyHost";
    public static final String DSS_WS_PROXY_PORT_INIT_PARAM = "DssWSProxyPort";

    // Request
    public static final String TARGET_SESSION_ATTRIBUTE_INIT_PARAM = "TargetSessionAttribute";
    public static final String SIGNATURE_REQUEST_SESSION_ATTRIBUTE_INIT_PARAM = "SignatureRequestSessionAttribute";
    public static final String SIGNATURE_REQUEST_ID_SESSION_ATTRIBUTE_INIT_PARAM = "SignatureRequestIdSessionAttribute";
    public static final String RELAY_STATE_SESSION_ATTRIBUTE_INIT_PARAM = "RelayStateSessionAttribute";

    // Runtime
    public static final String SIGNATURE_RESPONSE_SERVICE_INIT_PARAM = "SignatureResponseService";

    // Flow config
    private boolean iframe;
    private String nextPage;
    private String errorPage;
    private String cancelPage;
    private String errorMessageSessionAttribute;

    // Response config
    private String signedDocumentSessionAttribute;
    private String signatureResponseIdSessionAttribute;
    private String signatureCertificateSessionAttribute;

    // WS CLient config
    private String dssWSUrl;
    private String dssWSProxyHost;
    private int dssWSProxyPort;

    // Request config
    private String targetSessionAttribute;
    private String signatureRequestSessionAttribute;
    private String signatureRequestIdSessionAttribute;
    private String relayStateSessionAttribute;

    // Runtime config
    private ServiceLocator<SignatureResponseService> signatureResponseServiceLocator;

    private SignatureResponseProcessor signatureResponseProcessor;

    @Override
    public void init(ServletConfig config) throws ServletException {

        // flow
        LOG.debug("init");
        this.iframe = null != config.getInitParameter(IFRAME_INIT_PARAM);
        this.nextPage = getRequiredInitParameter(config, NEXT_PAGE_INIT_PARAM);
        LOG.debug("next page: " + this.nextPage);
        this.cancelPage = config.getInitParameter(CANCEL_PAGE_INIT_PARAM);
        this.errorPage = getRequiredInitParameter(config, ERROR_PAGE_INIT_PARAM);
        LOG.debug("error page: " + this.errorPage);
        this.errorMessageSessionAttribute = getRequiredInitParameter(config,
                ERROR_MESSAGE_SESSION_ATTRIBUTE_INIT_PARAM);
        LOG.debug("error message session attribute: " + this.errorMessageSessionAttribute);

        // WS client config
        this.dssWSUrl = config.getInitParameter(DSS_WS_URL_INIT_PARAM);
        this.dssWSProxyHost = config.getInitParameter(DSS_WS_PROXY_HOST_INIT_PARAM);
        String dssWSProxyPortString = config.getInitParameter(DSS_WS_PROXY_PORT_INIT_PARAM);
        if (null != dssWSProxyPortString) {
            this.dssWSProxyPort = Integer.parseInt(dssWSProxyPortString);
        }
        if (null != this.dssWSUrl) {
            LOG.debug("DSS WS: " + this.dssWSUrl + " (proxy=" + this.dssWSProxyHost + ":" + this.dssWSProxyPort
                    + ")");
        }

        // Response Config
        this.signedDocumentSessionAttribute = config.getInitParameter(SIGNED_DOCUMENT_SESSION_ATTRIBUTE_INIT_PARAM);
        this.signatureResponseIdSessionAttribute = config
                .getInitParameter(SIGNATURE_RESPONSE_ID_SESSION_ATTRIBUTE_INIT_PARAM);
        this.signatureCertificateSessionAttribute = config
                .getInitParameter(SIGNATURE_CERTIFICATE_SESSION_ATTRIBUTE_INIT_PARAM);

        if (null == this.signedDocumentSessionAttribute && null == this.signatureResponseIdSessionAttribute) {
            throw new ServletException("Need \"" + SIGNED_DOCUMENT_SESSION_ATTRIBUTE_INIT_PARAM + "\" or \""
                    + SIGNATURE_RESPONSE_ID_SESSION_ATTRIBUTE_INIT_PARAM + "\" init params");
        }

        String encodedServiceFingerprint = config.getInitParameter(SERVICE_FINGERPRINT_INIT_PARAM);
        byte[] serviceFingerprint;
        if (null != encodedServiceFingerprint) {
            LOG.debug("service fingerprint: " + encodedServiceFingerprint);
            try {
                serviceFingerprint = Hex.decodeHex(encodedServiceFingerprint.toCharArray());
            } catch (DecoderException e) {
                throw new ServletException("service fingerprint decoding error: " + e.getMessage(), e);
            }
        } else {
            serviceFingerprint = null;
        }

        // Request Config
        this.targetSessionAttribute = config.getInitParameter(TARGET_SESSION_ATTRIBUTE_INIT_PARAM);
        this.signatureRequestSessionAttribute = config
                .getInitParameter(SIGNATURE_REQUEST_SESSION_ATTRIBUTE_INIT_PARAM);
        this.signatureRequestIdSessionAttribute = config
                .getInitParameter(SIGNATURE_REQUEST_ID_SESSION_ATTRIBUTE_INIT_PARAM);
        this.relayStateSessionAttribute = config.getInitParameter(RELAY_STATE_SESSION_ATTRIBUTE_INIT_PARAM);

        // runtime config
        this.signatureResponseServiceLocator = new ServiceLocator<SignatureResponseService>(
                SIGNATURE_RESPONSE_SERVICE_INIT_PARAM, config);

        // Construct response processor
        this.signatureResponseProcessor = new SignatureResponseProcessor(serviceFingerprint);
    }

    private String getRequiredInitParameter(ServletConfig config, String paramName) throws ServletException {
        String paramValue = config.getInitParameter(paramName);
        if (null == paramValue) {
            throw new ServletException("missing init-param: " + paramName);
        }
        return paramValue;
    }

    /**
     * Clears the used session attributes. Also returns a reference to the
     * previously signed document.
     * 
     * @param httpSession
     *            the http session
     * @return reference to previously signed document
     */
    private byte[] clearAllSessionAttribute(HttpSession httpSession) {
        httpSession.removeAttribute(this.errorMessageSessionAttribute);
        byte[] signedDocument = (byte[]) httpSession.getAttribute(this.signedDocumentSessionAttribute);
        httpSession.removeAttribute(this.signedDocumentSessionAttribute);
        if (null != this.signatureCertificateSessionAttribute) {
            httpSession.removeAttribute(this.signatureCertificateSessionAttribute);
        }
        return signedDocument;
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        showErrorPage("DSS response processor not available via GET", request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        LOG.debug("doPost");
        HttpSession httpSession = request.getSession();
        byte[] previousSignedDocument = clearAllSessionAttribute(httpSession);

        String target = (String) httpSession.getAttribute(this.targetSessionAttribute);
        String base64encodedSignatureRequest = (String) httpSession
                .getAttribute(this.signatureRequestSessionAttribute);
        String signatureRequestId = (String) httpSession.getAttribute(this.signatureRequestIdSessionAttribute);
        String relayState = (String) httpSession.getAttribute(this.relayStateSessionAttribute);
        LOG.debug("RelayState: " + relayState);

        SignatureResponse signatureResponse;
        try {
            signatureResponse = this.signatureResponseProcessor.process(request, target,
                    base64encodedSignatureRequest, signatureRequestId, relayState);
        } catch (UserCancelledSignatureResponseProcessorException e) {
            if (null != this.cancelPage) {
                LOG.debug("redirecting to cancel page");
                /*
                 * In case of explicit user cancellation we preserve the signed
                 * document session attribute.
                 */
                httpSession.setAttribute(this.signedDocumentSessionAttribute, previousSignedDocument);
                redirectTo(response, request.getContextPath() + this.cancelPage);
                return;
            }
            showErrorPage(e.getMessage(), request, response);
            return;
        } catch (SignatureResponseProcessorException e) {
            showErrorPage(e.getMessage(), request, response);
            return;
        }

        byte[] decodedSignatureResponse = signatureResponse.getDecodedSignatureResponse();
        String signatureResponseId = signatureResponse.getSignatureResponseId();

        /*
         * Check signed document passed along, if not signatureResponseId should
         * be present.
         * 
         * If configuration is available for DSS WS Client fetch the signed
         * document here, else SP has to do it himself via signatureResponseId
         * pushed on session.
         */
        if (null == decodedSignatureResponse) {

            if (null == signatureResponseId) {
                showErrorPage("No signed document nor response ID found!", request, response);
                return;
            }

            // check WS client config available
            String dssWSUrl;
            if (this.signatureResponseServiceLocator.isConfigured()) {
                SignatureResponseService signatureResponseService = this.signatureResponseServiceLocator
                        .locateService();
                dssWSUrl = signatureResponseService.getDssWSUrl();
                LOG.debug("DSS WS URL: " + dssWSUrl);
            } else {
                dssWSUrl = this.dssWSUrl;
            }
            if (null != dssWSUrl) {
                DigitalSignatureServiceClient dssClient = new DigitalSignatureServiceClient(dssWSUrl);
                if (null != this.dssWSProxyHost) {
                    dssClient.setProxy(this.dssWSProxyHost, this.dssWSProxyPort);
                } else {
                    // disable previously set proxy
                    dssClient.setProxy(null, 0);
                }
                try {
                    decodedSignatureResponse = dssClient.retrieve(signatureResponseId);
                } catch (DocumentNotFoundException e) {
                    showErrorPage("Document not found at the eID DSS WS.", request, response);
                    return;
                }

            } else {

                if (null == this.signatureResponseIdSessionAttribute) {
                    showErrorPage("No SignatureResponseId session attribute " + "specified, aborting...", request,
                            response);
                    return;
                }
                // push response ID on session
                httpSession.setAttribute(this.signatureResponseIdSessionAttribute, signatureResponseId);

            }

            if (null != this.signatureRequestSessionAttribute) {
                httpSession.removeAttribute(this.signatureRequestSessionAttribute);
            }
            if (null != this.signatureRequestIdSessionAttribute) {
                httpSession.removeAttribute(this.signatureRequestIdSessionAttribute);
            }
        }

        /*
         * Push data into the HTTP session.
         */
        httpSession.setAttribute(this.signedDocumentSessionAttribute, decodedSignatureResponse);
        if (null != this.signatureCertificateSessionAttribute) {
            httpSession.setAttribute(this.signatureCertificateSessionAttribute,
                    signatureResponse.getSignatureCertificate());
        }

        /*
         * Continue work-flow.
         */
        redirectTo(response, request.getContextPath() + this.nextPage);
    }

    private void showErrorPage(String errorMessage, HttpServletRequest request, HttpServletResponse response)
            throws IOException {
        HttpSession httpSession = request.getSession();
        httpSession.setAttribute(this.errorMessageSessionAttribute, errorMessage);
        redirectTo(response, request.getContextPath() + this.errorPage);
    }

    private void redirectTo(HttpServletResponse response, String location) throws IOException {

        if (iframe) {
            response.setContentType("text/html");
            response.getWriter().println("<html>");
            response.getWriter().println("<head>");
            response.getWriter().println("<script type=\"text/javascript\">");
            response.getWriter().println("top.location.replace(\"" + location + "\");");
            response.getWriter().println("</script>");
            response.getWriter().println("</head>");
            response.getWriter().println("</html>");
        } else {
            response.sendRedirect(location);

        }
    }
}