Java tutorial
/************************************************************************* * * * 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; } }