org.ejbca.core.ejb.ra.CertificateRequestSessionBean.java Source code

Java tutorial

Introduction

Here is the source code for org.ejbca.core.ejb.ra.CertificateRequestSessionBean.java

Source

/*************************************************************************
 *                                                                       *
 *  EJBCA: 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.ejb.ra;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.SignatureException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.PersistenceException;

import org.apache.log4j.Logger;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.jce.netscape.NetscapeCertRequest;
import org.cesecore.core.ejb.ra.raadmin.EndEntityProfileSessionLocal;
import org.ejbca.core.EjbcaException;
import org.ejbca.core.ejb.JndiHelper;
import org.ejbca.core.ejb.authorization.AuthorizationSessionLocal;
import org.ejbca.core.ejb.ca.auth.AuthenticationSessionLocal;
import org.ejbca.core.ejb.ca.caadmin.CAAdminSessionLocal;
import org.ejbca.core.ejb.ca.sign.SignSessionLocal;
import org.ejbca.core.ejb.config.GlobalConfigurationSessionLocal;
import org.ejbca.core.ejb.hardtoken.HardTokenSessionLocal;
import org.ejbca.core.ejb.keyrecovery.KeyRecoverySessionLocal;
import org.ejbca.core.model.SecConst;
import org.ejbca.core.model.approval.ApprovalException;
import org.ejbca.core.model.approval.WaitingForApprovalException;
import org.ejbca.core.model.authorization.AccessRulesConstants;
import org.ejbca.core.model.authorization.AuthorizationDeniedException;
import org.ejbca.core.model.authorization.Authorizer;
import org.ejbca.core.model.ca.SignRequestSignatureException;
import org.ejbca.core.model.ca.WrongTokenTypeException;
import org.ejbca.core.model.ca.caadmin.CADoesntExistsException;
import org.ejbca.core.model.log.Admin;
import org.ejbca.core.model.ra.NotFoundException;
import org.ejbca.core.model.ra.UserDataConstants;
import org.ejbca.core.model.ra.UserDataVO;
import org.ejbca.core.model.ra.raadmin.EndEntityProfile;
import org.ejbca.core.model.ra.raadmin.UserDoesntFullfillEndEntityProfile;
import org.ejbca.core.model.util.GenerateToken;
import org.ejbca.core.protocol.IRequestMessage;
import org.ejbca.core.protocol.IResponseMessage;
import org.ejbca.core.protocol.SimpleRequestMessage;
import org.ejbca.util.Base64;
import org.ejbca.util.CertTools;
import org.ejbca.util.FileTools;
import org.ejbca.util.RequestMessageUtils;

import com.novosec.pkix.asn1.crmf.CertRequest;

/**
 * Combines EditUser (RA) with CertReq (CA) methods using transactions.
 *
 * @version $Id: CertificateRequestSessionBean.java 11802 2011-04-23 19:26:26Z netmackan $
 */
@Stateless(mappedName = JndiHelper.APP_JNDI_PREFIX + "CertificateRequestSessionRemote")
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public class CertificateRequestSessionBean
        implements CertificateRequestSessionRemote, CertificateRequestSessionLocal {

    private static final long serialVersionUID = 1L;
    private static final Logger log = Logger.getLogger(CertificateRequestSessionBean.class);

    @EJB
    private AuthenticationSessionLocal authenticationSession;
    @EJB
    private AuthorizationSessionLocal authorizationSession;
    @EJB
    private CAAdminSessionLocal caAdminSession;
    @EJB
    private EndEntityProfileSessionLocal endEntityProfileSession;
    @EJB
    private HardTokenSessionLocal hardTokenSession;
    @EJB
    private KeyRecoverySessionLocal keyRecoverySession;
    @EJB
    private GlobalConfigurationSessionLocal globalConfigurationSession;
    @EJB
    private UserAdminSessionLocal userAdminSession;
    @EJB
    private SignSessionLocal signSession;
    @Resource
    private SessionContext sessionContext;

    @Override
    public byte[] processCertReq(Admin admin, UserDataVO userdata, String req, int reqType, String hardTokenSN,
            int responseType) throws CADoesntExistsException, AuthorizationDeniedException, NotFoundException,
            InvalidKeyException, NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException,
            SignatureException, IOException, ObjectNotFoundException, CertificateException,
            UserDoesntFullfillEndEntityProfile, ApprovalException, EjbcaException {
        byte[] retval = null;

        // Check tokentype
        if (userdata.getTokenType() != SecConst.TOKEN_SOFT_BROWSERGEN) {
            throw new WrongTokenTypeException(
                    "Error: Wrong Token Type of user, must be 'USERGENERATED' for PKCS10/SPKAC/CRMF/CVC requests");
        }
        // This is the secret sauce, do the end entity handling automagically here before we get the cert
        addOrEditUser(admin, userdata, false, true);
        // Process request
        try {
            String password = userdata.getPassword();
            String username = userdata.getUsername();
            IRequestMessage imsg = null;
            if (reqType == SecConst.CERT_REQ_TYPE_PKCS10) {
                IRequestMessage pkcs10req = RequestMessageUtils.genPKCS10RequestMessage(req.getBytes());
                PublicKey pubKey = pkcs10req.getRequestPublicKey();
                imsg = new SimpleRequestMessage(pubKey, username, password);
            } else if (reqType == SecConst.CERT_REQ_TYPE_SPKAC) {
                // parts copied from request helper.
                byte[] reqBytes = req.getBytes();
                if (reqBytes != null) {
                    log.debug("Received NS request: " + new String(reqBytes));
                    byte[] buffer = Base64.decode(reqBytes);
                    if (buffer == null) {
                        return null;
                    }
                    ASN1InputStream in = new ASN1InputStream(new ByteArrayInputStream(buffer));
                    ASN1Sequence spkacSeq = (ASN1Sequence) in.readObject();
                    in.close();
                    NetscapeCertRequest nscr = new NetscapeCertRequest(spkacSeq);
                    // Verify POPO, we don't care about the challenge, it's not important.
                    nscr.setChallenge("challenge");
                    if (nscr.verify("challenge") == false) {
                        log.debug("POPO verification Failed");
                        throw new SignRequestSignatureException(
                                "Invalid signature in NetscapeCertRequest, popo-verification failed.");
                    }
                    log.debug("POPO verification successful");
                    PublicKey pubKey = nscr.getPublicKey();
                    imsg = new SimpleRequestMessage(pubKey, username, password);
                }
            } else if (reqType == SecConst.CERT_REQ_TYPE_CRMF) {
                byte[] request = Base64.decode(req.getBytes());
                ASN1InputStream in = new ASN1InputStream(request);
                ASN1Sequence crmfSeq = (ASN1Sequence) in.readObject();
                ASN1Sequence reqSeq = (ASN1Sequence) ((ASN1Sequence) crmfSeq.getObjectAt(0)).getObjectAt(0);
                CertRequest certReq = new CertRequest(reqSeq);
                SubjectPublicKeyInfo pKeyInfo = certReq.getCertTemplate().getPublicKey();
                KeyFactory keyFact = KeyFactory.getInstance("RSA", "BC");
                KeySpec keySpec = new X509EncodedKeySpec(pKeyInfo.getEncoded());
                PublicKey pubKey = keyFact.generatePublic(keySpec); // just check it's ok
                imsg = new SimpleRequestMessage(pubKey, username, password);
                // a simple crmf is not a complete PKI message, as desired by the CrmfRequestMessage class
                //PKIMessage msg = PKIMessage.getInstance(new ASN1InputStream(new ByteArrayInputStream(request)).readObject());
                //CrmfRequestMessage reqmsg = new CrmfRequestMessage(msg, null, true, null);
                //imsg = reqmsg;
            } else if (reqType == SecConst.CERT_REQ_TYPE_PUBLICKEY) {
                byte[] request;
                // Request can be Base64 encoded or in PEM format
                try {
                    request = FileTools.getBytesFromPEM(req.getBytes(), CertTools.BEGIN_PUBLIC_KEY,
                            CertTools.END_PUBLIC_KEY);
                } catch (IOException ex) {
                    try {
                        request = Base64.decode(req.getBytes());
                        if (request == null) {
                            throw new IOException("Base64 decode of buffer returns null");
                        }
                    } catch (ArrayIndexOutOfBoundsException ae) {
                        throw new IOException(
                                "Base64 decode fails, message not base64 encoded: " + ae.getMessage());
                    }
                }
                final ASN1InputStream in = new ASN1InputStream(request);
                final SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(in.readObject());
                final AlgorithmIdentifier keyAlg = keyInfo.getAlgorithmId();
                final X509EncodedKeySpec xKeySpec = new X509EncodedKeySpec(new DERBitString(keyInfo).getBytes());
                final KeyFactory keyFact = KeyFactory.getInstance(keyAlg.getObjectId().getId(), "BC");
                final PublicKey pubKey = keyFact.generatePublic(xKeySpec);
                imsg = new SimpleRequestMessage(pubKey, username, password);
            }
            if (imsg != null) {
                retval = getCertResponseFromPublicKey(admin, imsg, hardTokenSN, responseType, userdata);
            }
        } catch (NotFoundException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (InvalidKeyException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (NoSuchAlgorithmException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (InvalidKeySpecException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (NoSuchProviderException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (SignatureException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (IOException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (CertificateException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (EjbcaException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        }
        return retval;
    }

    @Override
    public IResponseMessage processCertReq(Admin admin, UserDataVO userdata, IRequestMessage req,
            Class responseClass) throws PersistenceException, AuthorizationDeniedException,
            UserDoesntFullfillEndEntityProfile, EjbcaException {
        // Check tokentype
        if (userdata.getTokenType() != SecConst.TOKEN_SOFT_BROWSERGEN) {
            throw new WrongTokenTypeException(
                    "Error: Wrong Token Type of user, must be 'USERGENERATED' for PKCS10/SPKAC/CRMF/CVC requests");
        }
        // This is the secret sauce, do the end entity handling automagically here before we get the cert
        addOrEditUser(admin, userdata, false, true);
        IResponseMessage retval = null;
        try {
            retval = signSession.createCertificate(admin, req, responseClass, userdata);
        } catch (NotFoundException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (EjbcaException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        }
        return retval;
    }

    /**
     * @throws CADoesntExistsException if userdata.caId is not a valid caid. This is checked in editUser or addUserFromWS
     */
    private void addOrEditUser(Admin admin, UserDataVO userdata, boolean clearpwd, boolean fromwebservice)
            throws AuthorizationDeniedException, UserDoesntFullfillEndEntityProfile, ApprovalException,
            PersistenceException, CADoesntExistsException, EjbcaException {

        int caid = userdata.getCAId();
        if (!authorizationSession.isAuthorizedNoLog(admin, AccessRulesConstants.CAPREFIX + caid)) {
            Authorizer.throwAuthorizationException(admin, AccessRulesConstants.CAPREFIX + caid, null);
        }
        if (!authorizationSession.isAuthorizedNoLog(admin, AccessRulesConstants.REGULAR_CREATECERTIFICATE)) {
            Authorizer.throwAuthorizationException(admin, AccessRulesConstants.REGULAR_CREATECERTIFICATE, null);
        }
        // First we need to fetch the CA configuration to see if we save UserData, if not, we still run addUserFromWS to
        // get all the proper authentication checks for CA and end entity profile.
        boolean useUserStorage = caAdminSession.getCAInfo(admin, caid).isUseUserStorage();
        // Add or edit user
        try {
            String username = userdata.getUsername();
            if (useUserStorage && userAdminSession.existsUser(admin, username)) {
                if (log.isDebugEnabled()) {
                    log.debug("User " + username + " exists, update the userdata. New status of user '"
                            + userdata.getStatus() + "'.");
                }
                userAdminSession.changeUser(admin, userdata, clearpwd, fromwebservice);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("New User " + username + ", adding userdata. New status of user '"
                            + userdata.getStatus() + "'.");
                }
                // addUserfromWS also checks useUserStorage internally, so don't dupliace the check
                userAdminSession.addUserFromWS(admin, userdata, clearpwd);
            }
        } catch (WaitingForApprovalException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            String msg = "Single transaction enrollment request rejected since approvals are enabled for this CA ("
                    + caid + ") or Certificate Profile (" + userdata.getCertificateProfileId() + ").";
            log.info(msg);
            throw new ApprovalException(msg);
        }
    }

    /**
     * Process a request in the CA module.
     * 
     * @param admin is the requesting administrator
     * @param msg is the request message processed by the CA
     * @param hardTokenSN is the hard token to associate this or null
     * @param responseType is one of SecConst.CERT_RES_TYPE_...
      * @return a encoded certificate of the type specified in responseType 
     */
    private byte[] getCertResponseFromPublicKey(Admin admin, IRequestMessage msg, String hardTokenSN,
            int responseType, UserDataVO userData)
            throws EjbcaException, CertificateEncodingException, CertificateException, IOException {
        byte[] retval = null;
        Class respClass = org.ejbca.core.protocol.X509ResponseMessage.class;
        IResponseMessage resp = signSession.createCertificate(admin, msg, respClass, userData);
        java.security.cert.Certificate cert = CertTools.getCertfromByteArray(resp.getResponseMessage());
        if (responseType == SecConst.CERT_RES_TYPE_CERTIFICATE) {
            retval = cert.getEncoded();
        }
        if (responseType == SecConst.CERT_RES_TYPE_PKCS7) {
            retval = signSession.createPKCS7(admin, cert, false);
        }
        if (responseType == SecConst.CERT_RES_TYPE_PKCS7WITHCHAIN) {
            retval = signSession.createPKCS7(admin, cert, true);
        }

        if (hardTokenSN != null) {
            hardTokenSession.addHardTokenCertificateMapping(admin, hardTokenSN, cert);
        }
        return retval;
    }

    @Override
    public byte[] processSoftTokenReq(Admin admin, UserDataVO userdata, String hardTokenSN, String keyspec,
            String keyalg, boolean createJKS) throws CADoesntExistsException, AuthorizationDeniedException,
            NotFoundException, InvalidKeyException, InvalidKeySpecException, NoSuchProviderException,
            SignatureException, IOException, ObjectNotFoundException, CertificateException,
            UserDoesntFullfillEndEntityProfile, ApprovalException, EjbcaException, KeyStoreException,
            NoSuchAlgorithmException, InvalidAlgorithmParameterException, PersistenceException {

        // This is the secret sauce, do the end entity handling automagically here before we get the cert
        addOrEditUser(admin, userdata, true, true);
        // Process request
        byte[] ret = null;
        try {
            // Get key recovery info
            boolean usekeyrecovery = globalConfigurationSession.getCachedGlobalConfiguration(admin)
                    .getEnableKeyRecovery();
            if (log.isDebugEnabled()) {
                log.debug("usekeyrecovery: " + usekeyrecovery);
            }
            boolean savekeys = userdata.getKeyRecoverable() && usekeyrecovery
                    && (userdata.getStatus() != UserDataConstants.STATUS_KEYRECOVERY);
            if (log.isDebugEnabled()) {
                log.debug("userdata.getKeyRecoverable(): " + userdata.getKeyRecoverable());
                log.debug("userdata.getStatus(): " + userdata.getStatus());
                log.debug("savekeys: " + savekeys);
            }
            boolean loadkeys = (userdata.getStatus() == UserDataConstants.STATUS_KEYRECOVERY) && usekeyrecovery;
            if (log.isDebugEnabled()) {
                log.debug("loadkeys: " + loadkeys);
            }
            int endEntityProfileId = userdata.getEndEntityProfileId();
            EndEntityProfile endEntityProfile = endEntityProfileSession.getEndEntityProfile(admin,
                    endEntityProfileId);
            boolean reusecertificate = endEntityProfile.getReUseKeyRecoveredCertificate();
            if (log.isDebugEnabled()) {
                log.debug("reusecertificate: " + reusecertificate);
            }
            // Generate keystore
            String password = userdata.getPassword();
            String username = userdata.getUsername();
            int caid = userdata.getCAId();
            GenerateToken tgen = new GenerateToken(authenticationSession, userAdminSession, caAdminSession,
                    keyRecoverySession, signSession);
            KeyStore keyStore = tgen.generateOrKeyRecoverToken(admin, username, password, caid, keyspec, keyalg,
                    createJKS, loadkeys, savekeys, reusecertificate, endEntityProfileId);
            String alias = keyStore.aliases().nextElement();
            X509Certificate cert = (X509Certificate) keyStore.getCertificate(alias);
            if ((hardTokenSN != null) && (cert != null)) {
                hardTokenSession.addHardTokenCertificateMapping(admin, hardTokenSN, cert);
            }
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            keyStore.store(baos, password.toCharArray());
            ret = baos.toByteArray();
        } catch (NotFoundException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (InvalidKeyException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (NoSuchAlgorithmException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (InvalidKeySpecException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (NoSuchProviderException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (SignatureException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (IOException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (CertificateException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (EjbcaException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (InvalidAlgorithmParameterException e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw e;
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            sessionContext.setRollbackOnly(); // This is an application exception so it wont trigger a roll-back automatically
            throw new KeyStoreException(e);
        }
        return ret;
    }
}