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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+