org.ejbca.ui.web.pub.CertDistServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.ui.web.pub.CertDistServlet.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA Community: The OpenSource Certificate Authority                *
 *                                                                       *
 *  This software is free software; you can redistribute it and/or       *
 *  modify it under the terms of the GNU Lesser General Public           *
 *  License as published by the Free Software Foundation; either         *
 *  version 2.1 of the License, or any later version.                    *
 *                                                                       *
 *  See terms of license at gnu.org.                                     *
 *                                                                       *
 *************************************************************************/

package org.ejbca.ui.web.pub;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;

import javax.ejb.EJB;
import javax.ejb.EJBException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.cesecore.CesecoreException;
import org.cesecore.authentication.tokens.AlwaysAllowLocalAuthenticationToken;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authentication.tokens.UsernamePrincipal;
import org.cesecore.authorization.AuthorizationDeniedException;
import org.cesecore.certificates.ca.CADoesntExistsException;
import org.cesecore.certificates.ca.SignRequestSignatureException;
import org.cesecore.certificates.certificate.CertificateStatus;
import org.cesecore.certificates.certificate.CertificateStoreSessionLocal;
import org.cesecore.certificates.crl.CrlStoreSessionLocal;
import org.cesecore.certificates.crl.RevokedCertInfo;
import org.cesecore.util.Base64;
import org.cesecore.util.CertTools;
import org.cesecore.util.StringTools;
import org.ejbca.core.ejb.ca.sign.SignSessionLocal;
import org.ejbca.cvc.CardVerifiableCertificate;
import org.ejbca.ui.web.RequestHelper;
import org.ejbca.util.HTMLTools;

/**
 * Servlet used to distribute certificates and CRLs.<br>
 *
 * The servlet is called with method GET or POST and syntax
 * <code>command=&lt;command&gt;</code>.
 * <p>The following commands are supported:<br>
 * <ul>
 * <li>crl - gets the latest CRL.
 * <li>deltacrl - gets the latest delta CRL.
 * <li>lastcert - gets latest certificate of a user, takes argument 'subject=<subjectDN>'.
 * <li>listcerts - lists all certificates of a user, takes argument 'subject=<subjectDN>'.
 * <li>revoked - checks if a certificate is revoked, takes arguments 'subject=<subjectDN>&serno=<serial number>'.
 * <li>cacert - returns ca certificate in PEM-format, takes argument 'issuer=<issuerDN>&level=<ca-level, 0=root>'
 * <li>nscacert - returns ca certificate for Firefox, same args as above
 * <li>iecacert - returns ca certificate for Internet Explorer, same args as above
 * </ul>
 * cacert, nscacert and iecacert also takes optional parameter level=<int 1,2,...>, where the level is
 * which ca certificate in a hierachy should be returned. 0=root (default), 1=sub to root etc.
 *
 * @version $Id: CertDistServlet.java 20264 2014-11-18 15:26:29Z anatom $
 */
public class CertDistServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    private static Logger log = Logger.getLogger(CertDistServlet.class);

    private static final String COMMAND_PROPERTY_NAME = "cmd";
    private static final String COMMAND_CRL = "crl";
    private static final String COMMAND_DELTACRL = "deltacrl";
    private static final String COMMAND_REVOKED = "revoked";
    private static final String COMMAND_EECERT = "eecert";
    private static final String COMMAND_CERT = "lastcert";
    private static final String COMMAND_LISTCERT = "listcerts";
    private static final String COMMAND_NSCACERT = "nscacert";
    private static final String COMMAND_IECACERT = "iecacert";
    private static final String COMMAND_CACERT = "cacert";
    private static final String COMMAND_CACHAIN = "cachain";

    private static final String SUBJECT_PROPERTY = "subject";
    private static final String CAID_PROPERTY = "caid";
    private static final String ISSUER_PROPERTY = "issuer";
    private static final String SERNO_PROPERTY = "serno";
    private static final String LEVEL_PROPERTY = "level";
    /* @Deprecated since EJBCA 6.3.0. MOZILLA_PROPERTY can be removed in EJBCA 6.4 or 6.5 */
    private static final String MOZILLA_PROPERTY = "moz";
    private static final String FORMAT_PROPERTY = "format";
    private static final String INSTALLTOBROWSER_PROPERTY = "installtobrowser";

    @EJB
    private CertificateStoreSessionLocal storesession;
    @EJB
    private CrlStoreSessionLocal crlSession;
    @EJB
    private SignSessionLocal signSession;

    /**
     * handles http post
     *
     * @param req servlet request
     * @param res servlet response
     *
     * @throws IOException input/output error
     * @throws ServletException error
     */
    public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException {
        log.trace(">doPost()");
        doGet(req, res);
        log.trace("<doPost()");
    } //doPost

    /**
     * handles http get
     *
     * @param req servlet request
     * @param res servlet response
     *
     * @throws IOException input/output error
     * @throws ServletException error
     */
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws java.io.IOException, ServletException {
        log.trace(">doGet()");

        String command;
        // Keep this for logging.
        String remoteAddr = req.getRemoteAddr();
        final AuthenticationToken administrator = new AlwaysAllowLocalAuthenticationToken(
                new UsernamePrincipal("PublicWeb: " + remoteAddr));
        //Admin administrator = new Admin(Admin.TYPE_PUBLIC_WEB_USER, remoteAddr);

        RequestHelper.setDefaultCharacterEncoding(req);
        String issuerdn = null;
        if (req.getParameter(ISSUER_PROPERTY) != null) {
            // HttpServetRequets.getParameter URLDecodes the value for you
            // No need to do it manually, that will cause problems with + characters
            issuerdn = req.getParameter(ISSUER_PROPERTY);
            issuerdn = CertTools.stringToBCDNString(issuerdn);
        }
        int caid = 0;
        if (req.getParameter(CAID_PROPERTY) != null) {
            caid = Integer.parseInt(req.getParameter(CAID_PROPERTY));
        }
        // See if the client wants the response cert or CRL in PEM format (default is DER)
        String format = req.getParameter(FORMAT_PROPERTY);
        command = req.getParameter(COMMAND_PROPERTY_NAME);
        if (command == null) {
            command = "";
        }
        if ((command.equalsIgnoreCase(COMMAND_CRL) || command.equalsIgnoreCase(COMMAND_DELTACRL))
                && issuerdn != null) {
            try {
                byte[] crl = null;
                if (command.equalsIgnoreCase(COMMAND_CRL)) {
                    crl = crlSession.getLastCRL(issuerdn, false); // CRL
                } else {
                    crl = crlSession.getLastCRL(issuerdn, true); // deltaCRL
                }
                X509CRL x509crl = CertTools.getCRLfromByteArray(crl);
                String dn = CertTools.getIssuerDN(x509crl);
                // We must remove cache headers for IE
                ServletUtils.removeCacheHeaders(res);
                // moz is only kept for backwards compatibility, can be removed in EJBCA 6.4 or 6.5
                String moz = req.getParameter(MOZILLA_PROPERTY);
                String filename = CertTools.getPartFromDN(dn, "CN") + ".crl";
                if (command.equalsIgnoreCase(COMMAND_DELTACRL)) {
                    filename = "delta_" + filename;
                }
                if ((moz == null) || !moz.equalsIgnoreCase("y")) {
                    res.setHeader("Content-disposition",
                            "attachment; filename=\"" + StringTools.stripFilename(filename) + "\"");
                }
                res.setContentType("application/x-x509-crl");
                if (StringUtils.equals(format, "PEM")) {
                    RequestHelper.sendNewB64File(Base64.encode(crl, true), res, filename,
                            RequestHelper.BEGIN_CRL_WITH_NL, RequestHelper.END_CRL_WITH_NL);
                } else {
                    res.setContentLength(crl.length);
                    res.getOutputStream().write(crl);
                }
                log.debug("Sent latest CRL to client at " + remoteAddr);
            } catch (Exception e) {
                log.debug("Error sending latest CRL to " + remoteAddr + ": ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error getting latest CRL.");
                return;
            }
        } else if (command.equalsIgnoreCase(COMMAND_EECERT)) {
            // HttpServetRequets.getParameter URLDecodes the value for you
            // No need to do it manually, that will cause problems with + characters
            String dn = req.getParameter(ISSUER_PROPERTY);
            if (dn == null) {
                log.debug("Bad request, no 'issuer' arg to 'eecert'.");
                res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                        "Usage command=eecert?issuer=<issuerdn>&serno=<serialnumber in hex>.");
                return;
            }
            String serno = req.getParameter(SERNO_PROPERTY);
            if (serno == null) {
                log.debug("Bad request, no 'serno' arg to 'eeceert'.");
                res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                        "Usage command=eecert?issuer=<issuerdn>&serno=<serialnumber in hex>.");
                return;
            }
            log.debug("Looking for certificate with issuer/serno '" + dn + "', '" + serno + "'.");
            try {
                // Serial number in hex
                Certificate cert = storesession.findCertificateByIssuerAndSerno(dn, new BigInteger(serno, 16));
                sendEndEntityCert(administrator, req, res, format, cert);
            } catch (NumberFormatException e) {
                log.debug("Error getting End Entity certificate, invalid serial number (hex): ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND,
                        "Error getting End Entity certificate, invalid serial number (hex).");
                return;
            } catch (CertificateEncodingException e) {
                log.info("Error getting End Entity certificate, invalid certificate?: ", e);
                res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                        "Error getting End Entity certificate, invalid certificate.");
                return;
            } catch (NoSuchFieldException e) {
                log.info("Error getting End Entity certificate, can not get field to generate filename?: ", e);
                res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                        "Error getting End Entity certificate, invalid certificate.");
                return;
            } catch (AuthorizationDeniedException e) {
                log.error("Error getting End Entity certificate, not authorized to create PKCS7: ", e);
                res.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                        "Error getting End Entity certificate, not authorized to create PKCS7.");
                return;
            } catch (CesecoreException e) {
                log.info(
                        "Error getting End Entity certificate, CA to create PKCS7 does not exist, or can not create PKCS7: ",
                        e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND,
                        "Error getting End Entity certificate, CA to create PKCS7 does not exist, or can not create PKCS7.");
                return;
            }
        } else if (command.equalsIgnoreCase(COMMAND_CERT) || command.equalsIgnoreCase(COMMAND_LISTCERT)) {
            // HttpServetRequets.getParameter URLDecodes the value for you
            // No need to do it manually, that will cause problems with + characters
            String dn = req.getParameter(SUBJECT_PROPERTY);
            if (dn == null) {
                log.debug("Bad request, no 'subject' arg to 'lastcert' or 'listcert' command.");
                res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                        "Usage command=lastcert/listcert?subject=<subjectdn>.");
                return;
            }
            try {
                log.debug("Looking for certificates for '" + dn + "'.");
                Collection<Certificate> certcoll = storesession.findCertificatesBySubject(dn);
                Object[] certs = certcoll.toArray();
                if (command.equalsIgnoreCase(COMMAND_CERT)) {
                    long maxdate = 0;
                    int latestcertno = -1;
                    for (int i = 0; i < certs.length; i++) {
                        if (i == 0) {
                            maxdate = CertTools.getNotBefore((Certificate) certs[i]).getTime();
                            latestcertno = 0;
                        } else if (CertTools.getNotBefore((Certificate) certs[i]).getTime() > maxdate) {
                            maxdate = CertTools.getNotBefore(((Certificate) certs[i])).getTime();
                            latestcertno = i;
                        }
                    }
                    Certificate certcert = null;
                    if (latestcertno > -1) {
                        certcert = (Certificate) certs[latestcertno];
                    }
                    if (certcert == null) {
                        log.debug("No certificate found for requested subject DN. '" + dn + "'.");
                        res.sendError(HttpServletResponse.SC_NOT_FOUND,
                                "No certificate found for requested subject DN.");
                    } else {
                        sendEndEntityCert(administrator, req, res, format, certcert);
                        log.debug("Sent latest certificate for '" + dn + "' to client at " + remoteAddr);
                    }
                }
                if (command.equalsIgnoreCase(COMMAND_LISTCERT)) {
                    res.setContentType("text/html");
                    PrintWriter pout = new PrintWriter(res.getOutputStream());
                    printHtmlHeader("Certificates for " + HTMLTools.htmlescape(dn), pout);
                    for (int i = 0; i < certs.length; i++) {
                        Date notBefore = CertTools.getNotBefore((Certificate) certs[i]);
                        Date notAfter = CertTools.getNotAfter((Certificate) certs[i]);
                        String subject = CertTools.getSubjectDN((Certificate) certs[i]);
                        String issuer = CertTools.getIssuerDN((Certificate) certs[i]);
                        BigInteger serno = CertTools.getSerialNumber((Certificate) certs[i]);
                        pout.println("<pre>Subject:" + subject);
                        pout.println("Issuer:" + issuer);
                        pout.println("NotBefore:" + notBefore.toString());
                        pout.println("NotAfter:" + notAfter.toString());
                        pout.println("Serial number:" + serno.toString());
                        pout.println("</pre>");
                        pout.println("<a href=\"certdist?cmd=revoked&issuer=" + URLEncoder.encode(issuer, "UTF-8")
                                + "&serno=" + serno.toString() + "\">Check if certificate is revoked</a>");
                        pout.println("<hr>");

                    }
                    if (certs.length == 0) {
                        pout.println("No certificates exists for '" + HTMLTools.htmlescape(dn) + "'.");
                    }
                    printHtmlFooter(pout);
                    pout.close();
                }
            } catch (Exception e) {
                log.debug("Error getting certificates for '" + dn + "' for " + remoteAddr + ": ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error getting certificates.");
                return;
            }
        } else if ((command.equalsIgnoreCase(COMMAND_NSCACERT) || command.equalsIgnoreCase(COMMAND_IECACERT)
                || command.equalsIgnoreCase(COMMAND_CACERT)) && (issuerdn != null || caid != 0)) {
            String lev = req.getParameter(LEVEL_PROPERTY);
            int level = 0;
            boolean pkcs7 = false;
            if (lev != null) {
                level = Integer.parseInt(lev);
            } else {
                pkcs7 = true;
            } // CA is level 0, next over root level 1 etc etc, -1 returns chain as PKCS7
            try {
                Certificate[] chain = null;
                chain = getCertificateChain(administrator, caid, issuerdn);
                // chain.length-1 is last cert in chain (root CA)
                if (chain.length < level) {
                    PrintStream ps = new PrintStream(res.getOutputStream());
                    ps.println("No CA certificate of level " + level + " exist.");
                    log.debug("No CA certificate of level " + level + " exist.");
                    return;
                }
                Certificate cacert = (Certificate) chain[level];
                String filename = RequestHelper.getFileNameFromCertNoEnding(cacert, "ca");
                byte[] enccert = null;
                if (pkcs7) {
                    enccert = signSession.createPKCS7(administrator, cacert, true);
                } else {
                    enccert = cacert.getEncoded();
                }
                if (command.equalsIgnoreCase(COMMAND_NSCACERT)) {
                    res.setContentType("application/x-x509-ca-cert");
                    res.setContentLength(enccert.length);
                    res.getOutputStream().write(enccert);
                    log.debug("Sent CA cert to NS client, len=" + enccert.length + ".");
                } else if (command.equalsIgnoreCase(COMMAND_IECACERT)) {
                    // We must remove cache headers for IE
                    ServletUtils.removeCacheHeaders(res);
                    if (pkcs7) {
                        res.setHeader("Content-disposition",
                                "attachment; filename=\"" + StringTools.stripFilename(filename) + ".p7c\"");
                    } else {
                        String ending = ".crt";
                        if (cacert instanceof CardVerifiableCertificate) {
                            ending = ".cvcert";
                        }
                        res.setHeader("Content-disposition",
                                "attachment; filename=\"" + StringTools.stripFilename(filename + ending) + "\"");
                    }
                    res.setContentType("application/octet-stream");
                    res.setContentLength(enccert.length);
                    res.getOutputStream().write(enccert);
                    log.debug("Sent CA cert to IE client, len=" + enccert.length + ".");
                } else if (command.equalsIgnoreCase(COMMAND_CACERT)) {
                    byte[] b64cert = Base64.encode(enccert);
                    String out;
                    if (pkcs7) {
                        out = "-----BEGIN PKCS7-----\n";
                    } else {
                        out = "-----BEGIN CERTIFICATE-----\n";
                    }
                    out += new String(b64cert);
                    if (pkcs7) {
                        out += "\n-----END PKCS7-----\n";
                    } else {
                        out += "\n-----END CERTIFICATE-----\n";
                    }
                    // We must remove cache headers for IE
                    ServletUtils.removeCacheHeaders(res);
                    res.setHeader("Content-disposition",
                            "attachment; filename=\"" + StringTools.stripFilename(filename) + ".pem\"");
                    res.setContentType("application/octet-stream");
                    res.setContentLength(out.length());
                    res.getOutputStream().write(out.getBytes());
                    log.debug("Sent CA cert to client, len=" + out.length() + ".");
                } else {
                    res.setContentType("text/plain");
                    res.getOutputStream().println(
                            "Commands=" + COMMAND_NSCACERT + " || " + COMMAND_IECACERT + " || " + COMMAND_CACERT);
                    return;
                }
            } catch (Exception e) {
                log.debug("Error getting CA certificates: ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error getting CA certificates.");
                return;
            }
        } else if (command.equalsIgnoreCase(COMMAND_CACHAIN) && (issuerdn != null || caid != 0)) {
            // Full certificate chain for CA was requested.
            try {
                handleCaChainCommands(administrator, issuerdn, caid, format, res);
            } catch (NoSuchFieldException e) {
                log.debug("Error getting certificates for '" + caid + "' for " + remoteAddr + ": ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error getting certificates.");
                return;
            }
        } else if (command.equalsIgnoreCase(COMMAND_REVOKED)) {
            String dn = req.getParameter(ISSUER_PROPERTY);
            if (dn == null) {
                log.debug("Bad request, no 'issuer' arg to 'revoked' command.");
                res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                        "Usage command=revoked?issuer=<issuerdn>&serno=<serialnumber>.");
                return;
            }
            String serno = req.getParameter(SERNO_PROPERTY);
            if (serno == null) {
                log.debug("Bad request, no 'serno' arg to 'revoked' command.");
                res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                        "Usage command=revoked?issuer=<issuerdn>&serno=<serialnumber>.");
                return;
            }
            log.debug("Looking for certificate for '" + dn + "' and serno='" + serno + "'.");
            try {
                CertificateStatus revinfo = storesession.getStatus(dn, new BigInteger(serno));
                PrintWriter pout = new PrintWriter(res.getOutputStream());
                res.setContentType("text/html");
                printHtmlHeader("Check revocation", pout);
                if (revinfo != null) {
                    if (revinfo.revocationReason == RevokedCertInfo.NOT_REVOKED) {
                        pout.println("<h1>NOT REVOKED</h1>");
                        pout.println("Certificate with issuer '" + HTMLTools.htmlescape(dn)
                                + "' and serial number '" + HTMLTools.htmlescape(serno) + "' is NOT revoked.");
                    } else {
                        pout.println("<h1>REVOKED</h1>");
                        pout.println("Certificate with issuer '" + HTMLTools.htmlescape(dn)
                                + "' and serial number '" + HTMLTools.htmlescape(serno) + "' is revoked.");
                        pout.println("RevocationDate is '" + revinfo.revocationDate + "' and reason '"
                                + revinfo.revocationReason + "'.");
                    }
                } else {
                    pout.println("<h1>CERTIFICATE DOES NOT EXIST</h1>");
                    pout.println("Certificate with issuer '" + HTMLTools.htmlescape(dn) + "' and serial number '"
                            + HTMLTools.htmlescape(serno) + "' does not exist.");
                }
                printHtmlFooter(pout);
                pout.close();
            } catch (Exception e) {
                log.debug("Error checking revocation for '" + dn + "' with serno '" + serno + "': ", e);
                res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error checking revocation.");
                return;
            }
        } else {
            res.sendError(HttpServletResponse.SC_BAD_REQUEST,
                    "Commands=cacert | lastcert | listcerts | crl | deltacrl | revoked && issuer=<issuerdn>");
            return;
        }

    }

    /** Sends a certificate for download to a client */
    private void sendEndEntityCert(final AuthenticationToken administrator, HttpServletRequest req,
            HttpServletResponse res, String format, Certificate certcert)
            throws CertificateEncodingException, NoSuchFieldException, IOException, CADoesntExistsException,
            SignRequestSignatureException, AuthorizationDeniedException {
        byte[] cert = certcert.getEncoded();
        String ending;
        if (certcert instanceof CardVerifiableCertificate) {
            ending = ".cvcert";
        } else if (StringUtils.equals(format, "PEM") || StringUtils.equals(format, "chain")) {
            ending = ".pem";
        } else if (StringUtils.equals(format, "PKCS7")) {
            ending = ".p7b";
        } else {
            ending = ".crt";
        }
        String filename = RequestHelper.getFileNameFromCertNoEnding(certcert, "ca");
        filename = filename + ending;
        // We must remove cache headers for IE
        ServletUtils.removeCacheHeaders(res);
        if ("netscape".equals(req.getParameter(INSTALLTOBROWSER_PROPERTY))) {
            res.setContentType("application/x-x509-user-cert");
        } else {
            res.setHeader("Content-disposition",
                    "attachment; filename=\"" + StringTools.stripFilename(filename) + "\"");
            res.setContentType("application/octet-stream");
        }
        if (StringUtils.equals(format, "PEM")) {
            RequestHelper.sendNewB64File(Base64.encode(cert, true), res, filename,
                    RequestHelper.BEGIN_CERTIFICATE_WITH_NL, RequestHelper.END_CERTIFICATE_WITH_NL);
        } else if (StringUtils.equals(format, "PKCS7")) {
            byte[] pkcs7 = signSession.createPKCS7(administrator, certcert, true);
            RequestHelper.sendNewB64File(Base64.encode(pkcs7, true), res, filename,
                    RequestHelper.BEGIN_PKCS7_WITH_NL, RequestHelper.END_PKCS7_WITH_NL);
        } else if (StringUtils.equals(format, "chain")) {
            int issuerCAId = CertTools.getIssuerDN(certcert).hashCode();
            LinkedList<Certificate> chain = new LinkedList<Certificate>(
                    signSession.getCertificateChain(administrator, issuerCAId));
            chain.addFirst(certcert);
            byte[] chainbytes = CertTools.getPemFromCertificateChain(chain);
            RequestHelper.sendNewB64File(chainbytes, res, filename, "", ""); // chain includes begin/end already
        } else {
            res.setContentLength(cert.length);
            res.getOutputStream().write(cert);
        }
    } // doGet

    private Certificate[] getCertificateChain(AuthenticationToken administrator, int caid, String issuerdn)
            throws AuthorizationDeniedException {
        Certificate[] chain;
        if (caid != 0) {
            chain = (Certificate[]) signSession.getCertificateChain(administrator, caid)
                    .toArray(new Certificate[0]);
        } else {
            chain = (Certificate[]) signSession.getCertificateChain(administrator, issuerdn.hashCode())
                    .toArray(new Certificate[0]);
        }
        return chain;
    }

    private void handleCaChainCommands(AuthenticationToken administrator, String issuerdn, int caid, String format,
            HttpServletResponse res) throws IOException, NoSuchFieldException {
        try {
            Certificate[] chain = getCertificateChain(administrator, caid, issuerdn);
            // Reverse the chain to get proper ordering for chain file
            // (top-level CA first, requested CA last).
            ArrayUtils.reverse(chain);

            // Construct the filename based on requested CA. Fail-back to
            // name "ca-chain.EXT".
            String filename = RequestHelper.getFileNameFromCertNoEnding(chain[chain.length - 1], "ca") + "-chain."
                    + format.toLowerCase();

            byte[] outbytes = new byte[0];
            // Encode and send back
            if ((format == null) || StringUtils.equalsIgnoreCase(format, "pem")) {
                outbytes = CertTools.getPemFromCertificateChain(Arrays.asList(chain));
            } else {
                // Create a JKS truststore with the CA certificates in
                final KeyStore store = KeyStore.getInstance("JKS");
                store.load(null, null);
                for (int i = 0; i < chain.length; i++) {
                    String cadn = CertTools.getSubjectDN(chain[i]);
                    String alias = CertTools.getPartFromDN(cadn, "CN");
                    if (alias == null) {
                        alias = CertTools.getPartFromDN(cadn, "O");
                    }
                    if (alias == null) {
                        alias = "cacert" + i;
                    }
                    alias = StringUtils.replaceChars(alias, ' ', '_');
                    alias = StringUtils.substring(alias, 0, 15);
                    store.setCertificateEntry(alias, chain[i]);
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    store.store(out, "changeit".toCharArray());
                    out.close();
                    outbytes = out.toByteArray();
                }
            }
            // We must remove cache headers for IE
            ServletUtils.removeCacheHeaders(res);
            res.setHeader("Content-disposition",
                    "attachment; filename=\"" + StringTools.stripFilename(filename) + "\"");
            res.setContentType("application/octet-stream");
            res.setContentLength(outbytes.length);
            res.getOutputStream().write(outbytes);
            log.debug("Sent CA certificate chain to client, len=" + outbytes.length + ".");
        } catch (CertificateEncodingException e) {
            log.debug("Error getting CA certificate chain: ", e);
            res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error getting CA certificate chain.");
        } catch (KeyStoreException e) {
            log.debug("Error creating JKS with CA certificate chain: ", e);
            res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error creating JKS with CA certificate chain.");
        } catch (NoSuchAlgorithmException e) {
            log.debug("Error creating JKS with CA certificate chain: ", e);
            res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error creating JKS with CA certificate chain.");
        } catch (CertificateException e) {
            log.debug("Error creating JKS with CA certificate chain: ", e);
            res.sendError(HttpServletResponse.SC_NOT_FOUND, "Error creating JKS with CA certificate chain.");
        } catch (EJBException e) {
            log.debug("CA does not exist: ", e);
            res.sendError(HttpServletResponse.SC_NOT_FOUND,
                    "CA does not exist: " + HTMLTools.htmlescape(e.getMessage()));
        } catch (AuthorizationDeniedException e) {
            log.debug("Authotization denied: ", e);
            res.sendError(HttpServletResponse.SC_UNAUTHORIZED,
                    "Authorization denied: " + HTMLTools.htmlescape(e.getMessage()));
        }
    }

    private void printHtmlHeader(String title, PrintWriter pout) {
        pout.println("<html><head>");
        pout.println("<title>" + title + "</title>");
        pout.println("<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">");
        pout.println("<META HTTP-EQUIV=\"Expires\" CONTENT=\"-1\">");
        pout.println("</head>");
        pout.println("<body><p>");
    }

    private void printHtmlFooter(PrintWriter pout) {
        pout.println("</body>");
        pout.println("<head>");
        pout.println("<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">");
        pout.println("<META HTTP-EQUIV=\"Expires\" CONTENT=\"-1\">");
        pout.println("</head>");
        pout.println("</html>");
    }

}