org.xipki.pki.scep.serveremulator.ScepServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.pki.scep.serveremulator.ScepServlet.java

Source

/*
 *
 * Copyright (c) 2013 - 2016 Lijun Liao
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License version 3
 * as published by the Free Software Foundation with the addition of the
 * following permission added to Section 15 as permitted in Section 7(a):
 *
 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
 * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
 * OF THIRD PARTY RIGHTS.
 *
 * 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 <http://www.gnu.org/licenses/>.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License.
 *
 * You can be released from the requirements of the license by purchasing
 * a commercial license. Buying such a license is mandatory as soon as you
 * develop commercial activities involving the XiPKI software without
 * disclosing the source code of your own applications.
 *
 * For more information, please contact Lijun Liao at this
 * address: lijun.liao@gmail.com
 */

package org.xipki.pki.scep.serveremulator;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Date;

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

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.cmp.PKIMessage;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cms.CMSAbsentContent;
import org.bouncycastle.cms.CMSException;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.util.encoders.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.audit.AuditEvent;
import org.xipki.commons.audit.AuditLevel;
import org.xipki.commons.audit.AuditService;
import org.xipki.commons.audit.AuditStatus;
import org.xipki.commons.common.util.LogUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.security.util.X509Util;
import org.xipki.pki.scep.exception.MessageDecodingException;
import org.xipki.pki.scep.message.CaCaps;
import org.xipki.pki.scep.message.NextCaMessage;
import org.xipki.pki.scep.transaction.CaCapability;
import org.xipki.pki.scep.transaction.Operation;
import org.xipki.pki.scep.util.ScepConstants;
import org.xipki.pki.scep.util.ScepUtil;

/**
 * URL http://host:port/scep/&lt;name&gt;/&lt;profile-alias&gt;/pkiclient.exe
 *
 * @author Lijun Liao
 * @since 2.0.0
 */

public class ScepServlet extends HttpServlet {

    private static final Logger LOG = LoggerFactory.getLogger(ScepServlet.class);

    private static final long serialVersionUID = 1L;

    private static final String CT_RESPONSE = ScepConstants.CT_PKI_MESSAGE;

    private AuditService auditService;

    private ScepResponder responder;

    public ScepServlet(final ScepResponder responder) {
        this.responder = ParamUtil.requireNonNull("responder", responder);
    }

    public AuditService getAuditService() {
        return auditService;
    }

    public void setAuditService(final AuditService auditService) {
        this.auditService = auditService;
    }

    @Override
    public void doGet(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        service(request, response, false);
    }

    @Override
    public void doPost(final HttpServletRequest request, final HttpServletResponse response)
            throws ServletException, IOException {
        service(request, response, true);
    }

    private void service(final HttpServletRequest request, final HttpServletResponse response, final boolean post)
            throws ServletException, IOException {
        String servletPath = request.getServletPath();

        AuditEvent event = new AuditEvent(new Date());
        event.setApplicationName(ScepAuditConstants.APPNAME);
        event.setName(ScepAuditConstants.NAME_PERF);
        event.addEventData(ScepAuditConstants.NAME_servletPath, servletPath);

        AuditLevel auditLevel = AuditLevel.INFO;
        AuditStatus auditStatus = AuditStatus.SUCCESSFUL;
        String auditMessage = null;

        OutputStream respStream = response.getOutputStream();

        try {
            CaCaps caCaps = responder.getCaCaps();
            if (post && !caCaps.containsCapability(CaCapability.POSTPKIOperation)) {
                final String message = "HTTP POST is not supported";
                LOG.error(message);

                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentLength(0);

                auditMessage = message;
                auditStatus = AuditStatus.FAILED;
                return;
            }

            String operation = request.getParameter("operation");
            event.addEventData(ScepAuditConstants.NAME_operation, operation);

            if ("PKIOperation".equalsIgnoreCase(operation)) {
                CMSSignedData reqMessage;
                // parse the request
                try {
                    byte[] content;
                    if (post) {
                        content = ScepUtil.read(request.getInputStream());
                    } else {
                        String b64 = request.getParameter("message");
                        content = Base64.decode(b64);
                    }

                    reqMessage = new CMSSignedData(content);
                } catch (Exception ex) {
                    final String message = "invalid request";
                    LogUtil.error(LOG, ex, message);
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    response.setContentLength(0);

                    auditMessage = message;
                    auditStatus = AuditStatus.FAILED;
                    return;
                }

                ContentInfo ci;
                try {
                    ci = responder.servicePkiOperation(reqMessage, event);
                } catch (MessageDecodingException ex) {
                    final String message = "could not decrypt and/or verify the request";
                    LogUtil.error(LOG, ex, message);
                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                    response.setContentLength(0);

                    auditMessage = message;
                    auditStatus = AuditStatus.FAILED;
                    return;
                } catch (CaException ex) {
                    final String message = "system internal error";
                    LogUtil.error(LOG, ex, message);
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    response.setContentLength(0);

                    auditMessage = message;
                    auditStatus = AuditStatus.FAILED;
                    return;
                }
                byte[] respBytes = ci.getEncoded();
                response.setContentType(CT_RESPONSE);
                response.setContentLength(respBytes.length);
                respStream.write(respBytes);
            } else if (Operation.GetCACaps.getCode().equalsIgnoreCase(operation)) {
                // CA-Ident is ignored
                response.setContentType(ScepConstants.CT_TEXT_PLAIN);
                byte[] caCapsBytes = responder.getCaCaps().getBytes();
                respStream.write(caCapsBytes);
                response.setContentLength(caCapsBytes.length);
            } else if (Operation.GetCACert.getCode().equalsIgnoreCase(operation)) {
                // CA-Ident is ignored
                byte[] respBytes;
                String ct;
                if (responder.getRaEmulator() == null) {
                    ct = ScepConstants.CT_X509_CA_CERT;
                    respBytes = responder.getCaEmulator().getCaCertBytes();
                } else {
                    ct = ScepConstants.CT_X509_CA_RA_CERT;
                    CMSSignedDataGenerator cmsSignedDataGen = new CMSSignedDataGenerator();
                    try {
                        cmsSignedDataGen
                                .addCertificate(new X509CertificateHolder(responder.getCaEmulator().getCaCert()));
                        ct = ScepConstants.CT_X509_CA_RA_CERT;
                        cmsSignedDataGen
                                .addCertificate(new X509CertificateHolder(responder.getRaEmulator().getRaCert()));
                        CMSSignedData degenerateSignedData = cmsSignedDataGen.generate(new CMSAbsentContent());
                        respBytes = degenerateSignedData.getEncoded();
                    } catch (CMSException ex) {
                        final String message = "system internal error";
                        LogUtil.error(LOG, ex, message);
                        response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                        response.setContentLength(0);

                        auditMessage = message;
                        auditStatus = AuditStatus.FAILED;
                        return;
                    }
                } // end if (responder.getRAEmulator() == null) {
                response.setContentType(ct);
                response.setContentLength(respBytes.length);
                respStream.write(respBytes);
            } else if (Operation.GetNextCACert.getCode().equalsIgnoreCase(operation)) {
                if (responder.getNextCaAndRa() == null) {
                    response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                    response.setContentLength(0);

                    auditMessage = "SCEP operation '" + operation + "' is not permitted";
                    auditStatus = AuditStatus.FAILED;
                    return;
                }

                try {
                    NextCaMessage nextCaMsg = new NextCaMessage();
                    nextCaMsg.setCaCert(X509Util.toX509Cert(responder.getNextCaAndRa().getCaCert()));
                    if (responder.getNextCaAndRa().getRaCert() != null) {
                        X509Certificate raCert = X509Util.toX509Cert(responder.getNextCaAndRa().getRaCert());
                        nextCaMsg.setRaCerts(Arrays.asList(raCert));
                    }

                    ContentInfo signedData = responder.encode(nextCaMsg);
                    byte[] respBytes = signedData.getEncoded();
                    response.setContentType(ScepConstants.CT_X509_NEXT_CA_CERT);
                    response.setContentLength(respBytes.length);
                    response.getOutputStream().write(respBytes);
                } catch (Exception ex) {
                    final String message = "system internal error";
                    LogUtil.error(LOG, ex, message);
                    response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                    response.setContentLength(0);

                    auditMessage = message;
                    auditStatus = AuditStatus.FAILED;
                }
            } else {
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                response.setContentLength(0);

                auditMessage = "unknown SCEP operation '" + operation + "'";
                auditStatus = AuditStatus.FAILED;
            } // end if ("PKIOperation".equalsIgnoreCase(operation))
        } catch (EOFException ex) {
            final String message = "connection reset by peer";
            LogUtil.warn(LOG, ex, message);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.setContentLength(0);
        } catch (Throwable th) {
            final String message = "Throwable thrown, this should not happen!";
            LogUtil.error(LOG, th, message);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.setContentLength(0);
            auditLevel = AuditLevel.ERROR;
            auditStatus = AuditStatus.FAILED;
            auditMessage = "internal error";
        } finally {
            try {
                response.flushBuffer();
            } finally {
                audit(auditService, event, auditLevel, auditStatus, auditMessage);
            }
        } // end try
    } // method service

    protected PKIMessage generatePkiMessage(final InputStream is) throws IOException {
        ParamUtil.requireNonNull("is", is);
        ASN1InputStream asn1Stream = new ASN1InputStream(is);

        try {
            return PKIMessage.getInstance(asn1Stream.readObject());
        } finally {
            try {
                asn1Stream.close();
            } catch (Exception ex) {
                LOG.error("could not close stream: {}", ex.getMessage());
            }
        }
    }

    static void audit(final AuditService auditService, final AuditEvent event, final AuditLevel auditLevel,
            final AuditStatus auditStatus, final String auditMessage) {
        if (auditLevel != null) {
            event.setLevel(auditLevel);
        }

        if (auditStatus != null) {
            event.setStatus(auditStatus);
        }

        if (auditMessage != null) {
            event.addEventData("message", auditMessage);
        }

        event.finish();
        auditService.logEvent(event);
    }

}