summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-25 00:13:45 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-25 00:13:45 +0700
commit11b4d6a7afeb6f3be906a1a0ddf132df3f255b4a (patch)
treef76b51eae3e754f33c61dde13c7dd429e0f70753
parentfixed refactored class names in pqc/utils (diff)
downloadBouncyCastle.NET-ed25519-11b4d6a7afeb6f3be906a1a0ddf132df3f255b4a.tar.xz
Add Asn1GeneralizedTime and use
-rw-r--r--crypto/src/asn1/Asn1GeneralizedTime.cs439
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs2
-rw-r--r--crypto/src/asn1/Asn1UniversalTypes.cs2
-rw-r--r--crypto/src/asn1/DerGeneralizedTime.cs350
-rw-r--r--crypto/src/asn1/cmp/PKIHeader.cs6
-rw-r--r--crypto/src/asn1/cmp/PKIHeaderBuilder.cs4
-rw-r--r--crypto/src/asn1/cmp/RevAnnContent.cs20
-rw-r--r--crypto/src/asn1/cms/KEKIdentifier.cs12
-rw-r--r--crypto/src/asn1/cms/RecipientKeyIdentifier.cs14
-rw-r--r--crypto/src/asn1/cms/Time.cs13
-rw-r--r--crypto/src/asn1/esf/OcspIdentifier.cs25
-rw-r--r--crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs6
-rw-r--r--crypto/src/asn1/ocsp/CrlID.cs6
-rw-r--r--crypto/src/asn1/ocsp/ResponseData.cs10
-rw-r--r--crypto/src/asn1/ocsp/RevokedInfo.cs10
-rw-r--r--crypto/src/asn1/ocsp/SingleResponse.cs18
-rw-r--r--crypto/src/asn1/tsp/TSTInfo.cs8
-rw-r--r--crypto/src/asn1/util/Asn1Dump.cs2
-rw-r--r--crypto/src/asn1/x509/AttCertValidityPeriod.cs16
-rw-r--r--crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs10
-rw-r--r--crypto/src/asn1/x509/TBSCertList.cs2
-rw-r--r--crypto/src/asn1/x509/Time.cs12
-rw-r--r--crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs10
-rw-r--r--crypto/src/asn1/x509/V2TBSCertListGenerator.cs2
-rw-r--r--crypto/src/asn1/x509/X509DefaultEntryConverter.cs2
-rw-r--r--crypto/src/asn1/x509/sigi/PersonalData.cs8
-rw-r--r--crypto/src/cmp/ProtectedPkiMessageBuilder.cs9
-rw-r--r--crypto/src/ocsp/BasicOCSPRespGenerator.cs9
-rw-r--r--crypto/src/ocsp/RevokedStatus.cs2
-rw-r--r--crypto/src/pkix/PkixCertPathValidatorUtilities.cs4
-rw-r--r--crypto/src/tsp/TimeStampTokenGenerator.cs65
-rw-r--r--crypto/src/x509/X509V2AttributeCertificateGenerator.cs4
-rw-r--r--crypto/src/x509/X509V2CRLGenerator.cs3
-rw-r--r--crypto/test/src/asn1/test/DeclarationOfMajorityUnitTest.cs6
-rw-r--r--crypto/test/src/asn1/test/GeneralizedTimeTest.cs101
-rw-r--r--crypto/test/src/asn1/test/PersonalDataUnitTest.cs6
-rw-r--r--crypto/test/src/asn1/test/X509NameTest.cs4
37 files changed, 675 insertions, 547 deletions
diff --git a/crypto/src/asn1/Asn1GeneralizedTime.cs b/crypto/src/asn1/Asn1GeneralizedTime.cs
new file mode 100644
index 000000000..e844c8ca2
--- /dev/null
+++ b/crypto/src/asn1/Asn1GeneralizedTime.cs
@@ -0,0 +1,439 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /**
+     * Base class representing the ASN.1 GeneralizedTime type.
+     * <p>
+     * The main difference between these and UTC time is a 4 digit year.
+     * </p>
+     * <p>
+     * One second resolution date+time on UTC timezone (Z)
+     * with 4 digit year (valid from 0001 to 9999).
+     * </p><p>
+     * Timestamp format is:  yyyymmddHHMMSS'Z'
+     * </p><p>
+     * <h2>X.690</h2>
+     * This is what is called "restricted string",
+     * and it uses ASCII characters to encode digits and supplemental data.
+     *
+     * <h3>11: Restrictions on BER employed by both CER and DER</h3>
+     * <h4>11.7 GeneralizedTime </h4>
+     * <p>
+     * <b>11.7.1</b> The encoding shall terminate with a "Z",
+     * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
+     * GeneralizedTime.
+     * </p><p>
+     * <b>11.7.2</b> The seconds element shall always be present.
+     * </p>
+     * <p>
+     * <b>11.7.3</b> The fractional-seconds elements, if present,
+     * shall omit all trailing zeros; if the elements correspond to 0,
+     * they shall be wholly omitted, and the decimal point element also
+     * shall be omitted.
+     */
+    public class Asn1GeneralizedTime
+        : Asn1Object
+    {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1GeneralizedTime), Asn1Tags.GeneralizedTime) { }
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
+        public static Asn1GeneralizedTime GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1GeneralizedTime asn1GeneralizedTime)
+                return asn1GeneralizedTime;
+
+            if (obj is IAsn1Convertible asn1Convertible)
+            {
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1GeneralizedTime converted)
+                    return converted;
+            }
+            else if (obj is byte[] bytes)
+            {
+                try
+                {
+                    return (Asn1GeneralizedTime)Meta.Instance.FromByteArray(bytes);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
+                }
+            }
+
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), nameof(obj));
+        }
+
+        public static Asn1GeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            return (Asn1GeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
+
+        internal readonly byte[] m_contents;
+
+        public Asn1GeneralizedTime(string time)
+        {
+            m_contents = Strings.ToByteArray(time);
+
+            try
+            {
+                ToDateTime();
+            }
+            catch (FormatException e)
+            {
+                throw new ArgumentException("invalid date string: " + e.Message);
+            }
+        }
+
+        public Asn1GeneralizedTime(DateTime time)
+        {
+            DateTime utc = time.ToUniversalTime();
+            var formatStr = @"yyyyMMddHHmmss\Z";
+            var formatProvider = DateTimeFormatInfo.InvariantInfo;
+            string utcString = utc.ToString(formatStr, formatProvider);
+            m_contents = Strings.ToByteArray(utcString);
+        }
+
+        // TODO Custom locale constructor?
+        //public Asn1GeneralizedTime(DateTime time, Locale locale)
+        //{
+        //    SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss\Z", locale);
+
+        //    dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+        //    this.contents = Strings.toByteArray(dateF.format(time));
+        //}
+
+        internal Asn1GeneralizedTime(byte[] bytes)
+        {
+            if (bytes == null)
+                throw new ArgumentNullException(nameof(bytes));
+            if (bytes.Length < 4)
+                throw new ArgumentException("GeneralizedTime string too short", nameof(bytes));
+
+            m_contents = bytes;
+
+            if (!(IsDigit(0) && IsDigit(1) && IsDigit(2) && IsDigit(3)))
+                throw new ArgumentException("illegal characters in GeneralizedTime string", nameof(bytes));
+        }
+
+        public string TimeString => Strings.FromByteArray(m_contents);
+
+        public string GetTime()
+        {
+            string stime = Strings.FromByteArray(m_contents);
+
+            //
+            // standardise the format.
+            //
+            if (stime[stime.Length - 1] == 'Z')
+                return stime.Substring(0, stime.Length - 1) + "GMT+00:00";
+
+            int signPos = stime.Length - 6;
+            char sign = stime[signPos];
+            if ((sign == '-' || sign == '+') && stime.IndexOf("GMT") == signPos - 3)
+            {
+                // already a GMT string!
+                return stime;
+            }
+
+            signPos = stime.Length - 5;
+            sign = stime[signPos];
+            if (sign == '-' || sign == '+')
+            {
+                return stime.Substring(0, signPos)
+                    + "GMT"
+                    + stime.Substring(signPos, 3)
+                    + ":"
+                    + stime.Substring(signPos + 3);
+            }
+
+            signPos = stime.Length - 3;
+            sign = stime[signPos];
+            if (sign == '-' || sign == '+')
+            {
+                return stime.Substring(0, signPos)
+                    + "GMT"
+                    + stime.Substring(signPos)
+                    + ":00";
+            }
+
+            return stime + CalculateGmtOffset(stime);
+        }
+
+        private string CalculateGmtOffset(string stime)
+        {
+            TimeZoneInfo timeZone = TimeZoneInfo.Local;
+            TimeSpan offset = timeZone.BaseUtcOffset;
+
+            string sign = "+";
+            if (offset.CompareTo(TimeSpan.Zero) < 0)
+            {
+                sign = "-";
+                offset = offset.Duration();
+            }
+
+            int hours = offset.Hours;
+            int minutes = offset.Minutes;
+
+            try
+            {
+                if (timeZone.SupportsDaylightSavingTime)
+                {
+                    string d = stime + "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
+                    string formatStr = CalculateGmtFormatString(d);
+
+                    DateTime dateTime = ParseDateString(d, formatStr, makeUniversal: true);
+
+                    if (timeZone.IsDaylightSavingTime(dateTime))
+                    {
+                        hours += sign.Equals("+") ? 1 : -1;
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                // we'll do our best and ignore daylight savings
+            }
+
+            return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
+        }
+
+        private string CalculateGmtFormatString(string d)
+        {
+            if (HasFractionalSeconds())
+            {
+                int fCount = Platform.IndexOf(d, "GMT") - 1 - d.IndexOf('.');
+                return @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz";
+            }
+
+            if (HasSeconds())
+                return @"yyyyMMddHHmmss'GMT'zzz";
+
+            if (HasMinutes())
+                return @"yyyyMMddHHmm'GMT'zzz";
+
+            return @"yyyyMMddHH'GMT'zzz";
+        }
+
+        private string Convert(int time)
+        {
+            if (time < 10)
+                return "0" + time;
+
+            return time.ToString();
+        }
+
+        public DateTime ToDateTime()
+        {
+            string formatStr;
+            string stime = Strings.FromByteArray(m_contents);
+            string d = stime;
+            bool makeUniversal = false;
+
+            if (Platform.EndsWith(stime, "Z"))
+            {
+                if (HasFractionalSeconds())
+                {
+                    int fCount = d.Length - d.IndexOf('.') - 2;
+                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
+                }
+                else if (HasSeconds())
+                {
+                    formatStr = @"yyyyMMddHHmmss\Z";
+                }
+                else if (HasMinutes())
+                {
+                    formatStr = @"yyyyMMddHHmm\Z";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH\Z";
+                }
+            }
+            else if (stime.IndexOf('-') > 0 || stime.IndexOf('+') > 0)
+            {
+                d = GetTime();
+                formatStr = CalculateGmtFormatString(d);
+                makeUniversal = true;
+            }
+            else
+            {
+                if (HasFractionalSeconds())
+                {
+                    int fCount = d.Length - 1 - d.IndexOf('.');
+                    formatStr = @"yyyyMMddHHmmss." + FString(fCount);
+                }
+                else if (HasSeconds())
+                {
+                    formatStr = @"yyyyMMddHHmmss";
+                }
+                else if (HasMinutes())
+                {
+                    formatStr = @"yyyyMMddHHmm";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH";
+                }
+            }
+
+            // TODO Epoch adjustment?
+            //return DateUtil.epochAdjust(dateF.parse(d));
+            return ParseDateString(d, formatStr, makeUniversal);
+        }
+
+        protected bool HasFractionalSeconds()
+        {
+            return m_contents.Length > 14 && m_contents[14] == '.';
+        }
+
+        protected bool HasSeconds()
+        {
+            return IsDigit(12) && IsDigit(13);
+        }
+
+        protected bool HasMinutes()
+        {
+            return IsDigit(10) && IsDigit(11);
+        }
+
+        private bool IsDigit(int pos)
+        {
+            return m_contents.Length > pos && m_contents[pos] >= '0' && m_contents[pos] <= '9';
+        }
+
+        internal override IAsn1Encoding GetEncoding(int encoding)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetDerTime());
+
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, m_contents);
+        }
+
+        internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return new PrimitiveEncoding(tagClass, tagNo, GetDerTime());
+
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
+        }
+
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            if (!(asn1Object is Asn1GeneralizedTime that))
+                return false;
+
+            return Arrays.AreEqual(m_contents, that.m_contents);
+        }
+
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
+        }
+
+        internal static Asn1GeneralizedTime CreatePrimitive(byte[] contents)
+        {
+            return new Asn1GeneralizedTime(contents);
+        }
+
+        internal byte[] GetDerTime()
+        {
+            if (m_contents[m_contents.Length - 1] != 'Z')
+            {
+                return m_contents; // TODO: is there a better way?
+            }
+
+            if (!HasMinutes())
+            {
+                byte[] derTime = new byte[m_contents.Length + 4];
+
+                Array.Copy(m_contents, 0, derTime, 0, m_contents.Length - 1);
+                Array.Copy(Strings.ToByteArray("0000Z"), 0, derTime, m_contents.Length - 1, 5);
+
+                return derTime;
+            }
+            else if (!HasSeconds())
+            {
+                byte[] derTime = new byte[m_contents.Length + 2];
+
+                Array.Copy(m_contents, 0, derTime, 0, m_contents.Length - 1);
+                Array.Copy(Strings.ToByteArray("00Z"), 0, derTime, m_contents.Length - 1, 3);
+
+                return derTime;
+            }
+            else if (HasFractionalSeconds())
+            {
+                int ind = m_contents.Length - 2;
+                while (ind > 0 && m_contents[ind] == '0')
+                {
+                    ind--;
+                }
+
+                if (m_contents[ind] == '.')
+                {
+                    byte[] derTime = new byte[ind + 1];
+
+                    Array.Copy(m_contents, 0, derTime, 0, ind);
+                    derTime[ind] = (byte)'Z';
+
+                    return derTime;
+                }
+                else
+                {
+                    byte[] derTime = new byte[ind + 2];
+
+                    Array.Copy(m_contents, 0, derTime, 0, ind + 1);
+                    derTime[ind + 1] = (byte)'Z';
+
+                    return derTime;
+                }
+            }
+            else
+            {
+                return m_contents;
+            }
+        }
+
+        private static string FString(int count)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < count; ++i)
+            {
+                sb.Append('f');
+            }
+            return sb.ToString();
+        }
+
+        private static DateTime ParseDateString(string s, string format, bool makeUniversal)
+        {
+            DateTimeStyles dateTimeStyles = DateTimeStyles.None;
+            if (Platform.EndsWith(format, "Z"))
+            {
+                dateTimeStyles |= DateTimeStyles.AdjustToUniversal;
+                dateTimeStyles |= DateTimeStyles.AssumeUniversal;
+            }
+
+            DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, dateTimeStyles);
+
+            return makeUniversal ? dt.ToUniversalTime() : dt;
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index aa91cdf62..e7caee9bd 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -426,7 +426,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.BitString:
                 return DerBitString.CreatePrimitive(bytes);
             case Asn1Tags.GeneralizedTime:
-                return DerGeneralizedTime.CreatePrimitive(bytes);
+                return Asn1GeneralizedTime.CreatePrimitive(bytes);
             case Asn1Tags.GeneralString:
                 return DerGeneralString.CreatePrimitive(bytes);
             case Asn1Tags.GraphicString:
diff --git a/crypto/src/asn1/Asn1UniversalTypes.cs b/crypto/src/asn1/Asn1UniversalTypes.cs
index 214918bcd..d188988f6 100644
--- a/crypto/src/asn1/Asn1UniversalTypes.cs
+++ b/crypto/src/asn1/Asn1UniversalTypes.cs
@@ -51,7 +51,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.UtcTime:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
                 return DerUtcTime.Meta.Instance;
             case Asn1Tags.GeneralizedTime:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
-                return DerGeneralizedTime.Meta.Instance;
+                return Asn1GeneralizedTime.Meta.Instance;
             case Asn1Tags.GraphicString:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
                 return DerGraphicString.Meta.Instance;
             case Asn1Tags.VisibleString:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs
index 898a3d585..0386ecb02 100644
--- a/crypto/src/asn1/DerGeneralizedTime.cs
+++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -1,367 +1,33 @@
 using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1
 {
-    /**
-     * Generalized time object.
-     */
     public class DerGeneralizedTime
-        : Asn1Object
+        : Asn1GeneralizedTime
     {
-        internal class Meta : Asn1UniversalType
-        {
-            internal static readonly Asn1UniversalType Instance = new Meta();
-
-            private Meta() : base(typeof(DerGeneralizedTime), Asn1Tags.GeneralizedTime) {}
-
-            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
-            {
-                return CreatePrimitive(octetString.GetOctets());
-            }
-        }
-
-        /**
-         * return a generalized time from the passed in object
-         *
-         * @exception ArgumentException if the object cannot be converted.
-         */
-        public static DerGeneralizedTime GetInstance(object obj)
-        {
-            if (obj == null || obj is DerGeneralizedTime)
-            {
-                return (DerGeneralizedTime)obj;
-            }
-            else if (obj is IAsn1Convertible)
-            {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerGeneralizedTime)
-                    return (DerGeneralizedTime)asn1Object;
-            }
-            else if (obj is byte[])
-            {
-                try
-                {
-                    return (DerGeneralizedTime)Meta.Instance.FromByteArray((byte[])obj);
-                }
-                catch (IOException e)
-                {
-                    throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
-                }
-            }
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
-        }
-
-        /**
-         * return a generalized Time object from a tagged object.
-         *
-         * @param taggedObject the tagged object holding the object we want
-         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot be converted.
-         */
-        public static DerGeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        public DerGeneralizedTime(byte[] time)
+            : base(time)
         {
-            return (DerGeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-        private readonly string time;
-
-        /**
-         * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
-         * for local time, or Z+-HHMM on the end, for difference between local
-         * time and UTC time. The fractional second amount f must consist of at
-         * least one number with trailing zeroes removed.
-         *
-         * @param time the time string.
-         * @exception ArgumentException if string is an illegal format.
-         */
-        public DerGeneralizedTime(
-            string time)
-        {
-            this.time = time;
-
-            try
-            {
-                ToDateTime();
-            }
-            catch (FormatException e)
-            {
-                throw new ArgumentException("invalid date string: " + e.Message);
-            }
-        }
-
-        /**
-         * base constructor from a local time object
-         */
         public DerGeneralizedTime(DateTime time)
+            : base(time)
         {
-            this.time = time.ToUniversalTime().ToString(@"yyyyMMddHHmmss\Z");
-        }
-
-        internal DerGeneralizedTime(
-            byte[] bytes)
-        {
-            //
-            // explicitly convert to characters
-            //
-            this.time = Strings.FromAsciiByteArray(bytes);
         }
 
-        /**
-         * Return the time.
-         * @return The time string as it appeared in the encoded object.
-         */
-        public string TimeString
+        public DerGeneralizedTime(string time)
+            : base(time)
         {
-            get { return time; }
-        }
-
-        /**
-         * return the time - always in the form of
-         *  YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
-         * <p>
-         * Normally in a certificate we would expect "Z" rather than "GMT",
-         * however adding the "GMT" means we can just use:
-         * <pre>
-         *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
-         * </pre>
-         * To read in the time and Get a date which is compatible with our local
-         * time zone.</p>
-         */
-        public string GetTime()
-        {
-            //
-            // standardise the format.
-            //
-            if (time[time.Length - 1] == 'Z')
-                return time.Substring(0, time.Length - 1) + "GMT+00:00";
-
-            int signPos = time.Length - 5;
-            char sign = time[signPos];
-            if (sign == '-' || sign == '+')
-            {
-                return time.Substring(0, signPos)
-                    + "GMT"
-                    + time.Substring(signPos, 3)
-                    + ":"
-                    + time.Substring(signPos + 3);
-            }
-            else
-            {
-                signPos = time.Length - 3;
-                sign = time[signPos];
-                if (sign == '-' || sign == '+')
-                {
-                    return time.Substring(0, signPos)
-                        + "GMT"
-                        + time.Substring(signPos)
-                        + ":00";
-                }
-            }
-
-            return time + CalculateGmtOffset();
-        }
-
-        private string CalculateGmtOffset()
-        {
-            char sign = '+';
-            DateTime time = ToDateTime();
-
-            TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(time);
-            if (offset.CompareTo(TimeSpan.Zero) < 0)
-            {
-                sign = '-';
-                offset = offset.Duration();
-            }
-            int hours = offset.Hours;
-            int minutes = offset.Minutes;
-
-            return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
-        }
-
-        private static string Convert(
-            int time)
-        {
-            if (time < 10)
-            {
-                return "0" + time;
-            }
-
-            return time.ToString();
-        }
-
-        public DateTime ToDateTime()
-        {
-            string formatStr;
-            string d = time;
-            bool makeUniversal = false;
-
-            if (Platform.EndsWith(d, "Z"))
-            {
-                if (HasFractionalSeconds)
-                {
-                    int fCount = d.Length - d.IndexOf('.') - 2;
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
-                }
-                else if (HasSeconds)
-                {
-                    formatStr = @"yyyyMMddHHmmss\Z";
-                }
-                else if (HasMinutes)
-                {
-                    formatStr = @"yyyyMMddHHmm\Z";
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHH\Z";
-                }
-            }
-            else if (time.IndexOf('-') > 0 || time.IndexOf('+') > 0)
-            {
-                d = GetTime();
-                makeUniversal = true;
-
-                if (HasFractionalSeconds)
-                {
-                    int fCount = Platform.IndexOf(d, "GMT") - 1 - d.IndexOf('.');
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz";
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHHmmss'GMT'zzz";
-                }
-            }
-            else
-            {
-                if (HasFractionalSeconds)
-                {
-                    int fCount = d.Length - 1 - d.IndexOf('.');
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount);
-                }
-                else if (HasSeconds)
-                {
-                    formatStr = @"yyyyMMddHHmmss";
-                }
-                else if (HasMinutes)
-                {
-                    formatStr = @"yyyyMMddHHmm";
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHH";
-                }
-
-                // TODO?
-//				dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
-            }
-
-            return ParseDateString(d, formatStr, makeUniversal);
-        }
-
-        private string FString(
-            int count)
-        {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < count; ++i)
-            {
-                sb.Append('f');
-            }
-            return sb.ToString();
-        }
-
-        private DateTime ParseDateString(string	s, string format, bool makeUniversal)
-        {
-            /*
-             * NOTE: DateTime.Kind and DateTimeStyles.AssumeUniversal not available in .NET 1.1
-             */
-            DateTimeStyles style = DateTimeStyles.None;
-            if (Platform.EndsWith(format, "Z"))
-            {
-                style |= DateTimeStyles.AdjustToUniversal;
-                style |= DateTimeStyles.AssumeUniversal;
-            }
-
-            DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, style);
-
-            return makeUniversal ? dt.ToUniversalTime() : dt;
-        }
-
-        private bool HasFractionalSeconds
-        {
-            get { return time.IndexOf('.') == 14; }
-        }
-
-        private bool HasSeconds =>  IsDigit(12) && IsDigit(13);
-
-        private bool HasMinutes => IsDigit(10) && IsDigit(11);
-
-        private bool IsDigit(int pos)
-        {
-            return time.Length > pos && char.IsDigit(time[pos]);
-        }
-
-        private byte[] GetOctets(int encoding)
-        {
-            if (Asn1OutputStream.EncodingDer == encoding && time[time.Length - 1] == 'Z')
-            {
-                if (!HasMinutes)
-                    return Strings.ToAsciiByteArray(time.Insert(time.Length - 1, "0000"));
-                if (!HasSeconds)
-                    return Strings.ToAsciiByteArray(time.Insert(time.Length - 1, "00"));
-
-                if (HasFractionalSeconds)
-                {
-                    int ind = time.Length - 2;
-                    while (ind > 0 && time[ind] == '0')
-                    {
-                        --ind;
-                    }
-
-                    if (time[ind] != '.')
-                    {
-                        ++ind;
-                    }
-
-                    if (ind != time.Length - 1)
-                    {
-                        return Strings.ToAsciiByteArray(time.Remove(ind, time.Length - 1 - ind));
-                    }
-                }
-            }
-
-            return Strings.ToAsciiByteArray(time);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetOctets(encoding));
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetDerTime());
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets(encoding));
-        }
-
-        protected override bool Asn1Equals(Asn1Object asn1Object)
-        {
-            DerGeneralizedTime that = asn1Object as DerGeneralizedTime;
-            return null != that
-                && this.time.Equals(that.time);
-        }
-
-        protected override int Asn1GetHashCode()
-        {
-            return time.GetHashCode();
-        }
-
-        internal static DerGeneralizedTime CreatePrimitive(byte[] contents)
-        {
-            return new DerGeneralizedTime(contents);
+            return new PrimitiveEncoding(tagClass, tagNo, GetDerTime());
         }
     }
 }
diff --git a/crypto/src/asn1/cmp/PKIHeader.cs b/crypto/src/asn1/cmp/PKIHeader.cs
index 553a81bc0..7ed914e6a 100644
--- a/crypto/src/asn1/cmp/PKIHeader.cs
+++ b/crypto/src/asn1/cmp/PKIHeader.cs
@@ -19,7 +19,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
         private readonly DerInteger pvno;
         private readonly GeneralName sender;
         private readonly GeneralName recipient;
-        private readonly DerGeneralizedTime messageTime;
+        private readonly Asn1GeneralizedTime messageTime;
         private readonly AlgorithmIdentifier protectionAlg;
         private readonly Asn1OctetString senderKID;       // KeyIdentifier
         private readonly Asn1OctetString recipKID;        // KeyIdentifier
@@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
                 switch (tObj.TagNo)
                 {
                 case 0:
-                    messageTime = DerGeneralizedTime.GetInstance(tObj, true);
+                    messageTime = Asn1GeneralizedTime.GetInstance(tObj, true);
                     break;
                 case 1:
                     protectionAlg = AlgorithmIdentifier.GetInstance(tObj, true);
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
             get { return recipient; }
         }
 
-        public virtual DerGeneralizedTime MessageTime
+        public virtual Asn1GeneralizedTime MessageTime
         {
             get { return messageTime; }
         }
diff --git a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
index d771dda4c..cbefc73b8 100644
--- a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
+++ b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		private DerInteger pvno;
 		private GeneralName sender;
 		private GeneralName recipient;
-		private DerGeneralizedTime messageTime;
+		private Asn1GeneralizedTime messageTime;
 		private AlgorithmIdentifier protectionAlg;
 		private Asn1OctetString senderKID;       // KeyIdentifier
 		private Asn1OctetString recipKID;        // KeyIdentifier
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 			this.recipient = recipient;
 		}
 
-		public virtual PkiHeaderBuilder SetMessageTime(DerGeneralizedTime time)
+		public virtual PkiHeaderBuilder SetMessageTime(Asn1GeneralizedTime time)
 		{
 			messageTime = time;
 			return this;
diff --git a/crypto/src/asn1/cmp/RevAnnContent.cs b/crypto/src/asn1/cmp/RevAnnContent.cs
index 4ef6fdbf7..cdd26c39f 100644
--- a/crypto/src/asn1/cmp/RevAnnContent.cs
+++ b/crypto/src/asn1/cmp/RevAnnContent.cs
@@ -19,18 +19,18 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
         private readonly PkiStatusEncodable m_status;
 		private readonly CertId m_certID;
-		private readonly DerGeneralizedTime m_willBeRevokedAt;
-		private readonly DerGeneralizedTime m_badSinceDate;
+		private readonly Asn1GeneralizedTime m_willBeRevokedAt;
+		private readonly Asn1GeneralizedTime m_badSinceDate;
 		private readonly X509Extensions m_crlDetails;
 
-        public RevAnnContent(PkiStatusEncodable status, CertId certID, DerGeneralizedTime willBeRevokedAt,
-			DerGeneralizedTime badSinceDate)
+        public RevAnnContent(PkiStatusEncodable status, CertId certID, Asn1GeneralizedTime willBeRevokedAt,
+            Asn1GeneralizedTime badSinceDate)
             : this(status, certID, willBeRevokedAt, badSinceDate, null)
         {
 		}
 
-        public RevAnnContent(PkiStatusEncodable status, CertId certID, DerGeneralizedTime willBeRevokedAt,
-			DerGeneralizedTime badSinceDate, X509Extensions crlDetails)
+        public RevAnnContent(PkiStatusEncodable status, CertId certID, Asn1GeneralizedTime willBeRevokedAt,
+            Asn1GeneralizedTime badSinceDate, X509Extensions crlDetails)
         {
             m_status = status;
             m_certID = certID;
@@ -43,8 +43,8 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		{
 			m_status = PkiStatusEncodable.GetInstance(seq[0]);
 			m_certID = CertId.GetInstance(seq[1]);
-			m_willBeRevokedAt = DerGeneralizedTime.GetInstance(seq[2]);
-			m_badSinceDate = DerGeneralizedTime.GetInstance(seq[3]);
+			m_willBeRevokedAt = Asn1GeneralizedTime.GetInstance(seq[2]);
+			m_badSinceDate = Asn1GeneralizedTime.GetInstance(seq[3]);
 
 			if (seq.Count > 4)
 			{
@@ -56,9 +56,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 		public virtual CertId CertID => m_certID;
 
-		public virtual DerGeneralizedTime WillBeRevokedAt => m_willBeRevokedAt;
+		public virtual Asn1GeneralizedTime WillBeRevokedAt => m_willBeRevokedAt;
 
-		public virtual DerGeneralizedTime BadSinceDate => m_badSinceDate;
+		public virtual Asn1GeneralizedTime BadSinceDate => m_badSinceDate;
 
 		public virtual X509Extensions CrlDetails => m_crlDetails;
 
diff --git a/crypto/src/asn1/cms/KEKIdentifier.cs b/crypto/src/asn1/cms/KEKIdentifier.cs
index a42217440..36ab94f52 100644
--- a/crypto/src/asn1/cms/KEKIdentifier.cs
+++ b/crypto/src/asn1/cms/KEKIdentifier.cs
@@ -8,12 +8,12 @@ namespace Org.BouncyCastle.Asn1.Cms
         : Asn1Encodable
     {
         private Asn1OctetString		keyIdentifier;
-        private DerGeneralizedTime	date;
+        private Asn1GeneralizedTime date;
         private OtherKeyAttribute	other;
 
 		public KekIdentifier(
             byte[]              keyIdentifier,
-            DerGeneralizedTime  date,
+            Asn1GeneralizedTime date,
             OtherKeyAttribute   other)
         {
             this.keyIdentifier = new DerOctetString(keyIdentifier);
@@ -31,9 +31,9 @@ namespace Org.BouncyCastle.Asn1.Cms
             case 1:
 				break;
             case 2:
-				if (seq[1] is DerGeneralizedTime)
+				if (seq[1] is Asn1GeneralizedTime)
 				{
-					date = (DerGeneralizedTime) seq[1];
+					date = (Asn1GeneralizedTime) seq[1];
 				}
 				else
 				{
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 				}
 				break;
             case 3:
-				date  = (DerGeneralizedTime) seq[1];
+				date  = (Asn1GeneralizedTime) seq[1];
 				other = OtherKeyAttribute.GetInstance(seq[2]);
 				break;
             default:
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 			get { return keyIdentifier; }
 		}
 
-		public DerGeneralizedTime Date
+		public Asn1GeneralizedTime Date
 		{
 			get { return date; }
 		}
diff --git a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
index 995ddab51..dea9ce09d 100644
--- a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
+++ b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
@@ -8,12 +8,12 @@ namespace Org.BouncyCastle.Asn1.Cms
         : Asn1Encodable
     {
         private Asn1OctetString      subjectKeyIdentifier;
-        private DerGeneralizedTime   date;
+        private Asn1GeneralizedTime  date;
         private OtherKeyAttribute    other;
 
 		public RecipientKeyIdentifier(
             Asn1OctetString         subjectKeyIdentifier,
-            DerGeneralizedTime      date,
+            Asn1GeneralizedTime     date,
             OtherKeyAttribute       other)
         {
             this.subjectKeyIdentifier = subjectKeyIdentifier;
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
 		public RecipientKeyIdentifier(
 			byte[]				subjectKeyIdentifier,
-			DerGeneralizedTime	date,
+            Asn1GeneralizedTime date,
 			OtherKeyAttribute	other)
 		{
 			this.subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier);
@@ -48,9 +48,9 @@ namespace Org.BouncyCastle.Asn1.Cms
 				case 1:
 					break;
 				case 2:
-					if (seq[1] is DerGeneralizedTime)
+					if (seq[1] is Asn1GeneralizedTime)
 					{
-						date = (DerGeneralizedTime) seq[1];
+						date = (Asn1GeneralizedTime)seq[1];
 					}
 					else
 					{
@@ -58,7 +58,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 					}
 					break;
 				case 3:
-					date  = (DerGeneralizedTime) seq[1];
+					date  = (Asn1GeneralizedTime)seq[1];
 					other = OtherKeyAttribute.GetInstance(seq[2]);
 					break;
 				default:
@@ -105,7 +105,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 			get { return subjectKeyIdentifier; }
 		}
 
-		public DerGeneralizedTime Date
+		public Asn1GeneralizedTime Date
 		{
 			get { return date; }
 		}
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
index 52fb4f937..e8a6905ad 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -22,7 +22,7 @@ namespace Org.BouncyCastle.Asn1.Cms
         {
             if (time == null)
                 throw new ArgumentNullException("time");
-            if (!(time is DerUtcTime) && !(time is DerGeneralizedTime))
+            if (!(time is DerUtcTime) && !(time is Asn1GeneralizedTime))
                 throw new ArgumentException("unknown object passed to Time");
 
             this.time = time;
@@ -33,8 +33,7 @@ namespace Org.BouncyCastle.Asn1.Cms
          * and 2049 a UTCTime object is Generated, otherwise a GeneralizedTime
          * is used.
          */
-        public Time(
-            DateTime date)
+        public Time(DateTime date)
         {
             string d = date.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
 
@@ -57,8 +56,8 @@ namespace Org.BouncyCastle.Asn1.Cms
                 return (Time)obj;
 			if (obj is DerUtcTime)
                 return new Time((DerUtcTime)obj);
-			if (obj is DerGeneralizedTime)
-                return new Time((DerGeneralizedTime)obj);
+			if (obj is Asn1GeneralizedTime)
+                return new Time((Asn1GeneralizedTime)obj);
 
             throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
         }
@@ -73,7 +72,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 				}
 				else
 				{
-					return ((DerGeneralizedTime)time).GetTime();
+					return ((Asn1GeneralizedTime)time).GetTime();
 				}
 			}
         }
@@ -89,7 +88,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 						return ((DerUtcTime)time).ToAdjustedDateTime();
 					}
 
-					return ((DerGeneralizedTime)time).ToDateTime();
+					return ((Asn1GeneralizedTime)time).ToDateTime();
 				}
 				catch (FormatException e)
 				{
diff --git a/crypto/src/asn1/esf/OcspIdentifier.cs b/crypto/src/asn1/esf/OcspIdentifier.cs
index e65f1cfe7..fa7069aed 100644
--- a/crypto/src/asn1/esf/OcspIdentifier.cs
+++ b/crypto/src/asn1/esf/OcspIdentifier.cs
@@ -20,7 +20,7 @@ namespace Org.BouncyCastle.Asn1.Esf
 		: Asn1Encodable
 	{
 		private readonly ResponderID		ocspResponderID;
-		private readonly DerGeneralizedTime	producedAt;
+		private readonly Asn1GeneralizedTime producedAt;
 
 		public static OcspIdentifier GetInstance(
 			object obj)
@@ -46,21 +46,30 @@ namespace Org.BouncyCastle.Asn1.Esf
 				throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
 
 			this.ocspResponderID = ResponderID.GetInstance(seq[0].ToAsn1Object());
-			this.producedAt = (DerGeneralizedTime) seq[1].ToAsn1Object();
+			this.producedAt = (Asn1GeneralizedTime)seq[1].ToAsn1Object();
 		}
 
-		public OcspIdentifier(
-			ResponderID	ocspResponderID,
-			DateTime	producedAt)
+		public OcspIdentifier(ResponderID ocspResponderID, DateTime producedAt)
 		{
 			if (ocspResponderID == null)
-				throw new ArgumentNullException();
+				throw new ArgumentNullException(nameof(ocspResponderID));
 
 			this.ocspResponderID = ocspResponderID;
-			this.producedAt = new DerGeneralizedTime(producedAt);
+			this.producedAt = new Asn1GeneralizedTime(producedAt);
 		}
 
-		public ResponderID OcspResponderID
+        public OcspIdentifier(ResponderID ocspResponderID, Asn1GeneralizedTime producedAt)
+        {
+            if (ocspResponderID == null)
+                throw new ArgumentNullException(nameof(ocspResponderID));
+            if (producedAt == null)
+                throw new ArgumentNullException(nameof(producedAt));
+
+            this.ocspResponderID = ocspResponderID;
+            this.producedAt = producedAt;
+        }
+
+        public ResponderID OcspResponderID
 		{
 			get { return ocspResponderID; }
 		}
diff --git a/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs b/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
index b82c9373d..c9c96cbda 100644
--- a/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
+++ b/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
@@ -64,7 +64,7 @@ namespace Org.BouncyCastle.Asn1.IsisMtt.X509
 		}
 
 		public DeclarationOfMajority(
-			DerGeneralizedTime dateOfBirth)
+            Asn1GeneralizedTime dateOfBirth)
 		{
 			this.declaration = new DerTaggedObject(false, 2, dateOfBirth);
 		}
@@ -155,14 +155,14 @@ namespace Org.BouncyCastle.Asn1.IsisMtt.X509
 			}
 		}
 
-		public virtual DerGeneralizedTime DateOfBirth
+		public virtual Asn1GeneralizedTime DateOfBirth
 		{
 			get
 			{
 				switch ((Choice) declaration.TagNo)
 				{
 					case Choice.DateOfBirth:
-						return DerGeneralizedTime.GetInstance(declaration, false);
+						return Asn1GeneralizedTime.GetInstance(declaration, false);
 					default:
 						return null;
 				}
diff --git a/crypto/src/asn1/ocsp/CrlID.cs b/crypto/src/asn1/ocsp/CrlID.cs
index fc1e59d22..24dda4edf 100644
--- a/crypto/src/asn1/ocsp/CrlID.cs
+++ b/crypto/src/asn1/ocsp/CrlID.cs
@@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     {
         private readonly DerIA5String		crlUrl;
         private readonly DerInteger			crlNum;
-        private readonly DerGeneralizedTime	crlTime;
+        private readonly Asn1GeneralizedTime crlTime;
 
 		// TODO Add GetInstance method(s) and make this private?
 		public CrlID(Asn1Sequence seq)
@@ -23,7 +23,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
                     crlNum = DerInteger.GetInstance(o, true);
                     break;
                 case 2:
-                    crlTime = DerGeneralizedTime.GetInstance(o, true);
+                    crlTime = Asn1GeneralizedTime.GetInstance(o, true);
                     break;
                 default:
                     throw new ArgumentException("unknown tag number: " + o.TagNo);
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return crlNum; }
 		}
 
-		public DerGeneralizedTime CrlTime
+		public Asn1GeneralizedTime CrlTime
 		{
 			get { return crlTime; }
 		}
diff --git a/crypto/src/asn1/ocsp/ResponseData.cs b/crypto/src/asn1/ocsp/ResponseData.cs
index a5769c0fa..dfb234bc1 100644
--- a/crypto/src/asn1/ocsp/ResponseData.cs
+++ b/crypto/src/asn1/ocsp/ResponseData.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		private readonly bool                versionPresent;
 		private readonly DerInteger          version;
 		private readonly ResponderID         responderID;
-		private readonly DerGeneralizedTime  producedAt;
+		private readonly Asn1GeneralizedTime producedAt;
 		private readonly Asn1Sequence        responses;
 		private readonly X509Extensions      responseExtensions;
 
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		public ResponseData(
 			DerInteger          version,
 			ResponderID         responderID,
-			DerGeneralizedTime  producedAt,
+            Asn1GeneralizedTime producedAt,
 			Asn1Sequence        responses,
 			X509Extensions      responseExtensions)
 		{
@@ -56,7 +56,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 
 		public ResponseData(
 			ResponderID         responderID,
-			DerGeneralizedTime  producedAt,
+            Asn1GeneralizedTime producedAt,
 			Asn1Sequence        responses,
 			X509Extensions      responseExtensions)
 			: this(V1, responderID, producedAt, responses, responseExtensions)
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			}
 
 			this.responderID = ResponderID.GetInstance(seq[index++]);
-			this.producedAt = (DerGeneralizedTime)seq[index++];
+			this.producedAt = (Asn1GeneralizedTime)seq[index++];
 			this.responses = (Asn1Sequence)seq[index++];
 
 			if (seq.Count > index)
@@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return responderID; }
 		}
 
-		public DerGeneralizedTime ProducedAt
+		public Asn1GeneralizedTime ProducedAt
 		{
 			get { return producedAt; }
 		}
diff --git a/crypto/src/asn1/ocsp/RevokedInfo.cs b/crypto/src/asn1/ocsp/RevokedInfo.cs
index c67be0678..e6438dd08 100644
--- a/crypto/src/asn1/ocsp/RevokedInfo.cs
+++ b/crypto/src/asn1/ocsp/RevokedInfo.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     public class RevokedInfo
         : Asn1Encodable
     {
-        private readonly DerGeneralizedTime revocationTime;
+        private readonly Asn1GeneralizedTime revocationTime;
         private readonly CrlReason revocationReason;
 
 		public static RevokedInfo GetInstance(
@@ -35,13 +35,13 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		}
 
 		public RevokedInfo(
-			DerGeneralizedTime revocationTime)
+            Asn1GeneralizedTime revocationTime)
 			: this(revocationTime, null)
 		{
 		}
 
 		public RevokedInfo(
-            DerGeneralizedTime  revocationTime,
+            Asn1GeneralizedTime revocationTime,
             CrlReason           revocationReason)
         {
 			if (revocationTime == null)
@@ -54,7 +54,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		private RevokedInfo(
             Asn1Sequence seq)
         {
-            this.revocationTime = (DerGeneralizedTime) seq[0];
+            this.revocationTime = (Asn1GeneralizedTime)seq[0];
 
 			if (seq.Count > 1)
             {
@@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
             }
         }
 
-		public DerGeneralizedTime RevocationTime
+		public Asn1GeneralizedTime RevocationTime
 		{
 			get { return revocationTime; }
 		}
diff --git a/crypto/src/asn1/ocsp/SingleResponse.cs b/crypto/src/asn1/ocsp/SingleResponse.cs
index ecdf3dab0..42b451af7 100644
--- a/crypto/src/asn1/ocsp/SingleResponse.cs
+++ b/crypto/src/asn1/ocsp/SingleResponse.cs
@@ -10,15 +10,15 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     {
         private readonly CertID              certID;
         private readonly CertStatus          certStatus;
-        private readonly DerGeneralizedTime  thisUpdate;
-        private readonly DerGeneralizedTime  nextUpdate;
+        private readonly Asn1GeneralizedTime thisUpdate;
+        private readonly Asn1GeneralizedTime nextUpdate;
         private readonly X509Extensions      singleExtensions;
 
 		public SingleResponse(
             CertID              certID,
             CertStatus          certStatus,
-            DerGeneralizedTime  thisUpdate,
-            DerGeneralizedTime  nextUpdate,
+            Asn1GeneralizedTime thisUpdate,
+            Asn1GeneralizedTime nextUpdate,
             X509Extensions      singleExtensions)
         {
             this.certID = certID;
@@ -33,11 +33,11 @@ namespace Org.BouncyCastle.Asn1.Ocsp
         {
             this.certID = CertID.GetInstance(seq[0]);
             this.certStatus = CertStatus.GetInstance(seq[1]);
-            this.thisUpdate = (DerGeneralizedTime)seq[2];
+            this.thisUpdate = (Asn1GeneralizedTime)seq[2];
 
 			if (seq.Count > 4)
             {
-                this.nextUpdate = DerGeneralizedTime.GetInstance(
+                this.nextUpdate = Asn1GeneralizedTime.GetInstance(
 					(Asn1TaggedObject) seq[3], true);
                 this.singleExtensions = X509Extensions.GetInstance(
 					(Asn1TaggedObject) seq[4], true);
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 
 				if (o.TagNo == 0)
                 {
-                    this.nextUpdate = DerGeneralizedTime.GetInstance(o, true);
+                    this.nextUpdate = Asn1GeneralizedTime.GetInstance(o, true);
                 }
                 else
                 {
@@ -90,12 +90,12 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return certStatus; }
 		}
 
-		public DerGeneralizedTime ThisUpdate
+		public Asn1GeneralizedTime ThisUpdate
 		{
 			get { return thisUpdate; }
 		}
 
-		public DerGeneralizedTime NextUpdate
+		public Asn1GeneralizedTime NextUpdate
 		{
 			get { return nextUpdate; }
 		}
diff --git a/crypto/src/asn1/tsp/TSTInfo.cs b/crypto/src/asn1/tsp/TSTInfo.cs
index 28a840e77..dde11494c 100644
--- a/crypto/src/asn1/tsp/TSTInfo.cs
+++ b/crypto/src/asn1/tsp/TSTInfo.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 		private readonly DerObjectIdentifier	tsaPolicyId;
 		private readonly MessageImprint			messageImprint;
 		private readonly DerInteger				serialNumber;
-		private readonly DerGeneralizedTime		genTime;
+		private readonly Asn1GeneralizedTime	genTime;
 		private readonly Accuracy				accuracy;
 		private readonly DerBoolean				ordering;
 		private readonly DerInteger				nonce;
@@ -49,7 +49,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 
 			// genTime
 			e.MoveNext();
-			genTime = DerGeneralizedTime.GetInstance(e.Current);
+			genTime = Asn1GeneralizedTime.GetInstance(e.Current);
 
 			// default for ordering
 			ordering = DerBoolean.False;
@@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 			DerObjectIdentifier	tsaPolicyId,
 			MessageImprint		messageImprint,
 			DerInteger			serialNumber,
-			DerGeneralizedTime	genTime,
+            Asn1GeneralizedTime genTime,
 			Accuracy			accuracy,
 			DerBoolean			ordering,
 			DerInteger			nonce,
@@ -140,7 +140,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 			get { return accuracy; }
 		}
 
-		public DerGeneralizedTime GenTime
+		public Asn1GeneralizedTime GenTime
 		{
 			get { return genTime; }
 		}
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index 7bae766c3..73586cf9b 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -211,7 +211,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
                 buf.Append(indent);
                 buf.AppendLine("UTCTime(" + utcTime.TimeString + ")");
             }
-            else if (obj is DerGeneralizedTime generalizedTime)
+            else if (obj is Asn1GeneralizedTime generalizedTime)
             {
                 buf.Append(indent);
                 buf.AppendLine("GeneralizedTime(" + generalizedTime.GetTime() + ")");
diff --git a/crypto/src/asn1/x509/AttCertValidityPeriod.cs b/crypto/src/asn1/x509/AttCertValidityPeriod.cs
index d31e07402..893bc0838 100644
--- a/crypto/src/asn1/x509/AttCertValidityPeriod.cs
+++ b/crypto/src/asn1/x509/AttCertValidityPeriod.cs
@@ -7,8 +7,8 @@ namespace Org.BouncyCastle.Asn1.X509
     public class AttCertValidityPeriod
         : Asn1Encodable
     {
-        private readonly DerGeneralizedTime	notBeforeTime;
-        private readonly DerGeneralizedTime	notAfterTime;
+        private readonly Asn1GeneralizedTime notBeforeTime;
+        private readonly Asn1GeneralizedTime notAfterTime;
 
 		public static AttCertValidityPeriod GetInstance(
             object obj)
@@ -39,24 +39,24 @@ namespace Org.BouncyCastle.Asn1.X509
 			if (seq.Count != 2)
 				throw new ArgumentException("Bad sequence size: " + seq.Count);
 
-			notBeforeTime = DerGeneralizedTime.GetInstance(seq[0]);
-			notAfterTime = DerGeneralizedTime.GetInstance(seq[1]);
+			notBeforeTime = Asn1GeneralizedTime.GetInstance(seq[0]);
+			notAfterTime = Asn1GeneralizedTime.GetInstance(seq[1]);
         }
 
 		public AttCertValidityPeriod(
-            DerGeneralizedTime	notBeforeTime,
-            DerGeneralizedTime	notAfterTime)
+            Asn1GeneralizedTime notBeforeTime,
+            Asn1GeneralizedTime notAfterTime)
         {
             this.notBeforeTime = notBeforeTime;
             this.notAfterTime = notAfterTime;
         }
 
-		public DerGeneralizedTime NotBeforeTime
+		public Asn1GeneralizedTime NotBeforeTime
 		{
 			get { return notBeforeTime; }
 		}
 
-		public DerGeneralizedTime NotAfterTime
+		public Asn1GeneralizedTime NotAfterTime
 		{
 			get { return notAfterTime; }
 		}
diff --git a/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs b/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
index 89e8de6cb..a87c2ee9e 100644
--- a/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
+++ b/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
@@ -36,7 +36,7 @@ namespace Org.BouncyCastle.Asn1.X509
 			throw new ArgumentException("unknown object in GetInstance: " + Platform.GetTypeName(obj), "obj");
 		}
 
-		private DerGeneralizedTime _notBefore, _notAfter;
+		private Asn1GeneralizedTime _notBefore, _notAfter;
 
 		private PrivateKeyUsagePeriod(
 			Asn1Sequence seq)
@@ -45,21 +45,21 @@ namespace Org.BouncyCastle.Asn1.X509
 			{
 				if (tObj.TagNo == 0)
 				{
-					_notBefore = DerGeneralizedTime.GetInstance(tObj, false);
+					_notBefore = Asn1GeneralizedTime.GetInstance(tObj, false);
 				}
 				else if (tObj.TagNo == 1)
 				{
-					_notAfter = DerGeneralizedTime.GetInstance(tObj, false);
+					_notAfter = Asn1GeneralizedTime.GetInstance(tObj, false);
 				}
 			}
 		}
 
-		public DerGeneralizedTime NotBefore
+		public Asn1GeneralizedTime NotBefore
 		{
 			get { return _notBefore; }
 		}
 
-		public DerGeneralizedTime NotAfter
+		public Asn1GeneralizedTime NotAfter
 		{
 			get { return _notAfter; }
 		}
diff --git a/crypto/src/asn1/x509/TBSCertList.cs b/crypto/src/asn1/x509/TBSCertList.cs
index ab847d563..865cfdd3d 100644
--- a/crypto/src/asn1/x509/TBSCertList.cs
+++ b/crypto/src/asn1/x509/TBSCertList.cs
@@ -194,7 +194,7 @@ namespace Org.BouncyCastle.Asn1.X509
 
 			if (seqPos < seq.Count
                 && (seq[seqPos] is DerUtcTime
-                   || seq[seqPos] is DerGeneralizedTime
+                   || seq[seqPos] is Asn1GeneralizedTime
                    || seq[seqPos] is Time))
             {
                 nextUpdate = Time.GetInstance(seq[seqPos++]);
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index efdf63850..791f08053 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -22,7 +22,7 @@ namespace Org.BouncyCastle.Asn1.X509
         {
             if (time == null)
                 throw new ArgumentNullException("time");
-            if (!(time is DerUtcTime) && !(time is DerGeneralizedTime))
+            if (!(time is DerUtcTime) && !(time is Asn1GeneralizedTime))
                 throw new ArgumentException("unknown object passed to Time");
 
             this.time = time;
@@ -56,8 +56,8 @@ namespace Org.BouncyCastle.Asn1.X509
                 return (Time)obj;
             if (obj is DerUtcTime)
                 return new Time((DerUtcTime)obj);
-            if (obj is DerGeneralizedTime)
-                return new Time((DerGeneralizedTime)obj);
+            if (obj is Asn1GeneralizedTime)
+                return new Time((Asn1GeneralizedTime)obj);
 
             throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
         }
@@ -66,10 +66,10 @@ namespace Org.BouncyCastle.Asn1.X509
         {
             if (time is DerUtcTime)
             {
-                return ((DerUtcTime) time).AdjustedTimeString;
+                return ((DerUtcTime)time).AdjustedTimeString;
             }
 
-            return ((DerGeneralizedTime) time).GetTime();
+            return ((Asn1GeneralizedTime)time).GetTime();
         }
 
         /// <summary>
@@ -86,7 +86,7 @@ namespace Org.BouncyCastle.Asn1.X509
                 }
                 else
                 {
-                    return ((DerGeneralizedTime)time).ToDateTime();
+                    return ((Asn1GeneralizedTime)time).ToDateTime();
                 }
             }
             catch (FormatException e)
diff --git a/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs b/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
index 02580b5b8..c78c966b0 100644
--- a/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
+++ b/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
@@ -26,11 +26,13 @@ namespace Org.BouncyCastle.Asn1.X509
         internal AttCertIssuer			issuer;
         internal AlgorithmIdentifier	signature;
         internal DerInteger				serialNumber;
-//        internal AttCertValidityPeriod	attrCertValidityPeriod;
         internal Asn1EncodableVector	attributes;
         internal DerBitString			issuerUniqueID;
         internal X509Extensions			extensions;
-        internal DerGeneralizedTime		startDate, endDate;
+
+        // Note: validity period start/end dates stored directly
+        //internal AttCertValidityPeriod attrCertValidityPeriod;
+        internal Asn1GeneralizedTime    startDate, endDate;
 
 		public V2AttributeCertificateInfoGenerator()
         {
@@ -78,13 +80,13 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetStartDate(
-            DerGeneralizedTime startDate)
+            Asn1GeneralizedTime startDate)
         {
             this.startDate = startDate;
         }
 
 		public void SetEndDate(
-            DerGeneralizedTime endDate)
+            Asn1GeneralizedTime endDate)
         {
             this.endDate = endDate;
         }
diff --git a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
index 1d58751fd..546dc91f9 100644
--- a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
+++ b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
@@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Asn1.X509
 		}
 
 		public void AddCrlEntry(DerInteger userCertificate, Time revocationDate, int reason,
-			DerGeneralizedTime invalidityDate)
+            Asn1GeneralizedTime invalidityDate)
 		{
             var extOids = new List<DerObjectIdentifier>();
             var extValues = new List<X509Extension>();
diff --git a/crypto/src/asn1/x509/X509DefaultEntryConverter.cs b/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
index 7282ead26..d155efc5a 100644
--- a/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
+++ b/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
@@ -46,7 +46,7 @@ namespace Org.BouncyCastle.Asn1.X509
 
 			if (oid.Equals(X509Name.DateOfBirth)) // accept time string as well as # (for compatibility)
 			{
-				return new DerGeneralizedTime(value);
+				return new Asn1GeneralizedTime(value);
 			}
 
 			if (oid.Equals(X509Name.C)
diff --git a/crypto/src/asn1/x509/sigi/PersonalData.cs b/crypto/src/asn1/x509/sigi/PersonalData.cs
index 439039888..e8c75bf93 100644
--- a/crypto/src/asn1/x509/sigi/PersonalData.cs
+++ b/crypto/src/asn1/x509/sigi/PersonalData.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 	{
 		private readonly NameOrPseudonym	nameOrPseudonym;
 		private readonly BigInteger			nameDistinguisher;
-		private readonly DerGeneralizedTime	dateOfBirth;
+		private readonly Asn1GeneralizedTime dateOfBirth;
 		private readonly DirectoryString	placeOfBirth;
 		private readonly string				gender;
 		private readonly DirectoryString	postalAddress;
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 					nameDistinguisher = DerInteger.GetInstance(o, false).Value;
 					break;
 				case 1:
-					dateOfBirth = DerGeneralizedTime.GetInstance(o, false);
+					dateOfBirth = Asn1GeneralizedTime.GetInstance(o, false);
 					break;
 				case 2:
 					placeOfBirth = DirectoryString.GetInstance(o, true);
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 		public PersonalData(
 			NameOrPseudonym		nameOrPseudonym,
 			BigInteger			nameDistinguisher,
-			DerGeneralizedTime	dateOfBirth,
+            Asn1GeneralizedTime dateOfBirth,
 			DirectoryString		placeOfBirth,
 			string				gender,
 			DirectoryString		postalAddress)
@@ -141,7 +141,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 			get { return nameDistinguisher; }
 		}
 
-		public DerGeneralizedTime DateOfBirth
+		public Asn1GeneralizedTime DateOfBirth
 		{
 			get { return dateOfBirth; }
 		}
diff --git a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
index 837eb177f..505747960 100644
--- a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
+++ b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Cms;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.X509;
@@ -44,7 +45,13 @@ namespace Org.BouncyCastle.Cmp
             return this;
         }
 
-        public ProtectedPkiMessageBuilder SetMessageTime(DerGeneralizedTime generalizedTime)
+        public ProtectedPkiMessageBuilder SetMessageTime(DateTime time)
+        {
+            m_hdrBuilder.SetMessageTime(new Asn1GeneralizedTime(time));
+            return this;
+        }
+
+        public ProtectedPkiMessageBuilder SetMessageTime(Asn1GeneralizedTime generalizedTime)
         {
             m_hdrBuilder.SetMessageTime(generalizedTime);
             return this;
diff --git a/crypto/src/ocsp/BasicOCSPRespGenerator.cs b/crypto/src/ocsp/BasicOCSPRespGenerator.cs
index 6cbba997d..ff7ae33d3 100644
--- a/crypto/src/ocsp/BasicOCSPRespGenerator.cs
+++ b/crypto/src/ocsp/BasicOCSPRespGenerator.cs
@@ -9,7 +9,6 @@ using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.Operators;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Security.Certificates;
-using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.X509;
 
 namespace Org.BouncyCastle.Ocsp
@@ -28,8 +27,8 @@ namespace Org.BouncyCastle.Ocsp
 		{
 			internal CertificateID         certId;
 			internal CertStatus            certStatus;
-			internal DerGeneralizedTime    thisUpdate;
-			internal DerGeneralizedTime    nextUpdate;
+			internal Asn1GeneralizedTime   thisUpdate;
+			internal Asn1GeneralizedTime   nextUpdate;
 			internal X509Extensions        extensions;
 
 			internal ResponseObject(
@@ -57,7 +56,7 @@ namespace Org.BouncyCastle.Ocsp
 						:	null;
 
 					this.certStatus = new CertStatus(
-						new RevokedInfo(new DerGeneralizedTime(rs.RevocationTime), revocationReason));
+						new RevokedInfo(new Asn1GeneralizedTime(rs.RevocationTime), revocationReason));
 				}
 
 				this.thisUpdate = new DerGeneralizedTime(thisUpdate);
@@ -187,7 +186,7 @@ namespace Org.BouncyCastle.Ocsp
 				}
 			}
 
-			ResponseData tbsResp = new ResponseData(responderID.ToAsn1Object(), new DerGeneralizedTime(producedAt),
+			ResponseData tbsResp = new ResponseData(responderID.ToAsn1Object(), new Asn1GeneralizedTime(producedAt),
 				new DerSequence(responses), responseExtensions);
 			DerBitString bitSig;
 
diff --git a/crypto/src/ocsp/RevokedStatus.cs b/crypto/src/ocsp/RevokedStatus.cs
index edbeb57da..903e50ef5 100644
--- a/crypto/src/ocsp/RevokedStatus.cs
+++ b/crypto/src/ocsp/RevokedStatus.cs
@@ -24,7 +24,7 @@ namespace Org.BouncyCastle.Ocsp
 			DateTime	revocationDate,
 			int			reason)
 		{
-			this.info = new RevokedInfo(new DerGeneralizedTime(revocationDate), new CrlReason(reason));
+			this.info = new RevokedInfo(new Asn1GeneralizedTime(revocationDate), new CrlReason(reason));
 		}
 
 		public DateTime RevocationTime
diff --git a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
index a1c3ce734..88affe53d 100644
--- a/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
+++ b/crypto/src/pkix/PkixCertPathValidatorUtilities.cs
@@ -630,11 +630,11 @@ namespace Org.BouncyCastle.Pkix
 
 			if (index - 1 == 0)
 			{
-				DerGeneralizedTime dateOfCertgen;
+                Asn1GeneralizedTime dateOfCertgen;
 				try
 				{
 					Asn1OctetString extVal = cert.GetExtensionValue(IsisMttObjectIdentifiers.IdIsisMttATDateOfCertGen);
-					dateOfCertgen = DerGeneralizedTime.GetInstance(extVal);
+					dateOfCertgen = Asn1GeneralizedTime.GetInstance(extVal);
 				}
 				catch (ArgumentException)
 				{
diff --git a/crypto/src/tsp/TimeStampTokenGenerator.cs b/crypto/src/tsp/TimeStampTokenGenerator.cs
index 79b8c6a3a..9e6a21f9c 100644
--- a/crypto/src/tsp/TimeStampTokenGenerator.cs
+++ b/crypto/src/tsp/TimeStampTokenGenerator.cs
@@ -334,21 +334,18 @@ namespace Org.BouncyCastle.Tsp
                 respExtensions = extGen.Generate();
             }
 
-
-
-            DerGeneralizedTime generalizedTime;
-            if (resolution != Resolution.R_SECONDS)
+            Asn1GeneralizedTime timeStampTime;
+            if (resolution == Resolution.R_SECONDS)
             {
-                generalizedTime = new DerGeneralizedTime(createGeneralizedTime(genTime));
-            } 
+                timeStampTime = new Asn1GeneralizedTime(genTime);
+            }
             else
             {
-                generalizedTime = new DerGeneralizedTime(genTime);
-            }
-
+                timeStampTime = CreateGeneralizedTime(genTime);
+            } 
 
             TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint,
-                new DerInteger(serialNumber), generalizedTime, accuracy,
+                new DerInteger(serialNumber), timeStampTime, accuracy,
                 derOrdering, nonce, tsa, respExtensions);
 
             try
@@ -382,13 +379,13 @@ namespace Org.BouncyCastle.Tsp
             {
                 throw new TspException("Exception encoding info", e);
             }
-            //			catch (InvalidAlgorithmParameterException e)
-            //			{
-            //				throw new TspException("Exception handling CertStore CRLs", e);
-            //			}
+            //catch (InvalidAlgorithmParameterException e)
+            //{
+            //    throw new TspException("Exception handling CertStore CRLs", e);
+            //}
         }
 
-        private string createGeneralizedTime(DateTime genTime)
+        private Asn1GeneralizedTime CreateGeneralizedTime(DateTime genTime)
         {
             string format = "yyyyMMddHHmmss.fff";
            
@@ -398,33 +395,31 @@ namespace Org.BouncyCastle.Tsp
             if (dotIndex <0)
             {
                 sBuild.Append("Z");
-                return sBuild.ToString();
+                return new Asn1GeneralizedTime(sBuild.ToString());
             }
 
             switch(resolution)
             {
-                case Resolution.R_TENTHS_OF_SECONDS:
-                    if (sBuild.Length > dotIndex + 2)
-                    {
-                        sBuild.Remove(dotIndex + 2, sBuild.Length-(dotIndex+2));
-                    }
-                    break;
-                case Resolution.R_HUNDREDTHS_OF_SECONDS:
-                    if (sBuild.Length > dotIndex + 3)
-                    {
-                        sBuild.Remove(dotIndex + 3, sBuild.Length-(dotIndex+3));
-                    }
-                    break;
+            case Resolution.R_TENTHS_OF_SECONDS:
+                if (sBuild.Length > dotIndex + 2)
+                {
+                    sBuild.Remove(dotIndex + 2, sBuild.Length-(dotIndex+2));
+                }
+                break;
+            case Resolution.R_HUNDREDTHS_OF_SECONDS:
+                if (sBuild.Length > dotIndex + 3)
+                {
+                    sBuild.Remove(dotIndex + 3, sBuild.Length-(dotIndex+3));
+                }
+                break;
 
 
-                case Resolution.R_SECONDS:
-                case Resolution.R_MILLISECONDS:
-                    // do nothing.
-                    break;
-             
+            case Resolution.R_SECONDS:
+            case Resolution.R_MILLISECONDS:
+                // do nothing.
+                break;
             }
 
-           
             while (sBuild[sBuild.Length - 1] == '0')
             {
                 sBuild.Remove(sBuild.Length - 1,1);
@@ -436,7 +431,7 @@ namespace Org.BouncyCastle.Tsp
             }
 
             sBuild.Append("Z");
-            return sBuild.ToString();
+            return new Asn1GeneralizedTime(sBuild.ToString());
         }
 
         private class TableGen
diff --git a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
index 1cb239e87..3a0a02ea9 100644
--- a/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
+++ b/crypto/src/x509/X509V2AttributeCertificateGenerator.cs
@@ -54,13 +54,13 @@ namespace Org.BouncyCastle.X509
 		public void SetNotBefore(
 			DateTime date)
 		{
-			acInfoGen.SetStartDate(new DerGeneralizedTime(date));
+			acInfoGen.SetStartDate(new Asn1GeneralizedTime(date));
 		}
 
 		public void SetNotAfter(
 			DateTime date)
 		{
-			acInfoGen.SetEndDate(new DerGeneralizedTime(date));
+			acInfoGen.SetEndDate(new Asn1GeneralizedTime(date));
 		}
 
 		/// <summary>Add an attribute.</summary>
diff --git a/crypto/src/x509/X509V2CRLGenerator.cs b/crypto/src/x509/X509V2CRLGenerator.cs
index dc3f8c662..a57383613 100644
--- a/crypto/src/x509/X509V2CRLGenerator.cs
+++ b/crypto/src/x509/X509V2CRLGenerator.cs
@@ -79,7 +79,8 @@ namespace Org.BouncyCastle.X509
 			int			reason,
 			DateTime	invalidityDate)
 		{
-			tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason, new DerGeneralizedTime(invalidityDate));
+			tbsGen.AddCrlEntry(new DerInteger(userCertificate), new Time(revocationDate), reason,
+				new Asn1GeneralizedTime(invalidityDate));
 		}
 
 		/**
diff --git a/crypto/test/src/asn1/test/DeclarationOfMajorityUnitTest.cs b/crypto/test/src/asn1/test/DeclarationOfMajorityUnitTest.cs
index 0975ea1b8..3af922a5c 100644
--- a/crypto/test/src/asn1/test/DeclarationOfMajorityUnitTest.cs
+++ b/crypto/test/src/asn1/test/DeclarationOfMajorityUnitTest.cs
@@ -17,7 +17,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 
 		public override void PerformTest()
 		{
-			DerGeneralizedTime dateOfBirth = new DerGeneralizedTime("20070315173729Z");
+			Asn1GeneralizedTime dateOfBirth = new Asn1GeneralizedTime("20070315173729Z");
 			DeclarationOfMajority decl = new DeclarationOfMajority(dateOfBirth);
 
 			CheckConstruction(decl, DeclarationOfMajority.Choice.DateOfBirth, dateOfBirth, -1);
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 		private void CheckConstruction(
 			DeclarationOfMajority			decl,
 			DeclarationOfMajority.Choice	type,
-			DerGeneralizedTime				dateOfBirth,
+            Asn1GeneralizedTime				dateOfBirth,
 			int								notYoungerThan)
 		{
 			CheckValues(decl, type, dateOfBirth, notYoungerThan);
@@ -65,7 +65,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 		private void CheckValues(
 			DeclarationOfMajority			decl,
 			DeclarationOfMajority.Choice	type,
-			DerGeneralizedTime				dateOfBirth,
+            Asn1GeneralizedTime				dateOfBirth,
 			int								notYoungerThan)
 		{
 			checkMandatoryField("type", (int) type, (int) decl.Type);
diff --git a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
index d9d84462a..1cc1d8433 100644
--- a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
+++ b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
@@ -163,7 +163,7 @@ namespace Org.BouncyCastle.Asn1.Tests
             {
                 string ii = input[i], oi = output[i];
 
-                DerGeneralizedTime t = new DerGeneralizedTime(ii);
+                Asn1GeneralizedTime t = new Asn1GeneralizedTime(ii);
                 DateTime dt = t.ToDateTime();
                 string st = t.GetTime();
 
@@ -171,14 +171,14 @@ namespace Org.BouncyCastle.Asn1.Tests
                 {
                     if (!st.Equals(oi))
                     {
-                        Fail("failed conversion test");
+                        Fail("failed GMT conversion test " + i);
                     }
 
                     string dts = dt.ToString(@"yyyyMMddHHmmss\Z");
                     string zi = zOutput[i];
                     if (!dts.Equals(zi))
                     {
-                        Fail("failed date conversion test");
+                        Fail("failed date conversion test " + i);
                     }
                 }
                 else
@@ -186,70 +186,74 @@ namespace Org.BouncyCastle.Asn1.Tests
                     string offset = CalculateGmtOffset(dt);
                     if (!st.Equals(oi + offset))
                     {
-                        Fail("failed conversion test");
+                        Fail("failed conversion test " + i);
                     }
                 }
             }
 
             for (int i = 0; i != input.Length; i++)
             {
-                DerGeneralizedTime t = new DerGeneralizedTime(input[i]);
+                Asn1GeneralizedTime t = new Asn1GeneralizedTime(input[i]);
 
                 if (!t.ToDateTime().ToString(@"yyyyMMddHHmmss.fff\Z").Equals(mzOutput[i]))
                 {
-                    Console.WriteLine("{0} != {1}", t.ToDateTime().ToString(@"yyyyMMddHHmmss.SSS\Z"), mzOutput[i]);
+                    Console.WriteLine("{0} != {1}", t.ToDateTime().ToString(@"yyyyMMddHHmmss.fff\Z"), mzOutput[i]);
 
-                    Fail("failed long date conversion test");
+                    Fail("failed long date conversion test " + i);
                 }
             }
 
-            // TODO
-            //for (int i = 0; i != mzOutput.Length; i++)
-            //{
-            //    DerGeneralizedTime t = new DerGeneralizedTime(mzOutput[i]);
+            for (int i = 0; i != mzOutput.Length; i++)
+            {
+                DerGeneralizedTime t = new DerGeneralizedTime(mzOutput[i]);
 
-            //    if (!AreEqual(t.GetEncoded(), new DerGeneralizedTime(derMzOutput[i]).GetEncoded()))
-            //    {
-            //        Fail("der encoding wrong");
-            //    }
-            //}
+                if (!AreEqual(t.GetEncoded(), new Asn1GeneralizedTime(derMzOutput[i]).GetEncoded()))
+                {
+                    Fail("der encoding wrong");
+                }
+            }
 
-            // TODO
-            //for (int i = 0; i != truncOutput.Length; i++)
-            //{
-            //    DerGeneralizedTime t = new DerGeneralizedTime(truncOutput[i]);
+            for (int i = 0; i != truncOutput.Length; i++)
+            {
+                DerGeneralizedTime t = new DerGeneralizedTime(truncOutput[i]);
 
-            //    if (!AreEqual(t.GetEncoded(), new DerGeneralizedTime(derTruncOutput[i]).GetEncoded()))
-            //    {
-            //        Fail("trunc der encoding wrong");
-            //    }
-            //}
+                if (!AreEqual(t.GetEncoded(), new Asn1GeneralizedTime(derTruncOutput[i]).GetEncoded()))
+                {
+                    Fail("trunc der encoding wrong");
+                }
+            }
 
             {
                 // check BER encoding is still "as given"
-                DerGeneralizedTime t = new DerGeneralizedTime("202208091215Z");
+                Asn1GeneralizedTime ber = new Asn1GeneralizedTime("202208091215Z");
+
+                //IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), ber.GetEncoded(Asn1Encodable.DL)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), ber.GetEncoded(Asn1Encodable.Ber)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), ber.GetEncoded(Asn1Encodable.Der)));
 
-                //IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), t.GetEncoded(Asn1Encodable.DL)));
-                IsTrue(Arrays.AreEqual(Hex.Decode("180d3230323230383039313231355a"), t.GetEncoded(Asn1Encodable.Ber)));
-                IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), t.GetEncoded(Asn1Encodable.Der)));
+                // check always uses DER encoding
+                DerGeneralizedTime der = new DerGeneralizedTime("202208091215Z");
+
+                //IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), der.GetEncoded(Asn1Encodable.DL)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), der.GetEncoded(Asn1Encodable.Ber)));
+                IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), der.GetEncoded(Asn1Encodable.Der)));
             }
 
-            // TODO
-            //{
-            //    // check an actual GMT string comes back untampered
-            //    DerGeneralizedTime time = new DerGeneralizedTime("20190704031318GMT+00:00");
+            {
+                // check an actual GMT string comes back untampered
+                Asn1GeneralizedTime time = new Asn1GeneralizedTime("20190704031318GMT+00:00");
 
-            //    IsTrue("20190704031318GMT+00:00".Equals(time.GetTime()));
+                IsTrue("20190704031318GMT+00:00".Equals(time.GetTime()));
+            }
 
-            //    try
-            //    {
-            //        DerGeneralizedTime.GetInstance(new byte[0]);
-            //    }
-            //    catch (ArgumentException e)
-            //    {
-            //        IsTrue(e.Message.Equals("GeneralizedTime string too short"));
-            //    }
-            //}
+            try
+            {
+                new DerGeneralizedTime(new byte[0]);
+            }
+            catch (ArgumentException e)
+            {
+                IsTrue(e.Message.StartsWith("GeneralizedTime string too short"));
+            }
 
             /*
              * [BMA-87]
@@ -275,17 +279,24 @@ namespace Org.BouncyCastle.Asn1.Tests
 
         private string CalculateGmtOffset(DateTime date)
         {
-            char sign = '+';
+            TimeZoneInfo timeZone = TimeZoneInfo.Local;
+            TimeSpan offset = timeZone.BaseUtcOffset;
 
-            TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(date);
+            char sign = '+';
             if (offset.CompareTo(TimeSpan.Zero) < 0)
             {
                 sign = '-';
                 offset = offset.Duration();
             }
+
             int hours = offset.Hours;
             int minutes = offset.Minutes;
 
+            if (timeZone.SupportsDaylightSavingTime && timeZone.IsDaylightSavingTime(date))
+            {
+                hours += sign.Equals("+") ? 1 : -1;
+            }
+
             return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
         }
 
diff --git a/crypto/test/src/asn1/test/PersonalDataUnitTest.cs b/crypto/test/src/asn1/test/PersonalDataUnitTest.cs
index b965578c1..7f79fa743 100644
--- a/crypto/test/src/asn1/test/PersonalDataUnitTest.cs
+++ b/crypto/test/src/asn1/test/PersonalDataUnitTest.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 		{
 			NameOrPseudonym nameOrPseudonym = new NameOrPseudonym("pseudonym");
 			BigInteger nameDistinguisher = BigInteger.ValueOf(10);
-			DerGeneralizedTime dateOfBirth= new DerGeneralizedTime("20070315173729Z");
+            Asn1GeneralizedTime dateOfBirth = new Asn1GeneralizedTime("20070315173729Z");
 			DirectoryString placeOfBirth = new DirectoryString("placeOfBirth");
 			string gender = "M";
 			DirectoryString postalAddress = new DirectoryString("address");
@@ -73,7 +73,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 			PersonalData		data,
 			NameOrPseudonym		nameOrPseudonym,
 			BigInteger			nameDistinguisher,
-			DerGeneralizedTime	dateOfBirth,
+            Asn1GeneralizedTime dateOfBirth,
 			DirectoryString		placeOfBirth,
 			string				gender,
 			DirectoryString		postalAddress)
@@ -97,7 +97,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 			PersonalData		data,
 			NameOrPseudonym		nameOrPseudonym,
 			BigInteger			nameDistinguisher,
-			DerGeneralizedTime	dateOfBirth,
+            Asn1GeneralizedTime dateOfBirth,
 			DirectoryString		placeOfBirth,
 			string				gender,
 			DirectoryString		postalAddress)
diff --git a/crypto/test/src/asn1/test/X509NameTest.cs b/crypto/test/src/asn1/test/X509NameTest.cs
index bafec3226..098500a0b 100644
--- a/crypto/test/src/asn1/test/X509NameTest.cs
+++ b/crypto/test/src/asn1/test/X509NameTest.cs
@@ -100,12 +100,12 @@ namespace Org.BouncyCastle.Asn1.Tests
             string				val)
         {
             IAsn1Convertible converted = createEntryValue(oid, val);
-            if (!(converted is DerGeneralizedTime))
+            if (!(converted is Asn1GeneralizedTime))
             {
                 Fail("encoding for " + oid + " not GeneralizedTime");
             }
             converted = createEntryValueFromString(oid, val);
-            if (!(converted is DerGeneralizedTime))
+            if (!(converted is Asn1GeneralizedTime))
             {
                 Fail("encoding for " + oid + " not GeneralizedTime");
             }