[Pki-devel] [PATCH] random certificate serial numbers - updated

Andrew Wnuk awnuk at redhat.com
Sat Apr 20 02:47:03 UTC 2013


This patch adds support for random certificate serial numbers.
It was updated to add ability to configure random certificate serial 
numbers using pkispawn.

Bug 912554.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://listman.redhat.com/archives/pki-devel/attachments/20130419/fec40016/attachment.htm>
-------------- next part --------------
diff --git a/base/ca/shared/conf/CS.cfg.in b/base/ca/shared/conf/CS.cfg.in
index a1acde2..1316e9b 100644
--- a/base/ca/shared/conf/CS.cfg.in
+++ b/base/ca/shared/conf/CS.cfg.in
@@ -769,7 +769,9 @@ cmsgateway._029=##
 cmsgateway.enableAdminEnroll=false
 https.port=8443
 http.port=8080
-dbs.enableSerialManagement=false
+dbs.enableSerialManagement=[PKI_ENABLE_RANDOM_SERIAL_NUMBERS]
+dbs.enableRandomSerialNumbers=[PKI_ENABLE_RANDOM_SERIAL_NUMBERS]
+dbs.randomSerialNumberCounter=0
 dbs.beginRequestNumber=1
 dbs.endRequestNumber=10000000
 dbs.requestIncrement=10000000
diff --git a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
index 21859a0..39f336b 100644
--- a/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
+++ b/base/common/src/com/netscape/certsrv/ca/ICertificateAuthority.java
@@ -34,6 +34,7 @@ import org.mozilla.jss.crypto.SignatureAlgorithm;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.dbs.IDBSubsystem;
 import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
 import com.netscape.certsrv.dbs.crldb.ICRLRepository;
 import com.netscape.certsrv.dbs.replicadb.IReplicaIDRepository;
@@ -473,6 +474,13 @@ public interface ICertificateAuthority extends ISubsystem {
     public IService getCAService();
 
     /**
+     * Retrieves the DB subsystem managing internal data storage.
+     *
+     * @return DB subsystem object
+     */
+    public IDBSubsystem getDBSubsystem();
+
+    /**
      * Returns the in-memory count of the processed OCSP requests.
      *
      * @return number of processed OCSP requests in memory
diff --git a/base/common/src/com/netscape/certsrv/common/Constants.java b/base/common/src/com/netscape/certsrv/common/Constants.java
index 880e146..bc8dcef 100644
--- a/base/common/src/com/netscape/certsrv/common/Constants.java
+++ b/base/common/src/com/netscape/certsrv/common/Constants.java
@@ -346,6 +346,8 @@ public interface Constants {
     public final static String PR_ALL_ALGORITHMS = "allSigningAlgorithms";
     public final static String PR_SERIAL = "startSerialNumber";
     public final static String PR_MAXSERIAL = "maxSerialNumber";
+    public final static String PR_SN_MANAGEMENT = "serialNumberManagement";
+    public final static String PR_RANDOM_SN = "randomSerialNumbers";
 
     /*========================================================
      * Access Control
diff --git a/base/common/src/com/netscape/certsrv/dbs/IDBSubsystem.java b/base/common/src/com/netscape/certsrv/dbs/IDBSubsystem.java
index dfa5312..de4060e 100644
--- a/base/common/src/com/netscape/certsrv/dbs/IDBSubsystem.java
+++ b/base/common/src/com/netscape/certsrv/dbs/IDBSubsystem.java
@@ -23,6 +23,7 @@ import netscape.ldap.LDAPConnection;
 
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.ISubsystem;
+import com.netscape.certsrv.base.IConfigStore;
 
 /**
  * An interface represents certificate server
@@ -204,6 +205,32 @@ public interface IDBSubsystem extends ISubsystem {
     public void setEnableSerialMgmt(boolean value) throws EBaseException;
 
     /**
+     * Gets internal DB configuration store
+     *
+     * @return internal DB configuration store
+     */
+    public IConfigStore getConfigStore();
+
+    /**
+     * Gets DB subsystem configuration store
+     *
+     * @return DB subsystem configuration store
+     */
+    public IConfigStore getDBConfigStore();
+
+    /**
+     * Gets attribute value for specified entry
+     *
+     * @param dn            entry's distinguished name 
+     * @param attrName      attribute's name 
+     * @param defaultValue  attribute's default value 
+     * @param errorValue    attribute's error value 
+     * @return attribute value
+     */
+    public String getEntryAttribute(String dn, String attrName,
+                                    String defaultValue, String errorValue);
+
+    /**
      * Returns LDAP connection to connection pool.
      *
      * @param conn connection to be returned
diff --git a/base/common/src/com/netscape/certsrv/dbs/certdb/ICertificateRepository.java b/base/common/src/com/netscape/certsrv/dbs/certdb/ICertificateRepository.java
index d54cfb3..40d22d6 100644
--- a/base/common/src/com/netscape/certsrv/dbs/certdb/ICertificateRepository.java
+++ b/base/common/src/com/netscape/certsrv/dbs/certdb/ICertificateRepository.java
@@ -42,6 +42,16 @@ import com.netscape.cmscore.dbs.CertificateRepository.RenewableCertificateCollec
 public interface ICertificateRepository extends IRepository {
 
     /**
+     * Retrieves the next certificate serial number, and also increases
+     * the serial number by one.
+     *
+     * @return serial number
+     * @exception EBaseException failed to retrieve next serial number
+     */
+    public BigInteger getNextSerialNumber()
+            throws EBaseException;
+
+    /**
      * Adds a certificate record to the repository. Each certificate
      * record contains four parts: certificate, meta-attributes,
      * issue information and reovcation information.
@@ -512,5 +522,23 @@ public interface ICertificateRepository extends IRepository {
      */
     public void removeCertRecords(BigInteger beginS, BigInteger endS) throws EBaseException;
 
+    /**
+     * Retrieves serial number management mode.
+     *
+     * @return serial number management mode,
+     * "true" indicates random serial number management,
+     * "false" indicates sequential serial number management.
+     */
+    public boolean getEnableRandomSerialNumbers();
+
+    /**
+     * Sets serial number management mode for certificates..
+     *
+     * @param random "true" sets random serial number management, "false" sequential
+     * @param updateMode "true" updates "description" attribute in certificate repository
+     * @param forceModeChange "true" forces certificate repository mode change
+     */
+    public void setEnableRandomSerialNumbers(boolean random, boolean updateMode, boolean forceModeChange);
+
     public void shutdown();
 }
diff --git a/base/common/src/com/netscape/certsrv/dbs/repository/IRepositoryRecord.java b/base/common/src/com/netscape/certsrv/dbs/repository/IRepositoryRecord.java
index 12dc71c..dd5f557 100644
--- a/base/common/src/com/netscape/certsrv/dbs/repository/IRepositoryRecord.java
+++ b/base/common/src/com/netscape/certsrv/dbs/repository/IRepositoryRecord.java
@@ -32,6 +32,7 @@ public interface IRepositoryRecord extends IDBObj {
 
     public final static String ATTR_SERIALNO = "serialNo";
     public final static String ATTR_PUB_STATUS = "publishingStatus";
+    public final static String ATTR_DESCRIPTION = "description";
 
     /**
      * Retrieves serial number.
@@ -41,4 +42,6 @@ public interface IRepositoryRecord extends IDBObj {
     public BigInteger getSerialNumber();
 
     public String getPublishingStatus();
+
+    public String getDescription();
 }
diff --git a/base/common/src/com/netscape/cms/servlet/admin/CAAdminServlet.java b/base/common/src/com/netscape/cms/servlet/admin/CAAdminServlet.java
index 9e06f04..09c77e5 100644
--- a/base/common/src/com/netscape/cms/servlet/admin/CAAdminServlet.java
+++ b/base/common/src/com/netscape/cms/servlet/admin/CAAdminServlet.java
@@ -1480,6 +1480,10 @@ public class CAAdminServlet extends AdminServlet {
         getSigningAlgConfig(params);
         getSerialConfig(params);
         getMaxSerialConfig(params);
+        params.put(Constants.PR_SN_MANAGEMENT,
+            Boolean.toString(mCA.getDBSubsystem().getEnableSerialMgmt()));
+        params.put(Constants.PR_RANDOM_SN,
+            Boolean.toString(mCA.getCertificateRepository().getEnableRandomSerialNumbers()));
 
         sendResponse(SUCCESS, null, params, resp);
     }
@@ -1549,6 +1553,10 @@ public class CAAdminServlet extends AdminServlet {
                 mCA.setStartSerial(value);
             } else if (key.equals(Constants.PR_MAXSERIAL)) {
                 mCA.setMaxSerial(value);
+            } else if (key.equals(Constants.PR_SN_MANAGEMENT)) {
+                mCA.getDBSubsystem().setEnableSerialMgmt(Boolean.valueOf(value));
+            } else if (key.equals(Constants.PR_RANDOM_SN)) {
+                mCA.getCertificateRepository().setEnableRandomSerialNumbers(Boolean.valueOf(value), true, false);
             }
         }
 
diff --git a/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java b/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java
index c2ecb87..b953351 100644
--- a/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java
+++ b/base/common/src/com/netscape/cmscore/dbs/CertificateRepository.java
@@ -25,6 +25,7 @@ import java.util.Date;
 import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Vector;
+import java.util.Random;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
@@ -43,8 +44,10 @@ import com.netscape.certsrv.apps.CMS;
 import com.netscape.certsrv.base.EBaseException;
 import com.netscape.certsrv.base.MetaInfo;
 import com.netscape.certsrv.base.SessionContext;
+import com.netscape.certsrv.base.IConfigStore;
 import com.netscape.certsrv.ca.ICRLIssuingPoint;
 import com.netscape.certsrv.dbs.EDBException;
+import com.netscape.certsrv.dbs.EDBRecordNotFoundException;
 import com.netscape.certsrv.dbs.IDBSSession;
 import com.netscape.certsrv.dbs.IDBSearchResults;
 import com.netscape.certsrv.dbs.IDBSubsystem;
@@ -56,6 +59,7 @@ import com.netscape.certsrv.dbs.certdb.ICertRecordList;
 import com.netscape.certsrv.dbs.certdb.ICertificateRepository;
 import com.netscape.certsrv.dbs.certdb.IRevocationInfo;
 import com.netscape.certsrv.dbs.repository.IRepository;
+import com.netscape.certsrv.dbs.repository.IRepositoryRecord;
 import com.netscape.certsrv.logging.ILogger;
 
 /**
@@ -71,6 +75,15 @@ public class CertificateRepository extends Repository
         implements ICertificateRepository {
 
     public final String CERT_X509ATTRIBUTE = "x509signedcert";
+    private static final String PROP_ENABLE_RANDOM_SERIAL_NUMBERS = "enableRandomSerialNumbers";
+    private static final String PROP_RANDOM_SERIAL_NUMBER_COUNTER = "randomSerialNumberCounter";
+    private static final String PROP_FORCE_MODE_CHANGE = "forceModeChange";
+    private static final String PROP_RANDOM_MODE = "random";
+    private static final String PROP_SEQUENTIAL_MODE = "sequential";
+    private static final String PROP_COLLISION_RECOVERY_STEPS = "collisionRecoverySteps";
+    private static final String PROP_COLLISION_RECOVERY_REGENERATIONS = "collisionRecoveryRegenerations";
+    private static final String PROP_MINIMUM_RANDOM_BITS = "minimumRandomBits";
+    private static final BigInteger BI_MINUS_ONE = (BigInteger.ZERO).subtract(BigInteger.ONE);
 
     private IDBSubsystem mDBService;
     private String mBaseDN;
@@ -85,6 +98,15 @@ public class CertificateRepository extends Repository
     private int mTransitMaxRecords = 1000000;
     private int mTransitRecordPageSize = 200;
 
+    private Random mRandom = null;
+    private int mBitLength = 0;
+    private BigInteger mRangeSize = null;
+    private int mMinRandomBitLength = 4;
+    private int mMaxCollisionRecoverySteps = 10;
+    private int mMaxCollisionRecoveryRegenerations = 3;
+    private IConfigStore mDBConfig = null;
+    private boolean mForceModeChange = false;
+
     public CertStatusUpdateTask certStatusUpdateTask;
     public RetrieveModificationsTask retrieveModificationsTask;
 
@@ -96,12 +118,302 @@ public class CertificateRepository extends Repository
         super(dbService, increment, baseDN);
         mBaseDN = certRepoBaseDN;
         mDBService = dbService;
+        mDBConfig = mDBService.getDBConfigStore();
     }
 
     public ICertRecord createCertRecord(BigInteger id, Certificate cert, MetaInfo meta) {
         return new CertRecord(id, cert, meta);
     }
 
+    public boolean getEnableRandomSerialNumbers() {
+        return mEnableRandomSerialNumbers;
+    }
+
+    public void setEnableRandomSerialNumbers(boolean random, boolean updateMode, boolean forceModeChange) {
+        CMS.debug("CertificateRepository:  setEnableRandomSerialNumbers   random="+random+"  updateMode="+updateMode);
+        if (mEnableRandomSerialNumbers ^ random || forceModeChange) {
+            mEnableRandomSerialNumbers = random;
+            CMS.debug("CertificateRepository:  setEnableRandomSerialNumbers   switching to " +
+                      ((random)?PROP_RANDOM_MODE:PROP_SEQUENTIAL_MODE) + " mode");
+            if (updateMode) {
+                setCertificateRepositoryMode((mEnableRandomSerialNumbers)? PROP_RANDOM_MODE: PROP_SEQUENTIAL_MODE);
+            }
+            mDBConfig.putBoolean(PROP_ENABLE_RANDOM_SERIAL_NUMBERS, mEnableRandomSerialNumbers);
+
+            BigInteger lastSerialNumber = null;
+            try {
+                lastSerialNumber = getLastSerialNumberInRange(mMinSerialNo,mMaxSerialNo);
+            } catch (Exception e) {
+            }
+            if (lastSerialNumber != null) {
+                super.setLastSerialNo(lastSerialNumber);
+                if (mEnableRandomSerialNumbers) {
+                    mCounter = lastSerialNumber.subtract(mMinSerialNo).add(BigInteger.ONE);
+                    CMS.debug("CertificateRepository:  setEnableRandomSerialNumbers  mCounter="+
+                               mCounter+"="+lastSerialNumber+"-"+mMinSerialNo+"+1");
+                    long t = System.currentTimeMillis();
+                    mDBConfig.putString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, mCounter.toString()+","+t);
+                } else {
+                    mCounter = BI_MINUS_ONE;
+                    mDBConfig.putString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, mCounter.toString());
+                }
+            }
+
+            try {
+                CMS.getConfigStore().commit(false);
+            } catch (Exception e) {
+            }
+        }
+    }
+
+    private BigInteger getRandomNumber() throws EBaseException {
+        BigInteger randomNumber = null;
+
+        if (mRandom == null) {
+            mRandom = new Random();
+        }
+        super.initCacheIfNeeded();
+
+        if (mRangeSize == null) {
+            mRangeSize = (mMaxSerialNo.subtract(mMinSerialNo)).add(BigInteger.ONE);
+            CMS.debug("CertificateRepository: getRandomNumber  mRangeSize="+mRangeSize);
+            mBitLength = mRangeSize.bitLength();
+            CMS.debug("CertificateRepository: getRandomNumber  mBitLength="+mBitLength+
+                      " >mMinRandomBitLength="+mMinRandomBitLength);
+        }
+        if (mBitLength < mMinRandomBitLength) {
+            CMS.debug("CertificateRepository: getRandomNumber  mBitLength="+mBitLength+
+                      " <mMinRandomBitLength="+mMinRandomBitLength);
+            CMS.debug("CertificateRepository: getRandomNumber:  Range size is too small to support random certificate serial numbers.");
+            throw new EBaseException ("Range size is too small to support random certificate serial numbers.");
+        }
+        randomNumber = new BigInteger((mBitLength), mRandom);
+        randomNumber = (randomNumber.multiply(mRangeSize)).shiftRight(mBitLength);
+        CMS.debug("CertificateRepository: getRandomNumber  randomNumber="+randomNumber);
+
+        return randomNumber; 
+    }
+
+    private BigInteger getRandomSerialNumber(BigInteger randomNumber) throws EBaseException {
+        BigInteger nextSerialNumber = null;
+
+        nextSerialNumber = randomNumber.add(mMinSerialNo);
+        CMS.debug("CertificateRepository: getRandomSerialNumber  nextSerialNumber="+nextSerialNumber);
+
+        return nextSerialNumber; 
+    }
+
+    private BigInteger checkSerialNumbers(BigInteger randomNumber, BigInteger serialNumber) throws EBaseException {
+        BigInteger nextSerialNumber = null;
+        BigInteger initialRandomNumber = randomNumber;
+        BigInteger delta = BigInteger.ZERO;
+        int i = 0;
+        int n = mMaxCollisionRecoverySteps;
+
+        do {
+            CMS.debug("CertificateRepository: checkSerialNumbers  checking("+(i+1)+")="+serialNumber);
+            try {
+                if (readCertificateRecord(serialNumber) != null) {
+                    CMS.debug("CertificateRepository: checkSerialNumbers  collision detected for serialNumber="+serialNumber);
+                }
+            } catch (EDBRecordNotFoundException nfe) {
+                CMS.debug("CertificateRepository: checkSerialNumbers  serial number "+serialNumber+" is available");
+                nextSerialNumber = serialNumber;
+            } catch (Exception e) {
+                CMS.debug("CertificateRepository: checkSerialNumbers  Exception="+e.getMessage());
+            }
+
+            if (nextSerialNumber == null) {
+                if (i%2 == 0) {
+                    delta = delta.add(BigInteger.ONE);
+                    serialNumber = getRandomSerialNumber(initialRandomNumber.add(delta));
+
+                    if (mMaxSerialNo != null && serialNumber.compareTo(mMaxSerialNo) > 0) {
+                        serialNumber = getRandomSerialNumber(initialRandomNumber.subtract(delta));
+                        i++;
+                        n++;
+                    }
+                } else {
+                    serialNumber = getRandomSerialNumber(initialRandomNumber.subtract(delta));
+                    if (mMinSerialNo != null && serialNumber.compareTo(mMinSerialNo) < 0) {
+                        delta = delta.add(BigInteger.ONE);
+                        serialNumber = getRandomSerialNumber(initialRandomNumber.add(delta));
+                        i++;
+                        n++;
+                    }
+                }
+                i++;
+            }
+        } while (nextSerialNumber == null && i < n);
+
+        return nextSerialNumber; 
+    }
+
+    private Object nextSerialNumberMonitor = new Object();
+
+    public BigInteger getNextSerialNumber() throws
+            EBaseException {
+
+        BigInteger nextSerialNumber = null;
+        BigInteger randomNumber = null;
+
+        synchronized (nextSerialNumberMonitor) {
+            super.initCacheIfNeeded();
+            CMS.debug("CertificateRepository: getNextSerialNumber  mEnableRandomSerialNumbers="+mEnableRandomSerialNumbers);
+
+            if (mEnableRandomSerialNumbers) {
+                int i = 0;
+                do {
+                    if (i > 0) {
+                        CMS.debug("CertificateRepository: getNextSerialNumber  regenerating serial number");
+                    }
+                    randomNumber = getRandomNumber();
+                    nextSerialNumber = getRandomSerialNumber(randomNumber);
+                    nextSerialNumber = checkSerialNumbers(randomNumber, nextSerialNumber);
+                    i++;
+                } while (nextSerialNumber == null && i < mMaxCollisionRecoveryRegenerations);
+
+                if (nextSerialNumber == null) {
+                    CMS.debug("CertificateRepository: in getNextSerialNumber  nextSerialNumber is null");
+                    throw new EBaseException( "nextSerialNumber is null" );
+                }
+
+                if (mCounter.compareTo(BigInteger.ZERO) >= 0 &&
+                    mMinSerialNo != null && mMaxSerialNo != null &&
+                    nextSerialNumber != null &&
+                    nextSerialNumber.compareTo(mMinSerialNo) >= 0 &&
+                    nextSerialNumber.compareTo(mMaxSerialNo) <= 0) {
+                    mCounter = mCounter.add(BigInteger.ONE);
+                }
+                CMS.debug("CertificateRepository: getNextSerialNumber  nextSerialNumber="+
+                          nextSerialNumber+"  mCounter="+mCounter);
+
+                super.checkRange();
+            } else {
+                nextSerialNumber = super.getNextSerialNumber();
+            }
+        }
+
+        return nextSerialNumber; 
+    }
+
+    private void updateCounter() {
+        CMS.debug("CertificateRepository: updateCounter  mEnableRandomSerialNumbers="+
+                  mEnableRandomSerialNumbers+"  mCounter="+mCounter);
+        try {
+            super.initCacheIfNeeded();
+        } catch (Exception e) {
+            CMS.debug("CertificateRepository: updateCounter  Exception from initCacheIfNeeded: "+e.getMessage());
+        }
+
+        String crMode = mDBService.getEntryAttribute(mBaseDN, IRepositoryRecord.ATTR_DESCRIPTION, "", null);
+
+        boolean modeChange = (mEnableRandomSerialNumbers && crMode != null && crMode.equals(PROP_SEQUENTIAL_MODE)) ||
+                             ((!mEnableRandomSerialNumbers) && crMode != null && crMode.equals(PROP_RANDOM_MODE));
+        CMS.debug("CertificateRepository: updateCounter  mEnableRandomSerialNumbers="+mEnableRandomSerialNumbers);
+        CMS.debug("CertificateRepository: updateCounter  CertificateRepositoryMode ="+crMode);
+        CMS.debug("CertificateRepository: updateCounter  modeChange="+modeChange);
+        if (modeChange) {
+            if (mForceModeChange) {
+                setEnableRandomSerialNumbers(mEnableRandomSerialNumbers, true, mForceModeChange);
+            } else {
+                setEnableRandomSerialNumbers(!mEnableRandomSerialNumbers, false, mForceModeChange);
+            }
+        } else if (mEnableRandomSerialNumbers && mCounter != null &&
+                   mCounter.compareTo(BigInteger.ZERO) >= 0) {
+            long t = System.currentTimeMillis();
+            mDBConfig.putString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, mCounter.toString()+","+t);
+            try {
+                CMS.getConfigStore().commit(false);
+            } catch (Exception e) {
+                CMS.debug("CertificateRepository: updateCounter  Exception committing ConfigStore="+e.getMessage());
+            }
+        }
+        CMS.debug("CertificateRepository: UpdateCounter  mEnableRandomSerialNumbers="+
+                  mEnableRandomSerialNumbers+"  mCounter="+mCounter);
+    }
+
+    private BigInteger getInRangeCount(String fromTime, BigInteger  minSerialNo, BigInteger maxSerialNo)
+    throws EBaseException {
+        BigInteger count = BigInteger.ZERO;
+        String filter = null;
+
+        if (fromTime != null && fromTime.length() > 0) {
+            filter = "(certCreateTime >= "+fromTime+")";
+        } else {
+            filter = "(&("+ICertRecord.ATTR_ID+">="+minSerialNo+")("+
+                           ICertRecord.ATTR_ID+"<="+maxSerialNo+"))";
+        }
+        CMS.debug("CertificateRepository: getInRangeCount  filter="+filter+
+                  "  minSerialNo="+minSerialNo+"  maxSerialNo="+maxSerialNo);
+
+        Enumeration<Object> e = findCertRecs(filter, new String[] {ICertRecord.ATTR_ID, "objectclass"});
+        while (e != null && e.hasMoreElements()) {
+            ICertRecord rec = (ICertRecord) e.nextElement();
+            if (rec != null) {
+                BigInteger sn = rec.getSerialNumber();
+                if (fromTime == null || fromTime.length() == 0 ||
+                    (minSerialNo != null && maxSerialNo != null &&
+                     sn != null && sn.compareTo(minSerialNo) >= 0 &&
+                     sn.compareTo(maxSerialNo) <= 0)) {
+                    count = count.add(BigInteger.ONE);
+                }
+            }
+        }
+        CMS.debug("CertificateRepository: getInRangeCount  count=" + count);
+
+        return count; 
+    }
+
+    private BigInteger getInRangeCounter(BigInteger  minSerialNo, BigInteger maxSerialNo)
+    throws EBaseException {
+        String c = null;
+        String t = null;
+        String s = (mDBConfig.getString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, "-1")).trim();
+        CMS.debug("CertificateRepository: getInRangeCounter:  saved counter string="+s);
+        int i = s.indexOf(',');
+        int n = s.length();
+        if (i > -1) {
+            if (i > 0) {
+                c = s.substring(0, i);
+                if (i < n) {
+                    t = s.substring(i+1);
+                }
+            } else {
+                c = "-1";
+            }
+        } else {
+            c = s;
+        }
+        CMS.debug("CertificateRepository: getInRangeCounter:  c=" + c + ((t != null)?("  t="+t):"null"));
+
+        BigInteger counter = new BigInteger(c);
+        BigInteger count = BigInteger.ZERO;
+        if (CMS.isPreOpMode()) {
+            CMS.debug("CertificateRepository: getInRangeCounter:  CMS.isPreOpMode");
+            counter = new BigInteger("-2");
+            mDBConfig.putString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, "-2");
+            try {
+                CMS.getConfigStore().commit(false);
+            } catch (Exception e) {
+                CMS.debug("CertificateRepository: updateCounter  Exception committing ConfigStore="+e.getMessage());
+            }
+        } else if (t != null) {
+            count = getInRangeCount(t, minSerialNo, maxSerialNo);
+            if (count.compareTo(BigInteger.ZERO) > 0) {
+                counter = counter.add(count);
+            }
+        } else if (s.equals("-2")) {
+            count = getInRangeCount(t, minSerialNo, maxSerialNo);
+            if (count.compareTo(BigInteger.ZERO) >= 0) {
+                counter = count;
+            }
+        }
+        CMS.debug("CertificateRepository: getInRangeCounter:  counter=" + counter);
+
+        return counter; 
+    }
+
     public BigInteger getLastSerialNumberInRange(BigInteger serial_low_bound, BigInteger serial_upper_bound)
             throws EBaseException {
 
@@ -114,7 +426,43 @@ public class CertificateRepository extends Repository
 
         }
 
-        String ldapfilter = "(" + "certstatus" + "=*" + ")";
+        mEnableRandomSerialNumbers = mDBConfig.getBoolean(PROP_ENABLE_RANDOM_SERIAL_NUMBERS, false);
+        mForceModeChange = mDBConfig.getBoolean(PROP_FORCE_MODE_CHANGE, false);
+        String crMode = mDBService.getEntryAttribute(mBaseDN, IRepositoryRecord.ATTR_DESCRIPTION, "", null);
+        mMinRandomBitLength = mDBConfig.getInteger(PROP_MINIMUM_RANDOM_BITS, 4);
+        mMaxCollisionRecoverySteps = mDBConfig.getInteger(PROP_COLLISION_RECOVERY_STEPS, 10);
+        mMaxCollisionRecoveryRegenerations = mDBConfig.getInteger(PROP_COLLISION_RECOVERY_REGENERATIONS, 3);
+        boolean modeChange = (mEnableRandomSerialNumbers && crMode != null && crMode.equals(PROP_SEQUENTIAL_MODE)) ||
+                             ((!mEnableRandomSerialNumbers) && crMode != null && crMode.equals(PROP_RANDOM_MODE));
+        CMS.debug("CertificateRepository: getLastSerialNumberInRange"+
+                  "  mEnableRandomSerialNumbers="+mEnableRandomSerialNumbers+
+                  "  mMinRandomBitLength="+mMinRandomBitLength+
+                  "  CollisionRecovery="+mMaxCollisionRecoveryRegenerations+","+mMaxCollisionRecoverySteps);
+        CMS.debug("CertificateRepository: getLastSerialNumberInRange  modeChange="+modeChange+
+                  "  mForceModeChange="+mForceModeChange+((crMode != null)?("  mode="+crMode):""));
+        if (modeChange) {
+            if (mForceModeChange) {
+                setCertificateRepositoryMode((mEnableRandomSerialNumbers)? PROP_RANDOM_MODE: PROP_SEQUENTIAL_MODE);
+                mForceModeChange = false;
+                mDBConfig.remove(PROP_FORCE_MODE_CHANGE);
+            } else {
+                mEnableRandomSerialNumbers = !mEnableRandomSerialNumbers;
+                mDBConfig.putBoolean(PROP_ENABLE_RANDOM_SERIAL_NUMBERS, mEnableRandomSerialNumbers);
+            }
+        }  
+        if (mEnableRandomSerialNumbers && mCounter == null) {
+            mCounter = getInRangeCounter(serial_low_bound, serial_upper_bound);
+        } else {
+            mCounter = BI_MINUS_ONE;
+        }
+        mDBConfig.putString(PROP_RANDOM_SERIAL_NUMBER_COUNTER, mCounter.toString());
+        try {
+            CMS.getConfigStore().commit(false);
+        } catch (Exception e) {
+        }
+        CMS.debug("CertificateRepository: getLastSerialNumberInRange  mEnableRandomSerialNumbers="+mEnableRandomSerialNumbers);
+
+        String ldapfilter = "("+ICertRecord.ATTR_CERT_STATUS+"=*"+")";
 
         String[] attrs = null;
 
@@ -130,7 +478,7 @@ public class CertificateRepository extends Repository
 
             BigInteger ret = new BigInteger(serial_low_bound.toString(10));
 
-            ret = ret.add(new BigInteger("-1"));
+            ret = ret.subtract(BigInteger.ONE); 
             CMS.debug("CertificateRepository:getLastCertRecordSerialNo: returning " + ret);
             return ret;
         }
@@ -156,6 +504,10 @@ public class CertificateRepository extends Repository
                 if (((serial.compareTo(serial_low_bound) == 0) || (serial.compareTo(serial_low_bound) == 1)) &&
                         ((serial.compareTo(serial_upper_bound) == 0) || (serial.compareTo(serial_upper_bound) == -1))) {
                     CMS.debug("getLastSerialNumberInRange returning: " + serial);
+                    if (modeChange && mEnableRandomSerialNumbers) {
+                        mCounter = serial.subtract(serial_low_bound).add(BigInteger.ONE);
+                        CMS.debug("getLastSerialNumberInRange mCounter: " + mCounter);
+                    }
                     return serial;
                 }
             } else {
@@ -165,9 +517,13 @@ public class CertificateRepository extends Repository
 
         BigInteger ret = new BigInteger(serial_low_bound.toString(10));
 
-        ret = ret.add(new BigInteger("-1"));
+        ret = ret.subtract(BigInteger.ONE); 
 
         CMS.debug("CertificateRepository:getLastCertRecordSerialNo: returning " + ret);
+        if (modeChange && mEnableRandomSerialNumbers) {
+            mCounter = BigInteger.ZERO;
+            CMS.debug("getLastSerialNumberInRange mCounter: " + mCounter);
+        }
         return ret;
 
     }
@@ -275,6 +631,7 @@ public class CertificateRepository extends Repository
         transitRevokedExpiredCertificates();
         CMS.getLogger().log(ILogger.EV_SYSTEM, ILogger.S_OTHER,
                 CMS.getLogMessage("CMSCORE_DBS_FINISH_REVOKED_EXPIRED_SEARCH"));
+        updateCounter();
     }
 
     /**
@@ -646,6 +1003,50 @@ public class CertificateRepository extends Repository
         return rec;
     }
 
+    public boolean checkCertificateRecord(BigInteger serialNo)
+        throws EBaseException {
+        IDBSSession s = mDBService.createSession();
+        CertRecord rec = null;
+        boolean exists = true;
+
+        try {
+            String name = "cn" + "=" +
+                serialNo.toString() + "," + getDN();
+            String attrs[] = { "DN" };
+
+            rec = (CertRecord) s.read(name, attrs);
+            if (rec == null) exists = false;
+        } catch (EDBRecordNotFoundException e) {
+            exists = false;
+        } catch (Exception e) {
+            throw new EBaseException(e.getMessage());
+        } finally {
+            if (s != null) 
+                s.close();
+        }
+        return exists;
+    }
+
+    private void setCertificateRepositoryMode(String mode) {
+        IDBSSession s = null;
+
+        CMS.debug("CertificateRepository: setCertificateRepositoryMode   setting mode: "+mode);
+        try {
+            s = mDBService.createSession();
+            ModificationSet mods = new ModificationSet();
+            String name = getDN();
+            mods.add(IRepositoryRecord.ATTR_DESCRIPTION, Modification.MOD_REPLACE, mode);
+            s.modify(name, mods);
+        } catch (Exception e) {
+            CMS.debug("CertificateRepository: setCertificateRepositoryMode   Exception: "+e.getMessage());
+        }
+        try {
+            if (s != null) s.close();
+        } catch (Exception e) {
+            CMS.debug("CertificateRepository: setCertificateRepositoryMode   Exception: "+e.getMessage());
+        }
+    }
+
     public synchronized void modifyCertificateRecord(BigInteger serialNo,
             ModificationSet mods) throws EBaseException {
         IDBSSession s = mDBService.createSession();
@@ -1195,7 +1596,7 @@ public class CertificateRepository extends Repository
             String fromVal = "0";
             try {
                 if (from != null) {
-                    Integer.parseInt(from);
+                    new BigInteger(from);
                     fromVal = from;
                 }
             } catch (Exception e1) {
diff --git a/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java b/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java
index 0824cc9..be674bf 100644
--- a/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java
+++ b/base/common/src/com/netscape/cmscore/dbs/DBSubsystem.java
@@ -138,8 +138,6 @@ public class DBSubsystem implements IDBSubsystem {
     private static final String PROP_INCREMENT_NAME = "increment_name";
     private static final String PROP_RANGE_DN = "rangeDN";
 
-    private static final BigInteger BI_ONE = new BigInteger("1");
-
     private ILogger mLogger = null;
 
     // singleton enforcement
@@ -424,7 +422,7 @@ public class DBSubsystem implements IDBSubsystem {
             conn.modify(dn, mods);
 
             // Add new range object
-            String endRange = nextRangeNo.add(incrementNo).subtract(BI_ONE).toString();
+            String endRange = nextRangeNo.add(incrementNo).subtract(BigInteger.ONE).toString();
             LDAPAttributeSet attrs = new LDAPAttributeSet();
             attrs.add(new LDAPAttribute("objectClass", "top"));
             attrs.add(new LDAPAttribute("objectClass", "pkiRange"));
@@ -436,6 +434,8 @@ public class DBSubsystem implements IDBSubsystem {
             String dn2 = "cn=" + nextRange + "," + rangeDN;
             LDAPEntry rangeEntry = new LDAPEntry(dn2, attrs);
             conn.add(rangeEntry);
+            CMS.debug("DBSubsystem: getNextRange  Next range has been added: " +
+                      nextRange + " - " + endRange);
         } catch (Exception e) {
             CMS.debug("DBSubsystem: getNextRange. Unable to provide next range :" + e);
             e.printStackTrace();
@@ -531,6 +531,7 @@ public class DBSubsystem implements IDBSubsystem {
                     PROP_NEXT_SERIAL_NUMBER, "0"), 16);
 
             mEnableSerialMgmt = mDBConfig.getBoolean(PROP_ENABLE_SERIAL_MGMT, false);
+            CMS.debug("DBSubsystem: init()  mEnableSerialMgmt="+mEnableSerialMgmt);
 
             // populate the certs hash entry
             Hashtable<String, String> certs = new Hashtable<String, String>();
@@ -783,6 +784,10 @@ public class DBSubsystem implements IDBSubsystem {
                 reg.registerAttribute(IRepositoryRecord.ATTR_PUB_STATUS,
                         new StringMapper(RepositorySchema.LDAP_ATTR_PUB_STATUS));
             }
+            if (!reg.isAttributeRegistered(IRepositoryRecord.ATTR_DESCRIPTION)) {
+                reg.registerAttribute(IRepositoryRecord.ATTR_DESCRIPTION,
+                        new StringMapper(RepositorySchema.LDAP_ATTR_DESCRIPTION));
+            }
 
         } catch (EBaseException e) {
             if (CMS.isPreOpMode())
@@ -791,6 +796,47 @@ public class DBSubsystem implements IDBSubsystem {
         }
     }
 
+    public String getEntryAttribute(String dn, String attrName,
+                                    String defaultValue, String errorValue) {
+        LDAPConnection conn = null;
+        String attrValue = null;
+        try {
+            conn = mLdapConnFactory.getConn();
+            String[] attrs = { attrName };
+            LDAPEntry entry = conn.read(dn, attrs);
+            if (entry != null) {
+                LDAPAttribute attr =  entry.getAttribute(attrName);
+                if (attr != null) {
+                    attrValue = (String) attr.getStringValues().nextElement();
+                } else {
+                    attrValue = defaultValue;
+                }
+            } else {
+                attrValue = errorValue;
+            }
+        } catch (LDAPException e) {
+            CMS.debug("DBSubsystem: getEntryAttribute  LDAPException  code="+e.getLDAPResultCode());
+            if (e.getLDAPResultCode() == LDAPException.NO_SUCH_OBJECT) {
+                attrValue = defaultValue;
+            }
+        } catch (Exception e) {
+            CMS.debug("DBSubsystem: getEntryAttribute. Unable to retrieve '"+attrName+"': "+ e);
+            attrValue = errorValue;
+        } finally {
+            try {
+                if ((conn != null) && (mLdapConnFactory != null)) {
+                    CMS.debug("Releasing ldap connection");
+                    mLdapConnFactory.returnConn(conn);
+                }
+            } catch (Exception e) {
+                CMS.debug("Error releasing the ldap connection" + e.toString());
+            }
+        }
+        CMS.debug("DBSubsystem: getEntryAttribute:  dn="+dn+"  attr="+attrName+":"+attrValue+";");
+
+        return attrValue;
+    }
+
     /**
      * Starts up this service.
      */
@@ -798,13 +844,20 @@ public class DBSubsystem implements IDBSubsystem {
     }
 
     /**
-     * Retrieves configuration store.
+     * Retrieves internal DB configuration store.
      */
     public IConfigStore getConfigStore() {
         return mConfig;
     }
 
     /**
+     * Retrieves DB subsystem configuration store.
+     */
+    public IConfigStore getDBConfigStore() {
+        return mDBConfig;
+    }
+
+    /**
      * Retrieves base DN of backend database.
      */
     public String getBaseDN() {
diff --git a/base/common/src/com/netscape/cmscore/dbs/Repository.java b/base/common/src/com/netscape/cmscore/dbs/Repository.java
index 57ac500..e6b6e83 100644
--- a/base/common/src/com/netscape/cmscore/dbs/Repository.java
+++ b/base/common/src/com/netscape/cmscore/dbs/Repository.java
@@ -49,7 +49,6 @@ import com.netscape.certsrv.dbs.repository.IRepositoryRecord;
 
 public abstract class Repository implements IRepository {
 
-    private static final BigInteger BI_ONE = new BigInteger("1");
     private BigInteger BI_INCREMENT = null;
     // (the next serialNo to be issued) - 1
     private BigInteger mSerialNo = null;
@@ -61,8 +60,10 @@ public abstract class Repository implements IRepository {
     private String mNextMaxSerial = null;
     private String mNextMinSerial = null;
 
-    private BigInteger mMinSerialNo = null;
-    private BigInteger mMaxSerialNo = null;
+    protected boolean mEnableRandomSerialNumbers = false;
+    protected BigInteger mCounter = null;
+    protected BigInteger mMinSerialNo = null;
+    protected BigInteger mMaxSerialNo = null;
     private BigInteger mNextMinSerialNo = null;
     private BigInteger mNextMaxSerialNo = null;
 
@@ -149,6 +150,7 @@ public abstract class Repository implements IRepository {
         }
 
         BigInteger serial = rec.getSerialNumber();
+        CMS.debug("Repository: getSerialNumber  serial="+serial);
 
         if (!mInit) {
             // cms may crash after issue a cert but before update
@@ -158,7 +160,7 @@ public abstract class Repository implements IRepository {
                         serial + "," + mBaseDN);
 
                 if (obj != null) {
-                    serial = serial.add(BI_ONE);
+                    serial = serial.add(BigInteger.ONE);
                     setSerialNumber(serial);
                 }
             } catch (EBaseException e) {
@@ -246,6 +248,10 @@ public abstract class Repository implements IRepository {
         return mMinSerial;
     }
 
+    protected void setLastSerialNo(BigInteger lastSN) {
+        mLastSerialNo = lastSN;
+    }
+
     /**
      * init serial number cache
      */
@@ -281,7 +287,10 @@ public abstract class Repository implements IRepository {
         String increment = mDB.getIncrementConfig(mRepo);
         String lowWaterMark = mDB.getLowWaterMarkConfig(mRepo);
 
-        CMS.debug("Repository: minSerial " + mMinSerial + " maxSerial: " + mMaxSerial);
+        CMS.debug("Repository: minSerial:" + mMinSerial + " maxSerial: " + mMaxSerial);
+        CMS.debug("Repository: nextMinSerial: " + ((mNextMinSerial == null)?"":mNextMinSerial) +
+                             " nextMaxSerial: " + ((mNextMaxSerial == null)?"":mNextMaxSerial));
+        CMS.debug("Repository: increment:" + increment + " lowWaterMark: " + lowWaterMark);
 
         if (mMinSerial != null)
             mMinSerialNo = new BigInteger(mMinSerial, mRadix);
@@ -317,6 +326,11 @@ public abstract class Repository implements IRepository {
 
     }
 
+    protected void initCacheIfNeeded() throws EBaseException {
+        if (mLastSerialNo == null) 
+            initCache();
+    }
+
     /**
      * get the next serial number in cache
      */
@@ -325,7 +339,7 @@ public abstract class Repository implements IRepository {
         CMS.debug("Repository:In getTheSerialNumber ");
         if (mLastSerialNo == null)
             initCache();
-        BigInteger serial = new BigInteger((mLastSerialNo.add(BI_ONE)).toString());
+        BigInteger serial = mLastSerialNo.add(BigInteger.ONE);
 
         if (mMaxSerialNo != null && serial.compareTo(mMaxSerialNo) > 0)
             return null;
@@ -354,7 +368,7 @@ public abstract class Repository implements IRepository {
         // < BI_INCREMENT and server restart right afterwards.
         mDB.setNextSerialConfig(num);
 
-        mSerialNo = num.subtract(BI_ONE);
+        mSerialNo = num.subtract(BigInteger.ONE);
         mNext = num.add(BI_INCREMENT);
         setSerialNumber(mNext);
     }
@@ -373,36 +387,65 @@ public abstract class Repository implements IRepository {
 
         if (mLastSerialNo == null) {
             initCache();
-
-            mLastSerialNo = mLastSerialNo.add(BI_ONE);
-
-        } else {
-            mLastSerialNo = mLastSerialNo.add(BI_ONE);
         }
-
         if (mLastSerialNo == null) {
             CMS.debug("Repository::getNextSerialNumber() " +
                        "- mLastSerialNo is null!");
             throw new EBaseException("mLastSerialNo is null");
         }
 
+        mLastSerialNo = mLastSerialNo.add(BigInteger.ONE);
+
+        checkRange();
+
+        BigInteger retSerial = new BigInteger(mLastSerialNo.toString());
+
+        CMS.debug("Repository: getNextSerialNumber: returning retSerial " + retSerial);
+        return retSerial; 
+    }
+
+    /**
+     * Checks to see if range needs to be switched.
+     *      
+     * @exception EBaseException thrown when next range is not allocated
+     */
+    protected void checkRange() throws EBaseException 
+    {
         // check if we have reached the end of the range
         // if so, move to next range
-        if (mLastSerialNo.compareTo(mMaxSerialNo) > 0) {
+        BigInteger randomLimit = null;
+        BigInteger rangeLength = null;
+        if ((this instanceof ICertificateRepository) &&
+            mDB.getEnableSerialMgmt() && mEnableRandomSerialNumbers) {
+            rangeLength = mMaxSerialNo.subtract(mMinSerialNo).add(BigInteger.ONE);
+            randomLimit = rangeLength.subtract(mLowWaterMarkNo.shiftRight(1));
+            CMS.debug("Repository: checkRange  rangeLength="+rangeLength);
+            CMS.debug("Repository: checkRange  randomLimit="+randomLimit);
+        }
+        CMS.debug("Repository: checkRange  mLastSerialNo="+mLastSerialNo);
+        if (mLastSerialNo.compareTo( mMaxSerialNo ) > 0 ||
+            ((!CMS.isPreOpMode()) && randomLimit != null && mCounter.compareTo(randomLimit) > 0)) {
+
             if (mDB.getEnableSerialMgmt()) {
                 CMS.debug("Reached the end of the range.  Attempting to move to next range");
+                if ((mNextMinSerialNo == null) || (mNextMaxSerialNo == null)) {
+                    if (rangeLength != null && mCounter.compareTo(rangeLength) < 0) {
+                        return;
+                    } else {
+                        throw new EDBException(CMS.getUserMessage("CMS_DBS_LIMIT_REACHED",
+                                                                  mLastSerialNo.toString()));
+                    }
+                }
                 mMinSerialNo = mNextMinSerialNo;
                 mMaxSerialNo = mNextMaxSerialNo;
-                mNextMinSerialNo = null;
-                mNextMaxSerialNo = null;
-                if ((mMaxSerialNo == null) || (mMinSerialNo == null)) {
-                    throw new EDBException(CMS.getUserMessage("CMS_DBS_LIMIT_REACHED",
-                            mLastSerialNo.toString()));
-                }
                 mLastSerialNo = mMinSerialNo;
+                mNextMinSerialNo  = null;
+                mNextMaxSerialNo  = null;
+                mCounter = BigInteger.ZERO;
+
                 // persist the changes
-                mDB.setMinSerialConfig(mRepo, mMinSerialNo.toString());
-                mDB.setMaxSerialConfig(mRepo, mMaxSerialNo.toString());
+                mDB.setMinSerialConfig(mRepo, mMinSerialNo.toString(mRadix));
+                mDB.setMaxSerialConfig(mRepo, mMaxSerialNo.toString(mRadix));
                 mDB.setNextMinSerialConfig(mRepo, null);
                 mDB.setNextMaxSerialConfig(mRepo, null);
             } else {
@@ -410,11 +453,6 @@ public abstract class Repository implements IRepository {
                         mLastSerialNo.toString()));
             }
         }
-
-        BigInteger retSerial = new BigInteger(mLastSerialNo.toString());
-
-        CMS.debug("Repository: getNextSerialNumber: returning retSerial " + retSerial);
-        return retSerial;
     }
 
     /**
@@ -436,13 +474,19 @@ public abstract class Repository implements IRepository {
         if (mLastSerialNo == null)
             initCache();
 
-        BigInteger numsInRange = mMaxSerialNo.subtract(mLastSerialNo);
+        BigInteger numsInRange = null;
+        if ((this instanceof ICertificateRepository) &&
+            mDB.getEnableSerialMgmt() && mEnableRandomSerialNumbers) {
+            numsInRange = (mMaxSerialNo.subtract(mMinSerialNo)).subtract(mCounter);
+        } else {
+            numsInRange = mMaxSerialNo.subtract(mLastSerialNo);
+        }
         BigInteger numsInNextRange = null;
         BigInteger numsAvail = null;
         CMS.debug("Serial numbers left in range: " + numsInRange.toString());
         CMS.debug("Last Serial Number: " + mLastSerialNo.toString());
         if ((mNextMaxSerialNo != null) && (mNextMinSerialNo != null)) {
-            numsInNextRange = mNextMaxSerialNo.subtract(mNextMinSerialNo);
+            numsInNextRange = mNextMaxSerialNo.subtract(mNextMinSerialNo).add(BigInteger.ONE);
             numsAvail = numsInRange.add(numsInNextRange);
             CMS.debug("Serial Numbers in next range: " + numsInNextRange.toString());
             CMS.debug("Serial Numbers available: " + numsAvail.toString());
@@ -458,7 +502,7 @@ public abstract class Repository implements IRepository {
                 CMS.debug("Next Range not available");
             } else {
                 CMS.debug("nNextMinSerialNo has been set to " + mNextMinSerialNo.toString(mRadix));
-                mNextMaxSerialNo = mNextMinSerialNo.add(mIncrementNo);
+                mNextMaxSerialNo = mNextMinSerialNo.add(mIncrementNo).subtract(BigInteger.ONE);
                 numsAvail = numsAvail.add(mIncrementNo);
                 mDB.setNextMinSerialConfig(mRepo, mNextMinSerialNo.toString(mRadix));
                 mDB.setNextMaxSerialConfig(mRepo, mNextMaxSerialNo.toString(mRadix));
diff --git a/base/common/src/com/netscape/cmscore/dbs/RepositoryRecord.java b/base/common/src/com/netscape/cmscore/dbs/RepositoryRecord.java
index 36d5ce9..a268f68 100644
--- a/base/common/src/com/netscape/cmscore/dbs/RepositoryRecord.java
+++ b/base/common/src/com/netscape/cmscore/dbs/RepositoryRecord.java
@@ -40,11 +40,13 @@ public class RepositoryRecord implements IRepositoryRecord {
     private static final long serialVersionUID = 1648450747848783853L;
     private BigInteger mSerialNo = null;
     private String mPublishingStatus = null;
+    private String mDescription = null;
 
     protected static Vector<String> mNames = new Vector<String>();
     static {
         mNames.addElement(IRepositoryRecord.ATTR_SERIALNO);
         mNames.addElement(IRepositoryRecord.ATTR_PUB_STATUS);
+        mNames.addElement(IRepositoryRecord.ATTR_DESCRIPTION);
     }
 
     /**
@@ -62,6 +64,8 @@ public class RepositoryRecord implements IRepositoryRecord {
             mSerialNo = (BigInteger) obj;
         } else if (name.equalsIgnoreCase(IRepositoryRecord.ATTR_PUB_STATUS)) {
             mPublishingStatus = (String) obj;
+        } else if (name.equalsIgnoreCase(IRepositoryRecord.ATTR_DESCRIPTION)) {
+            mDescription = (String) obj;
         } else {
             throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", name));
         }
@@ -75,6 +79,8 @@ public class RepositoryRecord implements IRepositoryRecord {
             return mSerialNo;
         } else if (name.equalsIgnoreCase(IRepositoryRecord.ATTR_PUB_STATUS)) {
             return mPublishingStatus;
+        } else if (name.equalsIgnoreCase(IRepositoryRecord.ATTR_DESCRIPTION)) {
+            return mDescription;
         } else {
             throw new EBaseException(CMS.getUserMessage("CMS_BASE_INVALID_ATTRIBUTE", name));
         }
@@ -108,4 +114,8 @@ public class RepositoryRecord implements IRepositoryRecord {
     public String getPublishingStatus() {
         return mPublishingStatus;
     }
+
+    public String getDescription() {
+        return mDescription;
+    }
 }
diff --git a/base/common/src/com/netscape/cmscore/dbs/RepositorySchema.java b/base/common/src/com/netscape/cmscore/dbs/RepositorySchema.java
index 4ec8da6..5dfc555 100644
--- a/base/common/src/com/netscape/cmscore/dbs/RepositorySchema.java
+++ b/base/common/src/com/netscape/cmscore/dbs/RepositorySchema.java
@@ -31,4 +31,5 @@ public class RepositorySchema {
     public static final String LDAP_OC_REPOSITORY = "repository";
     public static final String LDAP_ATTR_SERIALNO = "serialno";
     public static final String LDAP_ATTR_PUB_STATUS = "publishingStatus";
+    public final static String LDAP_ATTR_DESCRIPTION = "description";
 }
diff --git a/base/common/test/com/netscape/cmscore/dbs/DBSubsystemDefaultStub.java b/base/common/test/com/netscape/cmscore/dbs/DBSubsystemDefaultStub.java
index 396121b..9b22213 100644
--- a/base/common/test/com/netscape/cmscore/dbs/DBSubsystemDefaultStub.java
+++ b/base/common/test/com/netscape/cmscore/dbs/DBSubsystemDefaultStub.java
@@ -168,4 +168,17 @@ public class DBSubsystemDefaultStub implements IDBSubsystem {
         // TODO Auto-generated method stub
 
     }
+
+    @Override
+    public IConfigStore getDBConfigStore() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public String getEntryAttribute(String dn, String attrName,
+                                    String defaultValue, String errorValue) {
+        // TODO Auto-generated method stub
+        return null;
+    }
 }
diff --git a/base/console/src/com/netscape/admin/certsrv/config/CMSCAGeneralPanel.java b/base/console/src/com/netscape/admin/certsrv/config/CMSCAGeneralPanel.java
index bee66d6..4c89196 100644
--- a/base/console/src/com/netscape/admin/certsrv/config/CMSCAGeneralPanel.java
+++ b/base/console/src/com/netscape/admin/certsrv/config/CMSCAGeneralPanel.java
@@ -47,6 +47,8 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
     private JTextField mSerialNumber;
     private JTextField mMaxSerialNumber;
     private JCheckBox mValidity;
+    private JCheckBox mEnableSerialNumberManagement;
+    private JCheckBox mEnableRandomSerialNumbers;
     private Vector mGroupData;
     private static final String HELPINDEX =
       "configuration-ca-general-help";
@@ -189,49 +191,86 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
         gb2.setConstraints(dummy1, gbc);
         signingPanel.add(dummy1);
 
+        // add serial number management
+        CMSAdminUtil.resetGBC(gbc);
+        mEnableSerialNumberManagement = makeJCheckBox("MANAGEMENT");
+        //mEnableSerialNumberManagement.setEnabled(false);
+        gbc.anchor = gbc.CENTER;
+        gbc.gridwidth = gbc.REMAINDER;
+        gbc.gridheight = 1;
+        gbc.weightx = 1.0;
+        gbc.weighty = 1.0;
+        gbc.gridx = 0;
+        gbc.gridy = 0;
+        gb3.setConstraints(mEnableSerialNumberManagement, gbc);
+        serialPanel.add(mEnableSerialNumberManagement);
+
+        // add random serial numbers
+        CMSAdminUtil.resetGBC(gbc);
+        mEnableRandomSerialNumbers = makeJCheckBox("RANDOM");
+        gbc.anchor = gbc.CENTER;
+        gbc.gridwidth = gbc.REMAINDER;
+        gbc.gridheight = gbc.REMAINDER; //1;
+        gbc.weightx = 1.0;
+        gbc.weighty = 1.0;
+        gbc.gridx = 0;
+        gbc.gridy = 1;
+        gb3.setConstraints(mEnableRandomSerialNumbers, gbc);
+        serialPanel.add(mEnableRandomSerialNumbers);
+
         // add serial number block
         CMSAdminUtil.resetGBC(gbc);
         JLabel serialLabel = makeJLabel("SERIAL");
+        serialLabel.setEnabled(false);
         gbc.anchor = gbc.CENTER;
         gb3.setConstraints(serialLabel, gbc);
+        gbc.gridwidth = 1;
+        gbc.gridheight = 1;
+        gbc.weightx = 0.0;
         gbc.weighty = 1.0;
-        //gbc.insets = new Insets(COMPONENT_SPACE,0,COMPONENT_SPACE,0);
-        serialPanel.add(serialLabel);
+        gbc.gridx = 0;
+        gbc.gridy = 2;
+        //serialPanel.add(serialLabel);
 
         CMSAdminUtil.resetGBC(gbc);
         mSerialNumber = makeJTextField(17);
         mSerialNumber.setEnabled(false);
         gbc.anchor = gbc.NORTHWEST;
-        //gbc.gridwidth = gbc.REMAINDER;
-        //gbc.gridheight = gbc.REMAINDER;
-        //gbc.weightx = 1.0;
+        gbc.gridwidth = 1;
+        gbc.gridheight = 1;
+        gbc.weightx = 0.0;
         gbc.weighty = 1.0;
+        gbc.gridx = 1;
+        gbc.gridy = 2;
         gb3.setConstraints(mSerialNumber, gbc);
-        serialPanel.add(mSerialNumber);
+        //serialPanel.add(mSerialNumber);
 
         // add end serial number block
         CMSAdminUtil.resetGBC(gbc);
         JLabel maxSerialLabel = makeJLabel("MAXSERIAL");
-        gbc.anchor = gbc.EAST;
-        //gbc.insets = new Insets(COMPONENT_SPACE,DIFFERENT_COMPONENT_SPACE,0,0);
-        gbc.weightx = 0.0;
+        maxSerialLabel.setEnabled(false);
+        gbc.anchor = gbc.CENTER;
         gbc.gridwidth = 1;
+        gbc.gridheight = 1;
+        gbc.weightx = 0.0;
+        gbc.weighty = 1.0;
         gbc.gridx = 0;
+        gbc.gridy = 3;
         gb3.setConstraints(maxSerialLabel, gbc);
-        //gbc.weighty = 1.0;
-        serialPanel.add(maxSerialLabel);
+        //serialPanel.add(maxSerialLabel);
 
         CMSAdminUtil.resetGBC(gbc);
         mMaxSerialNumber = makeJTextField(17);
         mMaxSerialNumber.setEnabled(false);
-        gbc.anchor = gbc.NORTHWEST;
-        gbc.gridy = 1;
-        //gbc.gridwidth = gbc.REMAINDER;
-        //gbc.gridheight = gbc.REMAINDER;
-        //gbc.weightx = 1.0;
+        gbc.anchor = gbc.CENTER;
+        gbc.gridwidth = 1;
+        gbc.gridheight = 1;
+        gbc.weightx = 0.0;
         gbc.weighty = 1.0;
+        gbc.gridx = 1;
+        gbc.gridy = 3;
         gb3.setConstraints(mMaxSerialNumber, gbc);
-        serialPanel.add(mMaxSerialNumber);
+        //serialPanel.add(mMaxSerialNumber);
 
         CMSAdminUtil.resetGBC(gbc);
         JLabel dummy2 = new JLabel(" ");
@@ -249,13 +288,15 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
     public void refresh() {
         mModel.progressStart();
         NameValuePairs nvps = new NameValuePairs();
-        nvps.put(Constants.PR_EE_ENABLED, "");
+        //nvps.put(Constants.PR_EE_ENABLED, "");
         //nvps.add(Constants.PR_RA_ENABLED, "");
         nvps.put(Constants.PR_DEFAULT_ALGORITHM, "");
         nvps.put(Constants.PR_ALL_ALGORITHMS, "");
         nvps.put(Constants.PR_SERIAL, "");
         nvps.put(Constants.PR_MAXSERIAL, "");
         nvps.put(Constants.PR_VALIDITY, "");
+        nvps.put(Constants.PR_SN_MANAGEMENT, "");
+        nvps.put(Constants.PR_RANDOM_SN, "");
 
         try {
             NameValuePairs val = mAdmin.read(DestDef.DEST_CA_ADMIN,
@@ -268,22 +309,27 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
         }
         mModel.progressStop();
         clearDirtyFlag();
+        enableFields();
     }
 
     protected void populate(NameValuePairs nvps) {
         String defaultAlgorithm = "";
         for (String name : nvps.keySet()) {
             String value = nvps.get(name);
+/*
             if (name.equals(Constants.PR_EE_ENABLED)) {
                 mEEEnable.setSelected(getBoolean(value));
             } else if (name.equals(Constants.PR_OCSP_ENABLED)) {
                 mOCSPEnable.setSelected(getBoolean(value));
-/*
             } else if (name.equals(Constants.PR_RA_ENABLED)) {
                 mRAEnable.setSelected(getBoolean(nvp.getValue()));
 */
-            } else if (name.equals(Constants.PR_VALIDITY)) {
+            if (name.equals(Constants.PR_VALIDITY)) {
                 mValidity.setSelected(getBoolean(value));
+            } else if (name.equals(Constants.PR_SN_MANAGEMENT)) {
+                mEnableSerialNumberManagement.setSelected(getBoolean(value));
+            } else if (name.equals(Constants.PR_RANDOM_SN)) {
+                mEnableRandomSerialNumbers.setSelected(getBoolean(value));
             } else if (name.equals(Constants.PR_DEFAULT_ALGORITHM)) {
                 defaultAlgorithm = value;
             } else if (name.equals(Constants.PR_ALL_ALGORITHMS)) {
@@ -321,9 +367,19 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
     }
 
     public void actionPerformed(ActionEvent e) {
+        if (e.getSource().equals(mEnableSerialNumberManagement)) {
+            enableFields();
+        }
         super.actionPerformed(e);
     }
 
+    private void enableFields() {
+        boolean enable = mEnableSerialNumberManagement.isSelected();
+        mEnableRandomSerialNumbers.setEnabled(enable);
+        if (!enable) mEnableRandomSerialNumbers.setSelected(enable);
+        CMSAdminUtil.repaintComp(mEnableRandomSerialNumbers);
+    }
+
     private String hexToDecimal(String hex)
     {
         //String newHex = hex.substring(2);
@@ -338,6 +394,7 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
     public boolean applyCallback() {
         NameValuePairs nvps = new NameValuePairs();
 
+/*
         if (mEEEnable.isSelected())
             nvps.put(Constants.PR_EE_ENABLED, Constants.TRUE);
         else
@@ -348,7 +405,6 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
         else
             nvps.put(Constants.PR_OCSP_ENABLED, Constants.FALSE);
 
-/*
         if (mRAEnable.isSelected())
             nvps.add(Constants.PR_RA_ENABLED, Constants.TRUE);
         else
@@ -360,6 +416,17 @@ public class CMSCAGeneralPanel extends CMSBaseTab implements ItemListener {
         else
             nvps.put(Constants.PR_VALIDITY, Constants.FALSE);
 
+        if (mEnableSerialNumberManagement.isSelected())
+            nvps.put(Constants.PR_SN_MANAGEMENT, Constants.TRUE);
+        else
+            nvps.put(Constants.PR_SN_MANAGEMENT, Constants.FALSE);
+
+        if (mEnableSerialNumberManagement.isSelected() &&
+            mEnableRandomSerialNumbers.isSelected())
+            nvps.put(Constants.PR_RANDOM_SN, Constants.TRUE);
+        else
+            nvps.put(Constants.PR_RANDOM_SN, Constants.FALSE);
+
         nvps.put(Constants.PR_DEFAULT_ALGORITHM,
                 (String) mAlgorithms.getSelectedItem());
 
diff --git a/base/server/config/pkislots.cfg b/base/server/config/pkislots.cfg
index a2a661f..a68b45e 100644
--- a/base/server/config/pkislots.cfg
+++ b/base/server/config/pkislots.cfg
@@ -50,6 +50,7 @@ PKI_EE_SECURE_CLIENT_AUTH_PORT_UI_SLOT=[PKI_EE_SECURE_CLIENT_AUTH_PORT_UI]
 PKI_EE_SECURE_PORT_SLOT=[PKI_EE_SECURE_PORT]
 PKI_EE_SECURE_PORT_CONNECTOR_NAME_SLOT=[PKI_EE_SECURE_PORT_CONNECTOR_NAME]
 PKI_EE_SECURE_PORT_SERVER_COMMENT_SLOT=[PKI_EE_SECURE_PORT_SERVER_COMMENT]
+PKI_ENABLE_RANDOM_SERIAL_NUMBERS=[PKI_ENABLE_RANDOM_SERIAL_NUMBERS]
 PKI_GROUP_SLOT=[PKI_GROUP]
 PKI_INSTANCE_ID_SLOT=[PKI_INSTANCE_ID]
 PKI_INSTANCE_INITSCRIPT_SLOT=[PKI_INSTANCE_INITSCRIPT]
diff --git a/base/server/etc/default.cfg b/base/server/etc/default.cfg
index e848363..21a192c 100644
--- a/base/server/etc/default.cfg
+++ b/base/server/etc/default.cfg
@@ -372,6 +372,7 @@ pki_ocsp_signing_nickname=ocspSigningCert cert-%(pki_instance_name)s CA
 pki_ocsp_signing_signing_algorithm=SHA256withRSA
 pki_ocsp_signing_subject_dn=cn=CA OCSP Signing Certificate,o=%(pki_security_domain_name)s
 pki_ocsp_signing_token=Internal Key Storage Token
+pki_random_serial_numbers_enable=False
 pki_subordinate=False
 pki_admin_email=%(pki_admin_name)s@%(pki_dns_domainname)s
 pki_admin_name=%(pki_admin_uid)s
diff --git a/base/server/src/engine/pkiparser.py b/base/server/src/engine/pkiparser.py
index 2e15376..4fbabe1 100644
--- a/base/server/src/engine/pkiparser.py
+++ b/base/server/src/engine/pkiparser.py
@@ -810,6 +810,10 @@ class PKIConfigParser:
                     "+TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA," +\
                     "+TLS_DHE_RSA_WITH_AES_128_CBC_SHA," +\
                     "+TLS_DHE_RSA_WITH_AES_256_CBC_SHA"
+                if config.pki_master_dict['pki_subsystem'] == "CA":
+                    config.pki_master_dict['PKI_ENABLE_RANDOM_SERIAL_NUMBERS']=\
+                        config.pki_master_dict\
+                        ['pki_random_serial_numbers_enable'].lower()
             # Shared Apache/Tomcat NSS security database name/value pairs
             config.pki_master_dict['pki_shared_pfile'] =\
                 os.path.join(
diff --git a/dogtag/console-ui/src/CMSAdminRS.properties b/dogtag/console-ui/src/CMSAdminRS.properties
index e421049..4cf156b 100644
--- a/dogtag/console-ui/src/CMSAdminRS.properties
+++ b/dogtag/console-ui/src/CMSAdminRS.properties
@@ -387,6 +387,12 @@ CAGENERAL_COMBOBOX_ALGORITHM_VALUE_2=SHA1 with RSA
 CAGENERAL_COMBOBOX_ALGORITHM_VALUE_3=SHA256 with RSA
 CAGENERAL_COMBOBOX_ALGORITHM_VALUE_4=SHA512 with RSA
 CAGENERAL_COMBOBOX_ALGORITHM_VALUE_5=SHA1 with DSA
+CAGENERAL_BORDER_MANAGEMENT_LABEL=Serial Number Management
+CAGENERAL_CHECKBOX_MANAGEMENT_LABEL=Enable serial number management
+CAGENERAL_CHECKBOXL_MANAGEMENT_TTIP=Allow CA to manage serial numbers automatically
+CAGENERAL_BORDER_RANDOM_LABEL=Random Certificate Serial Numbers 
+CAGENERAL_CHECKBOX_RANDOM_LABEL=Enable random certificate serial numbers
+CAGENERAL_CHECKBOXL_RANDOM_TTIP=Allow CA to generate random certificate serial numbers
 CAGENERAL_BORDER_SERIAL_LABEL=Certificate Serial Number
 CAGENERAL_LABEL_SERIAL_LABEL=Next Serial Number: (0x)
 CAGENERAL_LABEL_SERIAL_TTIP=Specify the next serial number of the certificate that the CA issues


More information about the Pki-devel mailing list