org.ejbca.core.protocol.cmp.CmpMessageDispatcherSessionBean.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.protocol.cmp.CmpMessageDispatcherSessionBean.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.core.protocol.cmp;

import java.io.IOException;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.cmp.PKIBody;
import org.bouncycastle.asn1.cmp.PKIHeader;
import org.bouncycastle.asn1.cmp.PKIMessage;
import org.bouncycastle.asn1.cmp.PKIMessages;
import org.cesecore.authentication.tokens.AuthenticationToken;
import org.cesecore.authorization.control.AccessControlSessionLocal;
import org.cesecore.certificates.ca.CaSessionLocal;
import org.cesecore.certificates.certificate.CertificateStoreSessionLocal;
import org.cesecore.certificates.certificate.request.FailInfo;
import org.cesecore.certificates.certificate.request.ResponseMessage;
import org.cesecore.certificates.certificate.request.ResponseStatus;
import org.cesecore.certificates.certificateprofile.CertificateProfileSessionLocal;
import org.cesecore.configuration.GlobalConfigurationSessionLocal;
import org.cesecore.jndi.JndiConstants;
import org.cesecore.keys.token.CryptoTokenSessionLocal;
import org.cesecore.util.CryptoProviderTools;
import org.ejbca.config.CmpConfiguration;
import org.ejbca.core.ejb.authentication.web.WebAuthenticationProviderSessionLocal;
import org.ejbca.core.ejb.ca.sign.SignSessionLocal;
import org.ejbca.core.ejb.ra.CertificateRequestSessionLocal;
import org.ejbca.core.ejb.ra.EndEntityAccessSessionLocal;
import org.ejbca.core.ejb.ra.EndEntityManagementSessionLocal;
import org.ejbca.core.ejb.ra.raadmin.EndEntityProfileSessionLocal;
import org.ejbca.core.model.InternalEjbcaResources;

/**
 * Class that receives a CMP message and passes it on to the correct message handler.
 * 
 * ----- 
 * This processes does the following: 
 * 1. receive a CMP message 
 * 2. check which message type it is 
 * 3. dispatch to the correct message handler 
 * 4. send back the response received from the handler 
 * -----
 * 
 * Messages supported:
 * - Initialization Request - will return an Initialization Response
 * - Revocation Request - will return a Revocation Response
 * - PKI Confirmation - same as certificate confirmation accept - will return a PKIConfirm
 * - Certificate Confirmation - accept or reject by client - will return a PKIConfirm
 * 
 * @version $Id: CmpMessageDispatcherSessionBean.java 20921 2015-03-18 15:27:27Z mikekushner $
 */
@Stateless(mappedName = JndiConstants.APP_JNDI_PREFIX + "CmpMessageDispatcherSessionRemote")
public class CmpMessageDispatcherSessionBean
        implements CmpMessageDispatcherSessionLocal, CmpMessageDispatcherSessionRemote {

    private static final Logger log = Logger.getLogger(CmpMessageDispatcherSessionBean.class);
    /** Internal localization of logs and errors */
    private static final InternalEjbcaResources intres = InternalEjbcaResources.getInstance();

    @EJB
    private SignSessionLocal signSession;
    @EJB
    private EndEntityManagementSessionLocal endEntityManagementSession;
    @EJB
    private CaSessionLocal caSession;
    @EJB
    private EndEntityAccessSessionLocal endEntityAccessSession;
    @EJB
    private EndEntityProfileSessionLocal endEntityProfileSession;
    @EJB
    private CertificateProfileSessionLocal certificateProfileSession;
    @EJB
    private CertificateRequestSessionLocal certificateRequestSession;
    @EJB
    private CertificateStoreSessionLocal certificateStoreSession;
    @EJB
    private AccessControlSessionLocal authSession;
    @EJB
    private WebAuthenticationProviderSessionLocal authenticationProviderSession;
    @EJB
    private CryptoTokenSessionLocal cryptoTokenSession;
    @EJB
    private GlobalConfigurationSessionLocal globalConfigSession;

    private CmpConfiguration cmpConfiguration;

    @PostConstruct
    public void postConstruct() {
        CryptoProviderTools.installBCProviderIfNotAvailable(); // Install BouncyCastle provider, if not already available
        this.cmpConfiguration = (CmpConfiguration) this.globalConfigSession
                .getCachedConfiguration(CmpConfiguration.CMP_CONFIGURATION_ID);
    }

    /** The message may have been received by any transport protocol, and is passed here in it's binary ASN.1 form.
     * 
     * @param message der encoded CMP message as a byte array
     * @return IResponseMessage containing the CMP response message or null if there is no message to send back or some internal error has occurred
     * @throws IOException 
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public ResponseMessage dispatch(final AuthenticationToken admin, final byte[] ba, String confAlias)
            throws IOException {
        //ASN1Primitive derObject = new LimitLengthASN1Reader(new ByteArrayInputStream(ba), ba.length).readObject();
        final ASN1Primitive derObject = getDERObject(ba);
        return dispatch(admin, derObject, false, confAlias);
    }

    /** The message may have been received by any transport protocol, and is passed here in it's binary ASN.1 form.
     * 
     * @param message der encoded CMP message
     * @return IResponseMessage containing the CMP response message or null if there is no message to send back or some internal error has occurred
     */
    private ResponseMessage dispatch(final AuthenticationToken admin, final ASN1Primitive derObject,
            final boolean authenticated, String confAlias) {

        this.cmpConfiguration = (CmpConfiguration) this.globalConfigSession
                .getCachedConfiguration(CmpConfiguration.CMP_CONFIGURATION_ID);

        if (!cmpConfiguration.aliasExists(confAlias)) {
            log.info("There is no CMP alias: " + confAlias);
            return CmpMessageHelper.createUnprotectedErrorMessage(null, ResponseStatus.FAILURE,
                    FailInfo.INCORRECT_DATA, "Wrong URL. CMP alias '" + confAlias + "' does not exist");
        }

        final PKIMessage req;
        try {
            req = PKIMessage.getInstance(derObject);
            if (req == null) {
                throw new Exception("No CMP message could be parsed from received Der object.");
            }
        } catch (Throwable t) { // NOPMD: catch all to report errors back to client
            final String eMsg = intres.getLocalizedMessage("cmp.errornotcmpmessage");
            log.error(eMsg, t);
            // If we could not read the message, we should return an error BAD_REQUEST
            return CmpMessageHelper.createUnprotectedErrorMessage(null, ResponseStatus.FAILURE,
                    FailInfo.BAD_REQUEST, eMsg);
        }
        try {
            final PKIBody body = req.getBody();
            final int tagno = body.getType();
            if (log.isDebugEnabled()) {
                final PKIHeader header = req.getHeader();
                log.debug("Received CMP message with pvno=" + header.getPvno() + ", sender="
                        + header.getSender().toString() + ", recipient=" + header.getRecipient().toString());
                log.debug("Cmp configuration alias: " + confAlias);
                log.debug("The CMP message is already authenticated: " + authenticated);
                log.debug("Body is of type: " + tagno);
                log.debug("Transaction id: " + header.getTransactionID());
                //log.debug(ASN1Dump.dumpAsString(req));
            }

            BaseCmpMessage cmpMessage = null;
            ICmpMessageHandler handler = null;
            int unknownMessageType = -1;
            switch (tagno) {
            case 0:
                // 0 (ir, Initialization Request) and 2 (cr, Certification Req) are both certificate requests
                handler = new CrmfMessageHandler(admin, confAlias, caSession, certificateProfileSession,
                        certificateRequestSession, endEntityAccessSession, endEntityProfileSession, signSession,
                        certificateStoreSession, authSession, authenticationProviderSession,
                        endEntityManagementSession, globalConfigSession);
                cmpMessage = new CrmfRequestMessage(req, this.cmpConfiguration.getCMPDefaultCA(confAlias),
                        this.cmpConfiguration.getAllowRAVerifyPOPO(confAlias),
                        this.cmpConfiguration.getExtractUsernameComponent(confAlias));
                break;
            case 2:
                handler = new CrmfMessageHandler(admin, confAlias, caSession, certificateProfileSession,
                        certificateRequestSession, endEntityAccessSession, endEntityProfileSession, signSession,
                        certificateStoreSession, authSession, authenticationProviderSession,
                        endEntityManagementSession, globalConfigSession);
                cmpMessage = new CrmfRequestMessage(req, this.cmpConfiguration.getCMPDefaultCA(confAlias),
                        this.cmpConfiguration.getAllowRAVerifyPOPO(confAlias),
                        this.cmpConfiguration.getExtractUsernameComponent(confAlias));
                break;
            case 7:
                // Key Update request (kur, Key Update Request)
                handler = new CrmfKeyUpdateHandler(admin, confAlias, caSession, certificateProfileSession,
                        endEntityAccessSession, endEntityProfileSession, signSession, certificateStoreSession,
                        authSession, authenticationProviderSession, endEntityManagementSession,
                        globalConfigSession);
                cmpMessage = new CrmfRequestMessage(req, this.cmpConfiguration.getCMPDefaultCA(confAlias),
                        this.cmpConfiguration.getAllowRAVerifyPOPO(confAlias),
                        this.cmpConfiguration.getExtractUsernameComponent(confAlias));
                break;
            case 19:
                // PKI confirm (pkiconf, Confirmation)
            case 24:
                // Certificate confirmation (certConf, Certificate confirm)
                handler = new ConfirmationMessageHandler(admin, confAlias, caSession, endEntityProfileSession,
                        certificateProfileSession, authSession, authenticationProviderSession, cryptoTokenSession,
                        globalConfigSession);
                cmpMessage = new GeneralCmpMessage(req);
                break;
            case 11:
                // Revocation request (rr, Revocation Request)
                handler = new RevocationMessageHandler(admin, confAlias, endEntityManagementSession, caSession,
                        endEntityProfileSession, certificateProfileSession, certificateStoreSession, authSession,
                        endEntityAccessSession, authenticationProviderSession, cryptoTokenSession,
                        globalConfigSession);
                cmpMessage = new GeneralCmpMessage(req);
                break;
            case 20:
                // NestedMessageContent (nested)
                if (log.isDebugEnabled()) {
                    log.debug("Received a NestedMessageContent");
                }

                final NestedMessageContent nestedMessage = new NestedMessageContent(req, confAlias,
                        globalConfigSession);
                if (nestedMessage.verify()) {
                    if (log.isDebugEnabled()) {
                        log.debug("The NestedMessageContent was verified successfully");
                    }
                    try {
                        PKIMessages nestesMessages = (PKIMessages) nestedMessage.getPKIMessage().getBody()
                                .getContent();
                        PKIMessage msg = nestesMessages.toPKIMessageArray()[0];
                        return dispatch(admin, msg.toASN1Primitive(), true, confAlias);
                    } catch (IllegalArgumentException e) {
                        final String errMsg = e.getLocalizedMessage();
                        log.info(errMsg, e);
                        cmpMessage = new NestedMessageContent(req, confAlias, globalConfigSession);
                        return CmpMessageHelper.createUnprotectedErrorMessage(cmpMessage, ResponseStatus.FAILURE,
                                FailInfo.BAD_REQUEST, errMsg);
                    }
                } else {
                    final String errMsg = "Could not verify the RA, signature verification on NestedMessageContent failed.";
                    log.info(errMsg);
                    cmpMessage = new NestedMessageContent(req, confAlias, globalConfigSession);
                    return CmpMessageHelper.createUnprotectedErrorMessage(cmpMessage, ResponseStatus.FAILURE,
                            FailInfo.BAD_REQUEST, errMsg);
                }

            default:
                unknownMessageType = tagno;
                log.info("Received an unknown message type, tagno=" + tagno);
                break;
            }
            if (handler == null || cmpMessage == null) {
                if (unknownMessageType > -1) {
                    final String eMsg = intres.getLocalizedMessage("cmp.errortypenohandle",
                            Integer.valueOf(unknownMessageType));
                    log.error(eMsg);
                    return CmpMessageHelper.createUnprotectedErrorMessage(null, ResponseStatus.FAILURE,
                            FailInfo.BAD_REQUEST, eMsg);
                }
                throw new Exception("Something is null! Handler=" + handler + ", cmpMessage=" + cmpMessage);
            }
            final ResponseMessage ret = handler.handleMessage(cmpMessage, authenticated);
            if (ret != null) {
                log.debug("Received a response message of type '" + ret.getClass().getName()
                        + "' from CmpMessageHandler.");
            } else {
                log.error(intres.getLocalizedMessage("cmp.errorresponsenull"));
            }
            return ret;
        } catch (Exception e) {
            log.error(intres.getLocalizedMessage("cmp.errorprocess"), e);
            return null;
        }
    }

    private ASN1Primitive getDERObject(byte[] ba) throws IOException {
        ASN1InputStream ins = new ASN1InputStream(ba);
        try {
            ASN1Primitive obj = ins.readObject();
            return obj;
        } finally {
            ins.close();
        }
    }
}