diff -urN ../ejbca_3_5_11/Changelog.txt ./Changelog.txt --- ../ejbca_3_5_11/Changelog.txt 2009-01-28 10:50:42.000000000 +0100 +++ ./Changelog.txt 2009-03-13 09:24:08.000000000 +0100 @@ -1,3 +1,12 @@ +3.5.12, 2009-03-13 +--- +Improvement + * [ECA-1111] - Optimize performance of findCerts WS call + * [ECA-1112] - Create a new ant target similar to create-lot-of-users, but creates fewer users with many certs per user + +Bug + * [ECA-1091] - Serious bug in UserDataSource Authorization + 3.5.11, 2009-01-28 --- Improvement diff -urN ../ejbca_3_5_11/doc/RELEASE_NOTES ./doc/RELEASE_NOTES --- ../ejbca_3_5_11/doc/RELEASE_NOTES 2009-01-28 10:50:42.000000000 +0100 +++ ./doc/RELEASE_NOTES 2009-03-13 09:24:08.000000000 +0100 @@ -1,3 +1,14 @@ +EJBCA 3.5.12 +----------- +This is a minor release, fixing a performance issue with getCerts webservice call when a single user have lots of certificates, and +also an issue with authorization in UserDataSource. +- Optimize performance of findCerts WS call +- Serious bug in UserDataSource Authorization + +Read the changelog for details. + +This is a plug-in upgrade from 3.5.x. See UPGRADE for the simple instructions. + EJBCA 3.5.11 ----------- This is a minor release, fixing two minor issues with webservice calls used by HardTokenMgmt. diff -urN ../ejbca_3_5_11/doc/xdocs/index.xml ./doc/xdocs/index.xml --- ../ejbca_3_5_11/doc/xdocs/index.xml 2008-06-30 09:40:54.000000000 +0200 +++ ./doc/xdocs/index.xml 2009-03-13 09:24:08.000000000 +0100 @@ -12,7 +12,7 @@ here. - EJBCA 3.5.7 is now available for download.
SHA1 checksum: xx diff -urN ../ejbca_3_5_11/propertiesAndPaths.xmli ./propertiesAndPaths.xmli --- ../ejbca_3_5_11/propertiesAndPaths.xmli 2009-01-28 10:52:58.000000000 +0100 +++ ./propertiesAndPaths.xmli 2009-03-13 09:24:08.000000000 +0100 @@ -3,7 +3,7 @@ - + diff -urN ../ejbca_3_5_11/src/java/org/ejbca/core/ejb/ca/crl/CreateCRLSessionBean.java ./src/java/org/ejbca/core/ejb/ca/crl/CreateCRLSessionBean.java --- ../ejbca_3_5_11/src/java/org/ejbca/core/ejb/ca/crl/CreateCRLSessionBean.java 2008-06-13 10:23:04.000000000 +0200 +++ ./src/java/org/ejbca/core/ejb/ca/crl/CreateCRLSessionBean.java 2009-03-13 09:24:08.000000000 +0100 @@ -19,6 +19,7 @@ import javax.ejb.CreateException; import javax.ejb.EJBException; +import javax.ejb.FinderException; import org.ejbca.core.ejb.BaseSessionBean; import org.ejbca.core.ejb.ca.caadmin.ICAAdminSessionLocal; @@ -166,7 +167,7 @@ /** * Generates a new CRL by looking in the database for revoked certificates and generating a - * CRL. + * CRL. This method also "archives" certificates when after they are no longer neeeded in the CRL. * * @param admin administrator performing the task * @param issuerdn of the ca (normalized for EJBCA) @@ -204,9 +205,7 @@ // so the revoked certs are included in ONE CRL at least. if ( data.getExpireDate().before(now) ) { // Certificate has expired, set status to archived in the database - CertificateDataPK pk = new CertificateDataPK(data.getCertificateFingerprint()); - CertificateDataLocal certdata = certHome.findByPrimaryKey(pk); - certdata.setStatus(CertificateDataBean.CERT_ARCHIVED); + setArchivedStatus(data.getCertificateFingerprint()); } else { if (revDate == null) { data.setRevocationDate(new Date()); @@ -245,6 +244,20 @@ debug("verifyProtection, dn:" + issuerDN + ", serno=" + serno.toString(16)); + } + try { + if (protectHome != null) { + // First make a DN in our well-known format + Collection coll = certHome.findByIssuerDNSerialNumber(CertTools.stringToBCDNString(issuerDN), serno.toString()); + if (coll != null) { + if (coll.size() > 1) { + String msg = intres.getLocalizedMessage("store.errorseveralissuerserno", issuerDN, serno.toString(16)); + adapter.error(msg); + } + Iterator iter = coll.iterator(); + if (iter.hasNext()) { + CertificateDataLocal data = (CertificateDataLocal) iter.next(); + verifyProtection(data, protectHome, adapter); + } + } + } + } catch (FinderException e) { + throw new EJBException(e); // This should exist here + } + } + + + private static void verifyProtection(CertificateDataLocal data, TableProtectSessionLocalHome protectHome, Adapter adapter) { + CertificateInfo entry = new CertificateInfo(data.getFingerprint(), data.getCaFingerprint(), data.getSerialNumber(), data.getIssuerDN(), data.getSubjectDN(), data.getStatus(), data.getType(), data.getExpireDate(), data.getRevocationDate(), data.getRevocationReason()); + TableProtectSessionLocal protect; + try { + protect = protectHome.create(); + // The verify method will log failed verifies itself + TableVerifyResult res = protect.verify(entry); + if (res.getResultCode() != TableVerifyResult.VERIFY_SUCCESS) { + //adapter.error("Verify failed, but we go on anyway."); + } + } catch (CreateException e) { + String msg = intres.getLocalizedMessage("protect.errorcreatesession"); + adapter.error(msg, e); + } + } } diff -urN ../ejbca_3_5_11/src/java/org/ejbca/core/ejb/ca/store/LocalCertificateStoreSessionBean.java ./src/java/org/ejbca/core/ejb/ca/store/LocalCertificateStoreSessionBean.java --- ../ejbca_3_5_11/src/java/org/ejbca/core/ejb/ca/store/LocalCertificateStoreSessionBean.java 2008-05-02 08:27:22.000000000 +0200 +++ ./src/java/org/ejbca/core/ejb/ca/store/LocalCertificateStoreSessionBean.java 2009-03-13 09:24:08.000000000 +0100 @@ -788,7 +788,7 @@ } // findUsernameByCertSerno /** - * Finds certificate(s) for a given usernaem. + * Finds certificate(s) for a given username. * * @param admin Administrator performing the operation * @param username the usernaem of the certificate(s) that will be retrieved @@ -820,6 +820,39 @@ } // findCertificateByUsername /** + * Finds certificate(s) for a given username and status. + * + * @param admin Administrator performing the operation + * @param username the username of the certificate(s) that will be retrieved + * @param status the status of the CertificateDataBean.CERT_ constants + * @return Collection of Certificates ordered by expire date, with last expire date first + * @ejb.interface-method + */ + public Collection findCertificatesByUsernameAndStatus(Admin admin, String username, int status) { + if (log.isTraceEnabled()) { + log.trace(">findCertificatesByUsername(), username=" + username); + } + ArrayList ret = new ArrayList(); + try { + // Strip dangerous chars + username = StringTools.strip(username); + // This method on the entity bean does the ordering in the database + Collection coll = certHome.findByUsernameAndStatus(username, status); + if (coll != null) { + Iterator iter = coll.iterator(); + while (iter.hasNext()) { + ret.add(((CertificateDataLocal) iter.next()).getCertificate()); + } + } + } catch (javax.ejb.FinderException e) { // Ignore + } + if (log.isTraceEnabled()) { + log.trace(" findCerts(String username, boolean onlyValid) throws AuthorizationDeniedException, NotFoundException, EjbcaException { - List retval = null; try{ Admin admin = getAdmin(); getUserAdminSession().findUser(admin,username); - - Collection certs = getCertStoreSession().findCertificatesByUsername(admin,username); - - if(onlyValid){ - certs = returnOnlyValidCertificates(admin,certs); - } - - certs = returnOnlyAuthorizedCertificates(admin,certs); - - if(certs.size() > 0){ - retval = new ArrayList(); - Iterator iter = certs.iterator(); - for(int i=0; i < certs.size(); i++){ - retval.add(new Certificate((java.security.cert.Certificate) iter.next())); - } + Collection certs; + if (onlyValid) { + certs = getCertStoreSession().findCertificatesByUsernameAndStatus(admin, username, CertificateDataBean.CERT_ACTIVE); + } else { + certs = getCertStoreSession().findCertificatesByUsername(admin, username); } - }catch(AuthorizationDeniedException e){ - throw e; + retval = returnAuthorizedCertificates(admin, certs, onlyValid); } catch (ClassCastException e) { log.error("EJBCA WebService error, findCerts : ",e); throw new EjbcaException(e.getMessage()); @@ -255,10 +250,10 @@ throw new EjbcaException(e.getMessage()); } catch (FinderException e) { throw new NotFoundException(e.getMessage()); - } catch (CertificateEncodingException e) { + } catch (EJBException e) { log.error("EJBCA WebService error, findCerts : ",e); throw new EjbcaException(e.getMessage()); - } + } return retval; } @@ -1753,27 +1748,48 @@ return retval; } - private Collection returnOnlyAuthorizedCertificates(Admin admin, Collection certs) { - ArrayList retval = new ArrayList(); - - Iterator iter = certs.iterator(); + /** + * Checks authorization for each certificate and optionally check that it's valid. Does not check revocation status. + * @param admin is the admin used for authorization + * @param certs is the collection of certs to verify + * @param validate set to true to perform validation of each certificate + * @return a List of valid and authorized certificates + * @throws NamingException + * @throws CreateException + * @throws ClassCastException + */ + protected List returnAuthorizedCertificates(Admin admin, Collection certs, boolean validate) throws ClassCastException, CreateException, NamingException { + List retval = new ArrayList(); + Iterator iter = certs.iterator(); + Map authorizationCache = new HashMap(); while(iter.hasNext()){ - X509Certificate next = (X509Certificate) iter.next(); - - try{ - // check that admin is autorized to CA - int caid = CertTools.stringToBCDNString(next.getIssuerDN().toString()).hashCode(); - getAuthorizationSession().isAuthorizedNoLog(admin,AvailableAccessRules.CAPREFIX +caid); - retval.add(next); - }catch(AuthorizationDeniedException ade){ - log.debug("findCerts : not authorized to certificate " + next.getSerialNumber().toString(16)); + java.security.cert.Certificate next = iter.next(); + try { + if (validate) { + // Check validity + ((X509Certificate) next).checkValidity(new Date()); + getCertStoreSession().verifyProtection(admin, ((X509Certificate) next).getIssuerDN().toString(), ((X509Certificate) next).getSerialNumber()); + } + // Check authorization + int caid = CertTools.stringToBCDNString(((X509Certificate) next).getIssuerDN().toString()).hashCode(); + Boolean authorized = authorizationCache.get(caid); + if (authorized == null) { + authorized = getAuthorizationSession().isAuthorizedNoLog(admin,AvailableAccessRules.CAPREFIX +caid); + authorizationCache.put(caid, authorized); + } + if (authorized) { + retval.add(new Certificate((java.security.cert.Certificate) next)); + } + } catch (CertificateExpiredException e) { // Drop invalid cert + } catch (CertificateNotYetValidException e) { // Drop invalid cert + } catch (CertificateEncodingException e) { // Drop invalid cert + log.error("A defect certificate was detected."); + } catch (AuthorizationDeniedException e) { // Drop unauthorized cert } } - return retval; } - - + private final String[] softtokennames = {UserDataVOWS.TOKEN_TYPE_USERGENERATED,UserDataVOWS.TOKEN_TYPE_P12, UserDataVOWS.TOKEN_TYPE_JKS,UserDataVOWS.TOKEN_TYPE_PEM}; private final int[] softtokenids = {SecConst.TOKEN_SOFT_BROWSERGEN, diff -urN ../ejbca_3_5_11/src/test/se/anatom/ejbca/ra/TestAddLotsofCertsPerUser.java ./src/test/se/anatom/ejbca/ra/TestAddLotsofCertsPerUser.java --- ../ejbca_3_5_11/src/test/se/anatom/ejbca/ra/TestAddLotsofCertsPerUser.java 1970-01-01 01:00:00.000000000 +0100 +++ ./src/test/se/anatom/ejbca/ra/TestAddLotsofCertsPerUser.java 2009-03-13 09:24:08.000000000 +0100 @@ -0,0 +1,222 @@ +/************************************************************************* + * * + * 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 se.anatom.ejbca.ra; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.Random; + +import junit.framework.TestCase; + +import org.apache.log4j.Logger; +import org.ejbca.core.ejb.ServiceLocator; +import org.ejbca.core.ejb.ca.crl.ICreateCRLSessionHome; +import org.ejbca.core.ejb.ca.crl.ICreateCRLSessionRemote; +import org.ejbca.core.ejb.ca.sign.ISignSessionHome; +import org.ejbca.core.ejb.ca.sign.ISignSessionRemote; +import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionHome; +import org.ejbca.core.ejb.ca.store.ICertificateStoreSessionRemote; +import org.ejbca.core.ejb.ra.IUserAdminSessionHome; +import org.ejbca.core.ejb.ra.IUserAdminSessionRemote; +import org.ejbca.core.ejb.ra.raadmin.IRaAdminSessionHome; +import org.ejbca.core.ejb.ra.raadmin.IRaAdminSessionRemote; +import org.ejbca.core.model.SecConst; +import org.ejbca.core.model.ca.certificateprofiles.CertificateProfile; +import org.ejbca.core.model.ca.certificateprofiles.CertificateProfileExistsException; +import org.ejbca.core.model.ca.certificateprofiles.EndUserCertificateProfile; +import org.ejbca.core.model.ca.crl.RevokedCertInfo; +import org.ejbca.core.model.log.Admin; +import org.ejbca.core.model.ra.ExtendedInformation; +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.util.CertTools; + + +/** + * Add a lot of users and a lot of certificates for each user + * + * @version $Id: TestAddLotsofUsers.java 6668 2008-11-28 16:28:44Z jeklund $ + */ +public class TestAddLotsofCertsPerUser extends TestCase { + private static final Logger log = Logger.getLogger(TestAddLotsofCertsPerUser.class); + + private IUserAdminSessionRemote userAdminSession; + private ISignSessionRemote signSession; + private IRaAdminSessionRemote raAdminSession; + private ICertificateStoreSessionRemote certificateStoreSession; + private ICreateCRLSessionRemote createCrlSession; + + private static String baseUsername; + private static int userNo = 0; + private static final int caid = "CN=TEST".hashCode(); + private static KeyPair keys; + + /** + * Creates a new TestAddLotsofUsers object. + * + * @param name name + * @throws InvalidAlgorithmParameterException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + */ + public TestAddLotsofCertsPerUser(String name) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException { + super(name); + CertTools.installBCProvider(); + keys = org.ejbca.util.KeyTools.genKeys("2048", "RSA"); + try { + if (userAdminSession == null) { + userAdminSession = ((IUserAdminSessionHome) ServiceLocator.getInstance().getRemoteHome(IUserAdminSessionHome.JNDI_NAME, IUserAdminSessionHome.class)).create(); + } + if (signSession == null) { + signSession = ((ISignSessionHome) ServiceLocator.getInstance().getRemoteHome(ISignSessionHome.JNDI_NAME, ISignSessionHome.class)).create(); + } + if (raAdminSession == null) { + raAdminSession = ((IRaAdminSessionHome) ServiceLocator.getInstance().getRemoteHome(IRaAdminSessionHome.JNDI_NAME, IRaAdminSessionHome.class)).create(); + } + if (certificateStoreSession == null) { + certificateStoreSession = ((ICertificateStoreSessionHome) ServiceLocator.getInstance().getRemoteHome(ICertificateStoreSessionHome.JNDI_NAME, ICertificateStoreSessionHome.class)).create(); + } + if (createCrlSession == null) { + createCrlSession = ((ICreateCRLSessionHome) ServiceLocator.getInstance().getRemoteHome(ICreateCRLSessionHome.JNDI_NAME, ICreateCRLSessionHome.class)).create(); + } + } catch (Exception e) { + log.error("", e); + } + } + + protected void setUp() throws Exception { + baseUsername = "lotsacertsperuser-" + System.currentTimeMillis() + "-"; + } + + protected void tearDown() throws Exception { + } + + private String genUserName() throws Exception { + userNo++; + return baseUsername + userNo; + } + + private String genRandomPwd() throws Exception { + Random rand = new Random(new Date().getTime() + 4812); + String password = ""; + for (int i = 0; i < 8; i++) { + int randint = rand.nextInt(9); + password += (new Integer(randint)).toString(); + } + return password; + } + + /** + * tests creating 10 users, each with 50 active, 50 revoked, 50 expired and 50 expired and "archived" + * + * @throws Exception on error + */ + public void test01Create2000Users() throws Exception { + log.trace(">test01Create2000Users()"); + Admin administrator = new Admin(Admin.TYPE_INTERNALUSER); + final int NUMBER_OF_USERS = 10; + final int CERTS_OF_EACH_KIND = 50; + for (int i = 0; i < NUMBER_OF_USERS; i++) { + String username = genUserName(); + String password = genRandomPwd(); + final String certificateProfileName = "testLotsOfCertsPerUser"; + final String endEntityProfileName = "testLotsOfCertsPerUser"; + CertificateProfile certificateProfile = new EndUserCertificateProfile(); + certificateProfile.setValidity(1); + certificateProfile.setAllowValidityOverride(true); + try { + certificateStoreSession.addCertificateProfile(administrator, certificateProfileName, certificateProfile); + } catch (CertificateProfileExistsException e) { + } + + int type = SecConst.USER_ENDUSER; + int token = SecConst.TOKEN_SOFT_P12; + int profileid = SecConst.EMPTY_ENDENTITYPROFILE; + int certificatetypeid = SecConst.CERTPROFILE_FIXED_ENDUSER; + int hardtokenissuerid = SecConst.NO_HARDTOKENISSUER; + String dn = "C=SE, O=AnaTom, CN=" + username; + String subjectaltname = "rfc822Name=" + username + "@foo.se"; + String email = username + "@foo.se"; + if (userAdminSession.findUser(administrator, username) != null) { + System.out.println("Error : User already exists in the database."); + } + UserDataVO userdata = new UserDataVO(username, CertTools.stringToBCDNString(dn), caid, subjectaltname, + email, UserDataConstants.STATUS_NEW, type, profileid, certificatetypeid, + null,null, token, hardtokenissuerid, null); + userdata.setPassword(password); + userAdminSession.addUser(administrator, userdata, true); + // Create some valid certs + for (int j=0; j + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +