Browse Source

centralize simpledateformat access

Jason Rivard 5 years ago
parent
commit
eaedc2cfeb

+ 10 - 11
server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java

@@ -54,23 +54,22 @@ import password.pwm.ldap.search.UserSearchEngine;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.util.FormMap;
 import password.pwm.util.FormMap;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
-import password.pwm.util.password.RandomPasswordGenerator;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.PwmDateFormat;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.operations.ActionExecutor;
 import password.pwm.util.password.PasswordUtility;
 import password.pwm.util.password.PasswordUtility;
+import password.pwm.util.password.RandomPasswordGenerator;
 
 
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.io.IOException;
 import java.text.ParseException;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
@@ -541,10 +540,10 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
 
 
         final String expirationDateStr = pwmRequest.readParameterAsString( HTTP_PARAM_EXPIRATION_DATE );
         final String expirationDateStr = pwmRequest.readParameterAsString( HTTP_PARAM_EXPIRATION_DATE );
 
 
-        final Date expirationDate;
+        final Instant expirationDate;
         try
         try
         {
         {
-            expirationDate = new SimpleDateFormat( "yyyy-MM-dd" ).parse( expirationDateStr );
+            expirationDate = PwmDateFormat.parse( "yyyy-MM-dd", expirationDateStr );
         }
         }
         catch ( final ParseException e )
         catch ( final ParseException e )
         {
         {
@@ -556,7 +555,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
             ) );
             ) );
         }
         }
 
 
-        if ( expirationDate.before( new Date() ) )
+        if ( expirationDate.isBefore( Instant.now() ) )
         {
         {
             final String errorMsg = "expiration date must be in the future";
             final String errorMsg = "expiration date must be in the future";
             throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_FIELD_REQUIRED, errorMsg ) );
             throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_FIELD_REQUIRED, errorMsg ) );
@@ -566,14 +565,14 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
         final long futureDateMs = System.currentTimeMillis() + durationValueMs;
         final long futureDateMs = System.currentTimeMillis() + durationValueMs;
         final Instant futureDate = Instant.ofEpochMilli( futureDateMs );
         final Instant futureDate = Instant.ofEpochMilli( futureDateMs );
 
 
-        if ( expirationDate.after( Date.from( futureDate ) ) )
+        if ( expirationDate.isAfter( futureDate ) )
         {
         {
             final String errorMsg = "expiration date must be sooner than " + futureDate.toString();
             final String errorMsg = "expiration date must be sooner than " + futureDate.toString();
             throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_FIELD_REQUIRED, errorMsg ) );
             throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_FIELD_REQUIRED, errorMsg ) );
         }
         }
 
 
         LOGGER.trace( pwmRequest, () -> "read expiration date as " + expirationDate.toString() );
         LOGGER.trace( pwmRequest, () -> "read expiration date as " + expirationDate.toString() );
-        return expirationDate.toInstant();
+        return expirationDate;
     }
     }
 
 
     private static String determineUserDN( final Map<FormConfiguration, String> formValues, final Configuration config )
     private static String determineUserDN( final Map<FormConfiguration, String> formValues, final Configuration config )
@@ -687,7 +686,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
 
 
     private void calculateFutureDateFlags( final PwmRequest pwmRequest, final GuestRegistrationBean guestRegistrationBean )
     private void calculateFutureDateFlags( final PwmRequest pwmRequest, final GuestRegistrationBean guestRegistrationBean )
     {
     {
-        final SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd" );
+        final PwmDateFormat dateFormat = PwmDateFormat.newPwmDateFormat( "yyyy-MM-dd" );
 
 
         final long maxValidDays = pwmRequest.getConfig().readSettingAsLong( PwmSetting.GUEST_MAX_VALID_DAYS );
         final long maxValidDays = pwmRequest.getConfig().readSettingAsLong( PwmSetting.GUEST_MAX_VALID_DAYS );
         pwmRequest.setAttribute( PwmRequestAttribute.GuestMaximumValidDays, String.valueOf( maxValidDays ) );
         pwmRequest.setAttribute( PwmRequestAttribute.GuestMaximumValidDays, String.valueOf( maxValidDays ) );
@@ -698,7 +697,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
             if ( maxValidDays > 0 )
             if ( maxValidDays > 0 )
             {
             {
                 final long futureMS = maxValidDays * 24 * 60 * 60 * 1000;
                 final long futureMS = maxValidDays * 24 * 60 * 60 * 1000;
-                final Date maxValidDate = new Date( new Date().getTime() + ( futureMS ) );
+                final Instant maxValidDate = Instant.ofEpochMilli( System.currentTimeMillis() + futureMS );
                 maxExpirationDate = dateFormat.format( maxValidDate );
                 maxExpirationDate = dateFormat.format( maxValidDate );
             }
             }
             else
             else
@@ -725,7 +724,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet
             }
             }
             else
             else
             {
             {
-                currentExpirationDate = dateFormat.format( new Date() );
+                currentExpirationDate = dateFormat.format( Instant.now() );
             }
             }
         }
         }
 
 

+ 12 - 1
server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -408,13 +408,24 @@ public class DebugItemGenerator
         @Override
         @Override
         public void outputItem( final DebugItemInput debugItemInput, final OutputStream outputStream ) throws Exception
         public void outputItem( final DebugItemInput debugItemInput, final OutputStream outputStream ) throws Exception
         {
         {
+            final Locale locale = PwmConstants.DEFAULT_LOCALE;
             final PwmApplication pwmApplication = debugItemInput.getPwmApplication();
             final PwmApplication pwmApplication = debugItemInput.getPwmApplication();
             final Set<HealthRecord> records = pwmApplication.getHealthMonitor().getHealthRecords();
             final Set<HealthRecord> records = pwmApplication.getHealthMonitor().getHealthRecords();
-            final String recordJson = JsonUtil.serializeCollection( records, JsonUtil.Flag.PrettyPrint );
+
+            final List<HealthDebugInfo> outputInfos = new ArrayList<>();
+            records.forEach( healthRecord -> outputInfos.add( new HealthDebugInfo( healthRecord, healthRecord.getDetail( locale,  debugItemInput.obfuscatedConfiguration ) ) ) );
+            final String recordJson = JsonUtil.serializeCollection( outputInfos, JsonUtil.Flag.PrettyPrint );
             outputStream.write( recordJson.getBytes( PwmConstants.DEFAULT_CHARSET ) );
             outputStream.write( recordJson.getBytes( PwmConstants.DEFAULT_CHARSET ) );
         }
         }
     }
     }
 
 
+    @Value
+    private static class HealthDebugInfo
+    {
+        private final HealthRecord healthRecord;
+        private final String message;
+    }
+
     static class ThreadDumpDebugItemGenerator implements Generator
     static class ThreadDumpDebugItemGenerator implements Generator
     {
     {
         @Override
         @Override

+ 5 - 7
server/src/main/java/password/pwm/util/java/JavaHelper.java

@@ -45,8 +45,7 @@ import java.lang.management.ThreadInfo;
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
 import java.net.URI;
 import java.net.URI;
 import java.nio.ByteBuffer;
 import java.nio.ByteBuffer;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
+
 import java.time.Instant;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -327,14 +326,13 @@ public class JavaHelper
             return "";
             return "";
         }
         }
 
 
-        final DateFormat dateFormat = new SimpleDateFormat(
+        final PwmDateFormat dateFormat = PwmDateFormat.newPwmDateFormat(
                 PwmConstants.DEFAULT_DATETIME_FORMAT_STR,
                 PwmConstants.DEFAULT_DATETIME_FORMAT_STR,
-                PwmConstants.DEFAULT_LOCALE
+                PwmConstants.DEFAULT_LOCALE,
+                PwmConstants.DEFAULT_TIMEZONE
         );
         );
 
 
-        dateFormat.setTimeZone( PwmConstants.DEFAULT_TIMEZONE );
-
-        return dateFormat.format( date );
+        return dateFormat.format( date.toInstant() );
     }
     }
 
 
     public static Instant parseIsoToInstant( final String input )
     public static Instant parseIsoToInstant( final String input )

+ 12 - 12
server/src/main/java/password/pwm/util/java/JsonUtil.java

@@ -31,6 +31,7 @@ import com.google.gson.JsonSerializationContext;
 import com.google.gson.JsonSerializer;
 import com.google.gson.JsonSerializer;
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
 import com.novell.ldapchai.cr.ChallengeSet;
 import com.novell.ldapchai.cr.ChallengeSet;
+import password.pwm.PwmConstants;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.ldap.PwmLdapVendor;
 import password.pwm.ldap.PwmLdapVendor;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
@@ -45,7 +46,6 @@ import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
 import java.text.DateFormat;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.text.ParseException;
-import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Date;
 import java.util.Date;
@@ -191,16 +191,16 @@ public class JsonUtil
      */
      */
     private static class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date>
     private static class DateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date>
     {
     {
-        private static final DateFormat ISO_DATE_FORMAT;
-        private static final DateFormat GSON_DATE_FORMAT;
+        private static final PwmDateFormat ISO_DATE_FORMAT = PwmDateFormat.newPwmDateFormat(
+                "yyyy-MM-dd'T'HH:mm:ss'Z'",
+                PwmConstants.DEFAULT_LOCALE,
+                TimeZone.getTimeZone( "Zulu" ) );
 
 
-        static
+        private DateFormat getGsonDateFormat()
         {
         {
-            ISO_DATE_FORMAT = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss'Z'" );
-            ISO_DATE_FORMAT.setTimeZone( TimeZone.getTimeZone( "Zulu" ) );
-
-            GSON_DATE_FORMAT = DateFormat.getDateTimeInstance( DateFormat.DEFAULT, DateFormat.DEFAULT );
-            GSON_DATE_FORMAT.setTimeZone( TimeZone.getDefault() );
+            final DateFormat gsonDateFormat = DateFormat.getDateTimeInstance( DateFormat.DEFAULT, DateFormat.DEFAULT );
+            gsonDateFormat.setTimeZone( TimeZone.getDefault() );
+            return gsonDateFormat;
         }
         }
 
 
         private DateTypeAdapter( )
         private DateTypeAdapter( )
@@ -209,14 +209,14 @@ public class JsonUtil
 
 
         public synchronized JsonElement serialize( final Date date, final Type type, final JsonSerializationContext jsonSerializationContext )
         public synchronized JsonElement serialize( final Date date, final Type type, final JsonSerializationContext jsonSerializationContext )
         {
         {
-            return new JsonPrimitive( ISO_DATE_FORMAT.format( date ) );
+            return new JsonPrimitive( ISO_DATE_FORMAT.format( date.toInstant() ) );
         }
         }
 
 
         public synchronized Date deserialize( final JsonElement jsonElement, final Type type, final JsonDeserializationContext jsonDeserializationContext )
         public synchronized Date deserialize( final JsonElement jsonElement, final Type type, final JsonDeserializationContext jsonDeserializationContext )
         {
         {
             try
             try
             {
             {
-                return ISO_DATE_FORMAT.parse( jsonElement.getAsString() );
+                return Date.from( ISO_DATE_FORMAT.parse( jsonElement.getAsString() ) );
             }
             }
             catch ( final ParseException e )
             catch ( final ParseException e )
             {
             {
@@ -226,7 +226,7 @@ public class JsonUtil
             // for backwards compatibility
             // for backwards compatibility
             try
             try
             {
             {
-                return GSON_DATE_FORMAT.parse( jsonElement.getAsString() );
+                return getGsonDateFormat().parse( jsonElement.getAsString() );
             }
             }
             catch ( final ParseException e )
             catch ( final ParseException e )
             {
             {

+ 89 - 0
server/src/main/java/password/pwm/util/java/PwmDateFormat.java

@@ -0,0 +1,89 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2019 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import password.pwm.PwmConstants;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * A thread-safe wrapper for {@link java.text.SimpleDateFormat}.  This class exists to prevent SimpleDateFormat from being used anywhere without proper synchronization.
+ */
+public class PwmDateFormat
+{
+    private final String formatString;
+    private final Locale locale;
+    private final TimeZone timeZone;
+
+    private PwmDateFormat( final String formatString, final Locale locale, final TimeZone timeZone )
+    {
+        this.formatString = formatString;
+        this.locale = locale;
+        this.timeZone = (TimeZone) timeZone.clone();
+    }
+
+    public static PwmDateFormat newPwmDateFormat( final String formatString )
+    {
+        return new PwmDateFormat( formatString, PwmConstants.DEFAULT_LOCALE, PwmConstants.DEFAULT_TIMEZONE );
+    }
+
+    public static PwmDateFormat newPwmDateFormat( final String formatString, final Locale locale, final TimeZone timeZone )
+    {
+        return new PwmDateFormat( formatString, locale, timeZone );
+    }
+
+    public static String format( final String formatString, final Instant date )
+            throws IllegalArgumentException, NullPointerException
+    {
+        return newPwmDateFormat( formatString ).format( date );
+    }
+
+    public static Instant parse( final String formatString, final String input )
+            throws ParseException, IllegalArgumentException, NullPointerException
+    {
+        return newPwmDateFormat( formatString ).parse( input );
+    }
+
+    private SimpleDateFormat newSimpleDateFormat()
+            throws IllegalArgumentException, NullPointerException
+    {
+        final SimpleDateFormat simpleDateFormat = new SimpleDateFormat( formatString, locale );
+        simpleDateFormat.setTimeZone( timeZone );
+        return simpleDateFormat;
+    }
+
+    public String format( final Instant instant )
+            throws IllegalArgumentException, NullPointerException
+    {
+        return newSimpleDateFormat().format( Date.from( instant ) );
+    }
+
+    public Instant parse( final String input )
+            throws ParseException, IllegalArgumentException, NullPointerException
+    {
+        return newSimpleDateFormat().parse( input ).toInstant();
+    }
+}

+ 16 - 18
server/src/main/java/password/pwm/util/macro/StandardMacros.java

@@ -29,20 +29,18 @@ import password.pwm.config.PwmSetting;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.UserInfo;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.PwmDateFormat;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmRandom;
 
 
 import java.net.MalformedURLException;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URL;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.Instant;
 import java.time.ZoneOffset;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
@@ -252,21 +250,14 @@ public abstract class StandardMacros
         {
         {
             final List<String> parameters = splitMacroParameters( matchValue, "CurrentTime" );
             final List<String> parameters = splitMacroParameters( matchValue, "CurrentTime" );
 
 
-            final DateFormat dateFormat;
+            final String dateFormatStr;
             if ( parameters.size() > 0 && !parameters.get( 0 ).isEmpty() )
             if ( parameters.size() > 0 && !parameters.get( 0 ).isEmpty() )
             {
             {
-                try
-                {
-                    dateFormat = new SimpleDateFormat( parameters.get( 0 ) );
-                }
-                catch ( final IllegalArgumentException e )
-                {
-                    throw new MacroParseException( e.getMessage() );
-                }
+                dateFormatStr = parameters.get( 0 );
             }
             }
             else
             else
             {
             {
-                dateFormat = new SimpleDateFormat( PwmConstants.DEFAULT_DATETIME_FORMAT_STR );
+                dateFormatStr = PwmConstants.DEFAULT_DATETIME_FORMAT_STR;
             }
             }
 
 
             final TimeZone tz;
             final TimeZone tz;
@@ -290,8 +281,15 @@ public abstract class StandardMacros
                 throw new MacroParseException( "too many parameters" );
                 throw new MacroParseException( "too many parameters" );
             }
             }
 
 
-            dateFormat.setTimeZone( tz );
-            return dateFormat.format( new Date() );
+            try
+            {
+                final PwmDateFormat pwmDateFormat = PwmDateFormat.newPwmDateFormat( parameters.get( 0 ), PwmConstants.DEFAULT_LOCALE, tz );
+                return pwmDateFormat.format( Instant.now() );
+            }
+            catch ( final IllegalArgumentException e )
+            {
+                throw new MacroParseException( e.getMessage() );
+            }
         }
         }
     }
     }
 
 
@@ -379,7 +377,7 @@ public abstract class StandardMacros
             {
             {
                 try
                 try
                 {
                 {
-                    final DateFormat dateFormat = new SimpleDateFormat( datePattern );
+                    final PwmDateFormat dateFormat = PwmDateFormat.newPwmDateFormat( datePattern );
                     return dateFormat.format( pwdExpirationTime );
                     return dateFormat.format( pwdExpirationTime );
                 }
                 }
                 catch ( final IllegalArgumentException e )
                 catch ( final IllegalArgumentException e )
@@ -599,8 +597,8 @@ public abstract class StandardMacros
         {
         {
             return new MacroDefinitionFlag[]
             return new MacroDefinitionFlag[]
                     {
                     {
-                    MacroDefinitionFlag.SensitiveValue,
-            };
+                            MacroDefinitionFlag.SensitiveValue,
+                    };
         }
         }
     }
     }
 
 

+ 11 - 4
server/src/main/java/password/pwm/util/secure/HttpsServerCertificateManager.java

@@ -48,6 +48,7 @@ import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.PwmDateFormat;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -68,7 +69,7 @@ import java.security.PrivateKey;
 import java.security.SecureRandom;
 import java.security.SecureRandom;
 import java.security.Security;
 import java.security.Security;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
-import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Date;
@@ -293,9 +294,8 @@ public class HttpsServerCertificateManager
             final X500NameBuilder subjectName = new X500NameBuilder( BCStyle.INSTANCE );
             final X500NameBuilder subjectName = new X500NameBuilder( BCStyle.INSTANCE );
             subjectName.addRDN( BCStyle.CN, cnValue );
             subjectName.addRDN( BCStyle.CN, cnValue );
 
 
-            final SimpleDateFormat formatter = new SimpleDateFormat( "yyyyMMddhhmmss" );
-            final String serNumStr = formatter.format( new Date( System.currentTimeMillis() ) );
-            final BigInteger serialNumber = new BigInteger( serNumStr );
+            final BigInteger serialNumber = makeSerialNumber();
+
 
 
             // 2 days in the past
             // 2 days in the past
             final Date notBefore = new Date( System.currentTimeMillis() - TimeUnit.DAYS.toMillis( 2 ) );
             final Date notBefore = new Date( System.currentTimeMillis() - TimeUnit.DAYS.toMillis( 2 ) );
@@ -334,6 +334,13 @@ public class HttpsServerCertificateManager
             return new JcaX509CertificateConverter().setProvider( "BC" ).getCertificate( certGen.build( sigGen ) );
             return new JcaX509CertificateConverter().setProvider( "BC" ).getCertificate( certGen.build( sigGen ) );
         }
         }
 
 
+        private static BigInteger makeSerialNumber()
+        {
+            final PwmDateFormat formatter = PwmDateFormat.newPwmDateFormat( "yyyyMMddhhmmss" );
+            final String serNumStr = formatter.format( Instant.now() );
+            return new BigInteger( serNumStr );
+        }
+
         static KeyPair generateRSAKeyPair( final Configuration config )
         static KeyPair generateRSAKeyPair( final Configuration config )
                 throws Exception
                 throws Exception
         {
         {

+ 44 - 0
server/src/test/java/password/pwm/util/java/PwmDateFormatTest.java

@@ -0,0 +1,44 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2019 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.text.ParseException;
+import java.time.Instant;
+
+public class PwmDateFormatTest
+{
+    @Test
+    public void testDateFormat() throws ParseException
+    {
+        final PwmDateFormat pwmDateFormat = PwmDateFormat.newPwmDateFormat( "yyyyMMddhhmmss" );
+        Assert.assertEquals( "19700101120000", pwmDateFormat.format( Instant.ofEpochMilli( 0 ) ) );
+    }
+
+    @Test
+    public void testDateParse() throws ParseException
+    {
+        final PwmDateFormat pwmDateFormat = PwmDateFormat.newPwmDateFormat( "yyyyMMddhhmmss" );
+        Assert.assertEquals( Instant.ofEpochMilli( 0 ), pwmDateFormat.parse( "19700101120000" ) );
+    }
+}