mitm.application.djigzo.ws.impl.CAWSImpl.java Source code

Java tutorial

Introduction

Here is the source code for mitm.application.djigzo.ws.impl.CAWSImpl.java

Source

/*
 * Copyright (c) 2009-2012, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo 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 Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.application.djigzo.ws.impl;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.KeyStoreException;
import java.security.cert.CertStoreException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.security.auth.x500.X500Principal;

import mitm.application.djigzo.GlobalPreferencesManager;
import mitm.application.djigzo.TemplateProperties;
import mitm.application.djigzo.User;
import mitm.application.djigzo.ca.PFXMailBuilder;
import mitm.application.djigzo.ca.PFXSMSBuilder;
import mitm.application.djigzo.workflow.KeyAndCertificateWorkflow;
import mitm.application.djigzo.workflow.UserWorkflow;
import mitm.application.djigzo.workflow.UserWorkflow.GetUserMode;
import mitm.application.djigzo.ws.CABuilderParametersDTO;
import mitm.application.djigzo.ws.CAWS;
import mitm.application.djigzo.ws.CRLCreateParametersDTO;
import mitm.application.djigzo.ws.CertificateRequestHandlerDTO;
import mitm.application.djigzo.ws.RequestParametersDTO;
import mitm.application.djigzo.ws.RequestParametersDTOUtils;
import mitm.application.djigzo.ws.SendCertificatesDTO;
import mitm.application.djigzo.ws.X500PrincipalDTOUtils;
import mitm.application.djigzo.ws.X509CRLDTO;
import mitm.application.djigzo.ws.X509CRLDTOBuilder;
import mitm.application.djigzo.ws.X509CertificateDTO;
import mitm.application.djigzo.ws.X509CertificateDTOBuilder;
import mitm.common.hibernate.annotations.StartTransaction;
import mitm.common.mail.EmailAddressUtils;
import mitm.common.mail.MailTransport;
import mitm.common.properties.HierarchicalPropertiesException;
import mitm.common.security.KeyAndCertStore;
import mitm.common.security.KeyAndCertificate;
import mitm.common.security.SecurityFactoryFactory;
import mitm.common.security.ca.CA;
import mitm.common.security.ca.CABuilder;
import mitm.common.security.ca.CABuilderParameters;
import mitm.common.security.ca.CABuilderParametersImpl;
import mitm.common.security.ca.CABuilderResult;
import mitm.common.security.ca.CAException;
import mitm.common.security.ca.CASettings;
import mitm.common.security.ca.CASettingsProvider;
import mitm.common.security.ca.CertificateRequestHandler;
import mitm.common.security.ca.CertificateRequestHandlerRegistry;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.security.certstore.X509CertStoreEntry;
import mitm.common.security.crl.CRLStoreMaintainer;
import mitm.common.security.crl.X509CRLBuilder;
import mitm.common.security.crl.X509CRLBuilderHelper;
import mitm.common.security.crlstore.X509CRLStoreExt;
import mitm.common.sms.SMS;
import mitm.common.sms.SMSGateway;
import mitm.common.sms.hibernate.SMSImpl;
import mitm.common.template.TemplateBuilder;
import mitm.common.util.Check;
import mitm.common.util.MiscStringUtils;
import mitm.common.ws.NotAllowedWebServiceCheckedException;
import mitm.common.ws.WebServiceCheckedException;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CAWSImpl implements CAWS {
    private final static Logger logger = LoggerFactory.getLogger(CAWSImpl.class);

    /*
     * Used for the creation of a new CA
     */
    private final CABuilder caBuilder;

    /*
     * Used for issuing new certificates
     */
    private final CA ca;

    /*
     * Used to store the newly created root key and certificate
     */
    private final KeyAndCertStore rootKeyAndCertStore;

    /*
     * Used to store the newly created intermediate key and certificate
     */
    private final KeyAndCertStore intermediateKeyAndCertStore;

    /*
     * Used for storing and retrieving CRLs
     */
    private final X509CRLStoreExt crlStore;

    /*
     * Used for adding the new CRL to the CRL store
     */
    private final CRLStoreMaintainer crlStoreMaintainer;

    /*
     * Used for getting the CA settings
     */
    private final CASettingsProvider caSettingsProvider;

    /*
     * Used for getting a PFX
     */
    private final KeyAndCertificateWorkflow keyAndCertificateWorkflow;

    /*
     * Used for sending the PFX via email
     */
    private final MailTransport mailTransport;

    /*
     * Used to get some of the global templates
     */
    private final GlobalPreferencesManager globalPreferencesManager;

    /*
     * Used for sending SMS
     */
    private final SMSGateway smsGateway;

    /*
     * Used for retrieving all registered CertificateRequestHandler's 
     */
    private final CertificateRequestHandlerRegistry certificateRequestHandlerRegistry;

    /*
     * UserWorkflow is used for getting and creating users
     */
    private final UserWorkflow userWorkflow;

    /*
     * for building and processing Freemarker templates.
     */
    private final TemplateBuilder templateBuilder;

    /*
     * For building X509 Certificate DTOs
     */
    private final X509CertificateDTOBuilder certificateDTOBuilder;

    /*
     * If true the root private key will also be stored and not just the certificate
     */
    private final boolean storeRootPrivateKey;

    /*
     * The number of phone number digits we will anonymized
     */
    int anonymizedDigits;

    public CAWSImpl(CABuilder caBuilder, CA ca, KeyAndCertStore rootKeyAndCertStore,
            KeyAndCertStore intermediateKeyAndCertStore, X509CRLStoreExt crlStore,
            CRLStoreMaintainer crlStoreMaintainer, CASettingsProvider caSettingsProvider,
            KeyAndCertificateWorkflow keyAndCertificateWorkflow, MailTransport mailTransport, SMSGateway smsGateway,
            GlobalPreferencesManager globalPreferencesManager,
            CertificateRequestHandlerRegistry certificateRequestHandlerRegistry, UserWorkflow userWorkflow,
            TemplateBuilder templateBuilder, X509CertificateDTOBuilder certificateDTOBuilder,
            boolean storeRootPrivateKey, int anonymizedDigits) {
        Check.notNull(caBuilder, "caBuilder");
        Check.notNull(ca, "ca");
        Check.notNull(rootKeyAndCertStore, "rootKeyAndCertStore");
        Check.notNull(intermediateKeyAndCertStore, "intermediateKeyAndCertStore");
        Check.notNull(crlStore, "crlStore");
        Check.notNull(crlStoreMaintainer, "crlStoreMaintainer");
        Check.notNull(caSettingsProvider, "caSettingsProvider");
        Check.notNull(keyAndCertificateWorkflow, "keyAndCertificateWorkflow");
        Check.notNull(mailTransport, "mailTransport");
        Check.notNull(smsGateway, "smsGateway");
        Check.notNull(globalPreferencesManager, "globalPreferencesManager");
        Check.notNull(certificateRequestHandlerRegistry, "certificateRequestHandlerRegistry");
        Check.notNull(userWorkflow, "userWorkflow");
        Check.notNull(templateBuilder, "templateBuilder");
        Check.notNull(certificateDTOBuilder, "certificateDTOBuilder");

        this.caBuilder = caBuilder;
        this.ca = ca;
        this.rootKeyAndCertStore = rootKeyAndCertStore;
        this.intermediateKeyAndCertStore = intermediateKeyAndCertStore;
        this.crlStore = crlStore;
        this.crlStoreMaintainer = crlStoreMaintainer;
        this.caSettingsProvider = caSettingsProvider;
        this.keyAndCertificateWorkflow = keyAndCertificateWorkflow;
        this.mailTransport = mailTransport;
        this.smsGateway = smsGateway;
        this.globalPreferencesManager = globalPreferencesManager;
        this.certificateRequestHandlerRegistry = certificateRequestHandlerRegistry;
        this.userWorkflow = userWorkflow;
        this.templateBuilder = templateBuilder;
        this.certificateDTOBuilder = certificateDTOBuilder;
        this.storeRootPrivateKey = storeRootPrivateKey;
        this.anonymizedDigits = anonymizedDigits;
    }

    private boolean isBlank(Collection<String> list) {
        if (list == null) {
            return true;
        }

        for (String s : list) {
            if (StringUtils.isNotBlank(s)) {
                return false;
            }
        }

        return true;
    }

    @Override
    @StartTransaction
    public String buildCA(CABuilderParametersDTO parametersDTO) throws WebServiceCheckedException {
        try {
            if (parametersDTO == null) {
                throw new WebServiceCheckedException("parameters must not be empty.");
            }

            if (parametersDTO.getRootSubject() == null) {
                throw new WebServiceCheckedException("root subject must not be empty.");
            }

            if (parametersDTO.getIntermediateSubject() == null) {
                throw new WebServiceCheckedException("intermediate subject must not be empty.");
            }

            if (isBlank(parametersDTO.getRootSubject().getCommonName())) {
                throw new WebServiceCheckedException("Root common name must be set");
            }

            if (isBlank(parametersDTO.getIntermediateSubject().getCommonName())) {
                throw new WebServiceCheckedException("Intermediate common name must be set");
            }

            X500Principal rootSubject = X500PrincipalDTOUtils.toX500Principal(parametersDTO.getRootSubject());

            X500Principal intermediateSubject = X500PrincipalDTOUtils
                    .toX500Principal(parametersDTO.getIntermediateSubject());

            CABuilderParameters builderParameters = new CABuilderParametersImpl();

            builderParameters.setRootSubject(rootSubject);
            builderParameters.setIntermediateSubject(intermediateSubject);
            builderParameters.setRootKeyLength(parametersDTO.getRootKeyLength());
            builderParameters.setIntermediateKeyLength(parametersDTO.getIntermediateKeyLength());
            builderParameters.setRootValidity(parametersDTO.getRootValidity());
            builderParameters.setIntermediateValidity(parametersDTO.getIntermediateValidity());
            builderParameters.setCRLDistributionPoints(parametersDTO.getCRLDistributionPoints());
            builderParameters.setSignatureAlgorithm(parametersDTO.getSignatureAlgorithm());

            CABuilderResult result = caBuilder.buildCA(builderParameters);

            if (storeRootPrivateKey) {
                rootKeyAndCertStore.addKeyAndCertificate(result.getRoot());
            } else {
                rootKeyAndCertStore.addCertificate(result.getRoot().getCertificate());
            }

            intermediateKeyAndCertStore.addKeyAndCertificate(result.getIntermediate());

            return X509CertificateInspector.getThumbprint(result.getIntermediate().getCertificate());
        } catch (Exception e) {
            logger.error("buildCA failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    @Override
    @StartTransaction
    public X509CertificateDTO getActiveCA() throws WebServiceCheckedException {
        try {
            X509CertificateDTO certificateDTO = null;

            CASettings caSettings = caSettingsProvider.getSettings();

            X509CertStoreEntry entry = intermediateKeyAndCertStore
                    .getByThumbprint(caSettings.getSignerThumbprint());

            if (entry != null) {
                certificateDTO = certificateDTOBuilder.buildCertificateDTO(entry.getCertificate(),
                        entry.getKeyAlias());
            }

            return certificateDTO;
        } catch (Exception e) {
            logger.error("getActiveCA failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    @Override
    @StartTransaction
    public X509CertificateDTO setActiveCA(String thumbprint) throws WebServiceCheckedException {
        try {
            if (StringUtils.isBlank(thumbprint)) {
                throw new WebServiceCheckedException("Thumbprint is missing.");
            }

            thumbprint = thumbprint.trim();

            X509CertificateDTO certificateDTO = null;

            X509CertStoreEntry entry = intermediateKeyAndCertStore.getByThumbprint(thumbprint);

            if (entry != null) {
                CASettings caSettings = caSettingsProvider.getSettings();

                caSettings.setSignerThumbprint(thumbprint);

                certificateDTO = certificateDTOBuilder.buildCertificateDTO(entry.getCertificate(),
                        entry.getKeyAlias());
            } else {
                logger.warn("Certificate with thumbprint " + thumbprint + " not found.");
            }

            return certificateDTO;
        } catch (Exception e) {
            logger.error("getActiveCA failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    /*
     * With X509Certificates returned from user we do not know what the key alias is because
     * it's not a CertStore object. We therefore will lookup the key alias.
     */
    private String getKeyAlias(X509Certificate certificate) throws WebServiceCheckedException {
        try {
            if (certificate == null) {
                return null;
            }

            X509CertStoreEntry entry = intermediateKeyAndCertStore.getByCertificate(certificate);

            return entry != null ? entry.getKeyAlias() : null;
        } catch (CertStoreException e) {
            throw new WebServiceCheckedException(e);
        }
    }

    private X509CertificateDTO requestCertificateInternal(RequestParametersDTO requestDTO, boolean addUser,
            boolean skipIfAvailable) throws WebServiceCheckedException, CAException, IOException, AddressException,
            HierarchicalPropertiesException, CertStoreException, KeyStoreException {
        if (requestDTO == null) {
            throw new WebServiceCheckedException("request is missing");
        }

        if (requestDTO.getSubject() == null) {
            throw new WebServiceCheckedException("subject must be specified");
        }

        if (StringUtils.isBlank(requestDTO.getEmail())) {
            throw new WebServiceCheckedException("email must be specified");
        }

        String email = EmailAddressUtils.canonicalizeAndValidate(requestDTO.getEmail(), true);

        if (email == null) {
            throw new WebServiceCheckedException(requestDTO.getEmail() + " is not a valid email address.");
        }

        logger.debug("handling request for user " + email);

        User user = userWorkflow.getUser(email, GetUserMode.CREATE_IF_NOT_EXIST);

        KeyAndCertificate issued = null;

        if (!skipIfAvailable || user.getSigningKeyAndCertificate() == null) {
            issued = ca.requestCertificate(RequestParametersDTOUtils.toRequestParameters(requestDTO));
        }

        if (addUser) {
            userWorkflow.makePersistent(user);
        }

        /*
         * issued can be null when the certificate issuer does not immediately issue a certificate (the certificate
         * request will be stored in the request store and handled asynchronous)
         */
        return issued != null
                ? certificateDTOBuilder.buildKeyAndCertificateDTO(issued, getKeyAlias(issued.getCertificate()))
                : null;
    }

    @Override
    @StartTransaction
    public X509CertificateDTO requestCertificate(RequestParametersDTO requestDTO, boolean addUser,
            boolean skipIfAvailable) throws WebServiceCheckedException {
        try {
            return requestCertificateInternal(requestDTO, addUser, skipIfAvailable);
        } catch (Exception e) {
            logger.error("requestCertificate failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    @Override
    @StartTransaction
    public void requestCertificates(List<RequestParametersDTO> requests, boolean addUser, boolean skipIfAvailable)
            throws WebServiceCheckedException {
        try {
            if (requests == null) {
                throw new WebServiceCheckedException("requests is missing");
            }

            for (RequestParametersDTO request : requests) {
                requestCertificateInternal(request, addUser, skipIfAvailable);
            }
        } catch (Exception e) {
            logger.error("requestCertificates failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    @Override
    @StartTransaction
    public X509CRLDTO createCRL(CRLCreateParametersDTO parameters) throws WebServiceCheckedException {
        try {
            if (parameters == null) {
                throw new WebServiceCheckedException("parameters must be specified.");
            }

            if (StringUtils.isEmpty(parameters.getIssuerThumbprint())) {
                throw new WebServiceCheckedException("issuerThumbprint must be specified.");
            }

            if (StringUtils.isEmpty(parameters.getSignatureAlgorithm())) {
                throw new WebServiceCheckedException("signatureAlgorithm must be specified.");
            }

            KeyAndCertificate issuer = null;

            X509CertStoreEntry caEntry = intermediateKeyAndCertStore
                    .getByThumbprint(parameters.getIssuerThumbprint());

            if (caEntry != null) {
                issuer = intermediateKeyAndCertStore.getKeyAndCertificate(caEntry);
            }

            if (issuer == null || issuer.getPrivateKey() == null) {
                throw new WebServiceCheckedException("Issuer with thumbprint " + parameters.getIssuerThumbprint()
                        + " not found or no private key available");
            }

            X509CRLBuilder crlBuilder = SecurityFactoryFactory.getSecurityFactory().createX509CRLBuilder();

            crlBuilder.setSignatureAlgorithm(parameters.getSignatureAlgorithm());
            crlBuilder.setThisUpdate(new Date());
            crlBuilder.setNextUpdate(parameters.getNextUpdate());

            X509CRLBuilderHelper builderHelper = new X509CRLBuilderHelper(crlStore, crlBuilder);

            builderHelper.addEntries(parameters.getRevokedSerials());

            X509CRL crl = builderHelper.generateCRL(issuer, parameters.isUpdateExistingCRL());

            X509CRLDTO crlDTO = null;

            if (crl != null) {
                crlDTO = new X509CRLDTOBuilder(crl);

                /*
                 * Add the new CRL to the CRL store using the crlStoreMaintainer
                 * (replaces any existing CRL with the new CRL)
                 */
                crlStoreMaintainer.addCRL(crl);
            }

            return crlDTO;
        } catch (Exception e) {
            logger.error("createCRL failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    private List<X509Certificate> getCertificates(List<String> thumbprints) throws CertStoreException {
        List<X509Certificate> certificates = new LinkedList<X509Certificate>();

        for (String thumbprint : thumbprints) {
            X509CertStoreEntry entry = intermediateKeyAndCertStore.getByThumbprint(thumbprint);

            if (entry != null) {
                certificates.add(entry.getCertificate());
            }
        }

        return certificates;
    }

    /*
     * Creates a ID used to identify the PFX. The ID is placed in the mail and SMS
     */
    private String createPFXId() {
        return Long.toString(System.currentTimeMillis() % 10000);
    }

    @Override
    @StartTransaction
    public void sendCertificates(SendCertificatesDTO parameters) throws WebServiceCheckedException {
        try {
            if (parameters == null) {
                throw new WebServiceCheckedException("parameters is missing");
            }

            if (parameters.getThumbprints() == null || parameters.getThumbprints().size() == 0) {
                throw new WebServiceCheckedException("Thumbprints are missing");
            }

            if (StringUtils.isBlank(parameters.getPassword())) {
                throw new WebServiceCheckedException("password is missing");
            }

            if (StringUtils.isBlank(parameters.getSender())) {
                throw new WebServiceCheckedException("Sender is missing");
            }

            if (parameters.isSendSMS() && StringUtils.isBlank(parameters.getPhoneNumber())) {
                throw new WebServiceCheckedException("Phone number is missing");
            }

            if (StringUtils.isBlank(parameters.getRecipient())) {
                throw new WebServiceCheckedException("Recipient is missing");
            }

            String normalizedRecipient = EmailAddressUtils.canonicalizeAndValidate(parameters.getRecipient(), true);

            if (normalizedRecipient == null) {
                throw new WebServiceCheckedException("Recipient is not a valid email address.");
            }

            InternetAddress recipient = new InternetAddress(normalizedRecipient);

            List<X509Certificate> certificates = getCertificates(parameters.getThumbprints());

            if (certificates.size() == 0) {
                throw new WebServiceCheckedException("No certificates found.");
            }

            for (X509Certificate certificate : certificates) {
                if (!parameters.isAllowCA()) {
                    if (X509CertificateInspector.isCA(certificate)) {
                        throw new NotAllowedWebServiceCheckedException("Sending a CA certificate is not allowed.");
                    }
                }

                if (!parameters.isAllowEmailMismatch()) {
                    X509CertificateInspector inspector = new X509CertificateInspector(certificate);

                    if (!EmailAddressUtils.containsEmail(normalizedRecipient, inspector.getEmail())) {
                        throw new NotAllowedWebServiceCheckedException(
                                "Recipient email address does not match " + "certificate(s) email address(es).");
                    }
                }
            }

            ByteArrayOutputStream pfxStream = new ByteArrayOutputStream();

            keyAndCertificateWorkflow.getPFX(certificates, parameters.getPassword().toCharArray(), pfxStream);

            TemplateProperties templateProperties = globalPreferencesManager.getGlobalUserPreferences()
                    .getProperties().getTemplateProperties();

            PFXMailBuilder mailBuilder = new PFXMailBuilder(templateProperties.getMailPFXTemplate(),
                    templateBuilder);

            String phoneNumber = StringUtils.defaultString(parameters.getPhoneNumber());
            String anonymizedPhoneNumber = MiscStringUtils.replaceLastChars(phoneNumber, anonymizedDigits, "*");
            String pfxID = createPFXId();

            mailBuilder.setFrom(new InternetAddress(parameters.getSender()));
            mailBuilder.setRecipient(recipient);

            /*
             * Set additional properties which can be used in the message template
             */
            mailBuilder.addProperty("password", parameters.getPassword());
            mailBuilder.addProperty("sendSMS", parameters.isSendSMS());
            mailBuilder.addProperty("phoneNumber", phoneNumber);
            mailBuilder.addProperty("phoneNumberAnonymized", anonymizedPhoneNumber);
            mailBuilder.addProperty("id", pfxID);

            mailBuilder.setPFX(pfxStream.toByteArray());

            MimeMessage message = mailBuilder.createMessage();

            mailTransport.sendMessage(message, recipient);

            if (parameters.isSendSMS()) {
                PFXSMSBuilder smsBuilder = new PFXSMSBuilder(templateProperties.getSMSPFXPasswordTemplate(),
                        templateBuilder);

                smsBuilder.setPassword(parameters.getPassword());
                smsBuilder.addProperty("id", pfxID);

                SMS sms = new SMSImpl(parameters.getPhoneNumber(), smsBuilder.createSMS(), null);

                smsGateway.sendSMS(sms);
            }
        } catch (NotAllowedWebServiceCheckedException e) {
            logger.warn("Action now allowed. Message: " + e.getMessage());

            throw e;
        } catch (Exception e) {
            logger.error("sendCertificates failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }

    @Override
    @StartTransaction
    public List<CertificateRequestHandlerDTO> getCertificateRequestHandlers() throws WebServiceCheckedException {
        try {
            List<CertificateRequestHandler> handlers = certificateRequestHandlerRegistry.getHandlers();

            List<CertificateRequestHandlerDTO> handlersDTO = new LinkedList<CertificateRequestHandlerDTO>();

            for (CertificateRequestHandler handler : handlers) {
                handlersDTO.add(new CertificateRequestHandlerDTO(handler.getCertificateHandlerName(),
                        handler.isEnabled(), handler.isInstantlyIssued()));
            }

            return handlersDTO;
        } catch (Exception e) {
            logger.error("getCertificateRequestHandlers failed.", e);

            throw new WebServiceCheckedException(ExceptionUtils.getRootCauseMessage(e));
        }
    }
}