From cf2633e7cd4f2d29f4e3e56a3d3b81b11e254a41 Mon Sep 17 00:00:00 2001
From: Peter Dettman
Date: Mon, 7 Nov 2022 20:04:44 +0700
Subject: Overhaul UTCTime classes
---
crypto/src/asn1/Asn1UtcTime.cs | 295 ++++++++++-----------
crypto/src/asn1/DerUTCTime.cs | 27 +-
crypto/src/asn1/cms/Time.cs | 112 ++++----
crypto/src/asn1/esf/CrlIdentifier.cs | 43 +--
crypto/src/asn1/x509/Time.cs | 84 +++---
crypto/src/cms/SignerInformation.cs | 2 +-
crypto/src/x509/X509Certificate.cs | 4 +-
crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs | 9 +-
crypto/test/src/asn1/test/TimeTest.cs | 7 +-
crypto/test/src/asn1/test/UTCTimeTest.cs | 76 ++----
crypto/test/src/test/nist/NistCertPathTest.cs | 10 +-
11 files changed, 332 insertions(+), 337 deletions(-)
diff --git a/crypto/src/asn1/Asn1UtcTime.cs b/crypto/src/asn1/Asn1UtcTime.cs
index 05de430c4..478b5c485 100644
--- a/crypto/src/asn1/Asn1UtcTime.cs
+++ b/crypto/src/asn1/Asn1UtcTime.cs
@@ -1,14 +1,13 @@
using System;
using System.Globalization;
using System.IO;
+using System.Text;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Asn1
{
- /**
- * UTC time object.
- */
+ /// UTCTime ASN.1 type
public class Asn1UtcTime
: Asn1Object
{
@@ -55,210 +54,206 @@ namespace Org.BouncyCastle.Asn1
}
}
- throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
+ throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), nameof(obj));
}
- /**
- * return a UTC Time 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 Asn1UtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
{
return (Asn1UtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
}
- private readonly string time;
+ private readonly string m_timeString;
+ private readonly DateTime m_dateTime;
+ private readonly bool m_dateTimeLocked;
+ private readonly int m_twoDigitYearMax;
- /**
- * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
- * never encoded. When you're creating one of these objects from scratch, that's
- * what you want to use, otherwise we'll try to deal with whatever Gets read from
- * the input stream... (this is why the input format is different from the GetTime()
- * method output).
- *
- * @param time the time string.
- */
- public Asn1UtcTime(string time)
- {
- if (time == null)
- throw new ArgumentNullException("time");
+ public Asn1UtcTime(string timeString)
+ {
+ if (timeString == null)
+ throw new ArgumentNullException(nameof(timeString));
- this.time = time;
+ m_timeString = timeString;
try
{
- ToDateTime();
- }
- catch (FormatException e)
+ m_dateTime = FromString(timeString, out m_twoDigitYearMax);
+ m_dateTimeLocked = false;
+ }
+ catch (FormatException e)
{
throw new ArgumentException("invalid date string: " + e.Message);
}
- }
+ }
- /**
- * base constructor from a DateTime object
- */
- public Asn1UtcTime(DateTime time)
- {
- this.time = time.ToUniversalTime().ToString("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
+ [Obsolete("Use `Asn1UtcTime(DateTime, int)' instead")]
+ public Asn1UtcTime(DateTime dateTime)
+ {
+ DateTime utc = dateTime.ToUniversalTime();
+ dateTime = new DateTime(utc.Year, utc.Month, utc.Day, utc.Hour, utc.Minute, utc.Second, DateTimeKind.Utc);
+
+ m_dateTime = dateTime;
+ m_dateTimeLocked = true;
+ m_timeString = ToStringCanonical(dateTime, out m_twoDigitYearMax);
}
- internal Asn1UtcTime(byte[] contents)
+ public Asn1UtcTime(DateTime dateTime, int twoDigitYearMax)
{
- //
- // explicitly convert to characters
- //
- this.time = Strings.FromAsciiByteArray(contents);
+ DateTime utc = dateTime.ToUniversalTime();
+ dateTime = new DateTime(utc.Year, utc.Month, utc.Day, utc.Hour, utc.Minute, utc.Second, DateTimeKind.Utc);
+
+ Validate(dateTime, twoDigitYearMax);
+
+ m_dateTime = dateTime;
+ m_dateTimeLocked = true;
+ m_timeString = ToStringCanonical(dateTime);
+ m_twoDigitYearMax = twoDigitYearMax;
}
- /**
- * return the time as a date based on whatever a 2 digit year will return. For
- * standardised processing use ToAdjustedDateTime().
- *
- * @return the resulting date
- * @exception ParseException if the date string cannot be parsed.
- */
- public DateTime ToDateTime()
+ internal Asn1UtcTime(byte[] contents)
+ // NOTE: Non-ASCII characters will produce '?' characters, which will fail DateTime parsing
+ : this(Encoding.ASCII.GetString(contents))
{
- return ParseDateString(TimeString, @"yyMMddHHmmss'GMT'zzz");
}
- /**
- * return the time as an adjusted date
- * in the range of 1950 - 2049.
- *
- * @return a date in the range of 1950 to 2049.
- * @exception ParseException if the date string cannot be parsed.
- */
- public DateTime ToAdjustedDateTime()
+ public string TimeString => m_timeString;
+
+ public DateTime ToDateTime()
{
- return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz");
+ return m_dateTime;
}
- private DateTime ParseDateString(string dateStr, string formatStr)
- {
- DateTime dt = DateTime.ParseExact(
- dateStr,
- formatStr,
- DateTimeFormatInfo.InvariantInfo);
+ public DateTime ToDateTime(int twoDigitYearMax)
+ {
+ if (InRange(m_dateTime, twoDigitYearMax))
+ return m_dateTime;
- return dt.ToUniversalTime();
- }
+ if (m_dateTimeLocked)
+ throw new InvalidOperationException();
- /**
- * return the time - always in the form of
- * YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
- *
- * Normally in a certificate we would expect "Z" rather than "GMT",
- * however adding the "GMT" means we can just use:
- *
- * dateF = new SimpleDateFormat("yyMMddHHmmssz");
- *
- * To read in the time and Get a date which is compatible with our local
- * time zone.
- *
- * Note: In some cases, due to the local date processing, this
- * may lead to unexpected results. If you want to stick the normal
- * convention of 1950 to 2049 use the GetAdjustedTime() method.
- */
- public string TimeString
+ int twoDigitYear = m_dateTime.Year % 100;
+ int twoDigitYearCutoff = twoDigitYearMax % 100;
+
+ int diff = twoDigitYear - twoDigitYearCutoff;
+ int newYear = twoDigitYearMax + diff;
+ if (diff > 0)
+ {
+ newYear -= 100;
+ }
+
+ return m_dateTime.AddYears(newYear - m_dateTime.Year);
+ }
+
+ public DateTime ToDateTime(Calendar calendar)
{
- get
- {
- //
- // standardise the format.
- //
- if (time.IndexOf('-') < 0 && time.IndexOf('+') < 0)
- {
- if (time.Length == 11)
- {
- return time.Substring(0, 10) + "00GMT+00:00";
- }
- else
- {
- return time.Substring(0, 12) + "GMT+00:00";
- }
- }
- else
- {
- int index = time.IndexOf('-');
- if (index < 0)
- {
- index = time.IndexOf('+');
- }
- string d = time;
-
- if (index == time.Length - 3)
- {
- d += "00";
- }
-
- if (index == 10)
- {
- return d.Substring(0, 10) + "00GMT" + d.Substring(10, 3) + ":" + d.Substring(13, 2);
- }
- else
- {
- return d.Substring(0, 12) + "GMT" + d.Substring(12, 3) + ":" + d.Substring(15, 2);
- }
- }
- }
+ return ToDateTime(calendar.TwoDigitYearMax);
}
- ///
- /// Return a time string as an adjusted date with a 4 digit year.
- /// This goes in the range of 1950 - 2049.
- ///
- public string AdjustedTimeString
- {
- get
- {
- string d = TimeString;
- string c = d[0] < '5' ? "20" : "19";
+ /// Return an adjusted date in the range of 1950 - 2049.
+ [Obsolete("Use 'ToDateTime(2049)' instead")]
+ public DateTime ToAdjustedDateTime()
+ {
+ return ToDateTime(2049);
+ }
- return c + d;
- }
- }
+ public int TwoDigitYearMax => m_twoDigitYearMax;
- internal byte[] GetOctets()
+ internal byte[] GetContents(int encoding)
{
- return Strings.ToAsciiByteArray(time);
+ if (encoding == Asn1OutputStream.EncodingDer && m_timeString.Length != 13)
+ {
+ string canonical = ToStringCanonical(m_dateTime);
+ return Encoding.ASCII.GetBytes(canonical);
+ }
+
+ return Encoding.ASCII.GetBytes(m_timeString);
}
internal override IAsn1Encoding GetEncoding(int encoding)
{
- return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime, GetOctets());
+ return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime, GetContents(encoding));
}
internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
{
- return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+ return new PrimitiveEncoding(tagClass, tagNo, GetContents(encoding));
}
protected override bool Asn1Equals(Asn1Object asn1Object)
- {
+ {
if (!(asn1Object is Asn1UtcTime that))
return false;
- return this.time == that.time;
+ // TODO Performance
+ return Arrays.AreEqual(
+ this.GetContents(Asn1OutputStream.EncodingDer),
+ that.GetContents(Asn1OutputStream.EncodingDer));
}
- protected override int Asn1GetHashCode()
- {
- return time.GetHashCode();
+ protected override int Asn1GetHashCode()
+ {
+ // TODO Performance
+ return Arrays.GetHashCode(
+ this.GetContents(Asn1OutputStream.EncodingDer));
}
- public override string ToString()
- {
- return time;
- }
+ public override string ToString()
+ {
+ return m_timeString;
+ }
internal static Asn1UtcTime CreatePrimitive(byte[] contents)
{
return new Asn1UtcTime(contents);
}
+
+ private static DateTime FromString(string s, out int twoDigitYearMax)
+ {
+ var provider = DateTimeFormatInfo.InvariantInfo;
+ twoDigitYearMax = provider.Calendar.TwoDigitYearMax;
+
+ switch (s.Length)
+ {
+ case 11:
+ return DateTime.ParseExact(s, @"yyMMddHHmm\Z", provider,
+ DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
+ case 13:
+ return DateTime.ParseExact(s, @"yyMMddHHmmss\Z", provider,
+ DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
+ case 15:
+ return DateTime.ParseExact(s, @"yyMMddHHmmzzz", provider,
+ DateTimeStyles.AdjustToUniversal);
+ case 17:
+ return DateTime.ParseExact(s, @"yyMMddHHmmsszzz", provider,
+ DateTimeStyles.AdjustToUniversal);
+ default:
+ throw new FormatException();
+ }
+ }
+
+ private static bool InRange(DateTime dateTime, int twoDigitYearMax)
+ {
+ return (uint)(twoDigitYearMax - dateTime.Year) < 100;
+ }
+
+ private static string ToStringCanonical(DateTime dateTime, out int twoDigitYearMax)
+ {
+ var provider = DateTimeFormatInfo.InvariantInfo;
+ twoDigitYearMax = provider.Calendar.TwoDigitYearMax;
+
+ Validate(dateTime, twoDigitYearMax);
+
+ return dateTime.ToString(@"yyMMddHHmmss\Z", provider);
+ }
+
+ private static string ToStringCanonical(DateTime dateTime)
+ {
+ return dateTime.ToString(@"yyMMddHHmmss\Z", DateTimeFormatInfo.InvariantInfo);
+ }
+
+ private static void Validate(DateTime dateTime, int twoDigitYearMax)
+ {
+ if (!InRange(dateTime, twoDigitYearMax))
+ throw new ArgumentOutOfRangeException(nameof(dateTime));
+ }
}
}
diff --git a/crypto/src/asn1/DerUTCTime.cs b/crypto/src/asn1/DerUTCTime.cs
index 089285367..33e502c67 100644
--- a/crypto/src/asn1/DerUTCTime.cs
+++ b/crypto/src/asn1/DerUTCTime.cs
@@ -5,21 +5,36 @@ namespace Org.BouncyCastle.Asn1
public class DerUtcTime
: Asn1UtcTime
{
- public DerUtcTime(string time)
- : base(time)
+ public DerUtcTime(string timeString)
+ : base(timeString)
{
}
- public DerUtcTime(DateTime time)
- : base(time)
+ [Obsolete("Use `DerUtcTime(DateTime, int)' instead")]
+ public DerUtcTime(DateTime dateTime)
+ : base(dateTime)
{
}
- internal DerUtcTime(byte[] contents)
+ public DerUtcTime(DateTime dateTime, int twoDigitYearMax)
+ : base(dateTime, twoDigitYearMax)
+ {
+ }
+
+ internal DerUtcTime(byte[] contents)
: base(contents)
{
}
- // TODO: create proper DER encoding.
+ internal override IAsn1Encoding GetEncoding(int encoding)
+ {
+ return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime,
+ GetContents(Asn1OutputStream.EncodingDer));
+ }
+
+ internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
+ {
+ return new PrimitiveEncoding(tagClass, tagNo, GetContents(Asn1OutputStream.EncodingDer));
+ }
}
}
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
index 67c73285b..f054e3809 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -8,24 +8,41 @@ namespace Org.BouncyCastle.Asn1.Cms
public class Time
: Asn1Encodable, IAsn1Choice
{
- private readonly Asn1Object time;
+ public static Time GetInstance(object obj)
+ {
+ if (obj == null)
+ return null;
+ if (obj is Time time)
+ return time;
+ if (obj is Asn1UtcTime utcTime)
+ return new Time(utcTime);
+ if (obj is Asn1GeneralizedTime generalizedTime)
+ return new Time(generalizedTime);
+
+ throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), nameof(obj));
+ }
- public static Time GetInstance(
- Asn1TaggedObject obj,
- bool explicitly)
+ public static Time GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
{
- return GetInstance(obj.GetObject());
+ return GetInstance(taggedObject.GetObject());
}
- public Time(
- Asn1Object time)
+ private readonly Asn1Object m_timeObject;
+
+ public Time(Asn1GeneralizedTime generalizedTime)
+ {
+ this.m_timeObject = generalizedTime ?? throw new ArgumentNullException(nameof(generalizedTime));
+ }
+
+ public Time(Asn1UtcTime utcTime)
{
- if (time == null)
- throw new ArgumentNullException("time");
- if (!(time is Asn1UtcTime) && !(time is Asn1GeneralizedTime))
- throw new ArgumentException("unknown object passed to Time");
+ if (utcTime == null)
+ throw new ArgumentNullException(nameof(utcTime));
- this.time = time;
+ // Validate utcTime is in the appropriate year range
+ utcTime.ToDateTime(2049);
+
+ this.m_timeObject = utcTime;
}
/**
@@ -35,61 +52,36 @@ namespace Org.BouncyCastle.Asn1.Cms
*/
public Time(DateTime date)
{
- DateTime d = date.ToUniversalTime();
+ DateTime utc = date.ToUniversalTime();
- if (d.Year < 1950 || d.Year > 2049)
+ if (utc.Year < 1950 || utc.Year > 2049)
{
- time = new DerGeneralizedTime(d);
+ m_timeObject = new DerGeneralizedTime(utc);
}
else
{
- time = new DerUtcTime(d);
+ m_timeObject = new DerUtcTime(utc, 2049);
}
}
- public static Time GetInstance(object obj)
- {
- if (obj == null)
- return null;
- if (obj is Time time)
- return time;
- if (obj is Asn1UtcTime utcTime)
- return new Time(utcTime);
- if (obj is Asn1GeneralizedTime generalizedTime)
- return new Time(generalizedTime);
-
- throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
- }
-
- public string TimeString
+ public DateTime ToDateTime()
{
- get
- {
- if (time is Asn1UtcTime utcTime)
- return utcTime.AdjustedTimeString;
+ try
+ {
+ if (m_timeObject is Asn1UtcTime utcTime)
+ return utcTime.ToDateTime(2049);
- return ((Asn1GeneralizedTime)time).GetTime();
- }
+ return ((Asn1GeneralizedTime)m_timeObject).ToDateTime();
+ }
+ catch (FormatException e)
+ {
+ // this should never happen
+ throw new InvalidOperationException("invalid date string: " + e.Message);
+ }
}
- public DateTime Date
- {
- get
- {
- try
- {
- if (time is Asn1UtcTime utcTime)
- return utcTime.ToAdjustedDateTime();
-
- return ((Asn1GeneralizedTime)time).ToDateTime();
- }
- catch (FormatException e)
- {
- // this should never happen
- throw new InvalidOperationException("invalid date string: " + e.Message);
- }
- }
- }
+ [Obsolete("Use 'ToDateTime' instead")]
+ public DateTime Date => ToDateTime();
/**
* Produce an object suitable for an Asn1OutputStream.
@@ -101,7 +93,15 @@ namespace Org.BouncyCastle.Asn1.Cms
*/
public override Asn1Object ToAsn1Object()
{
- return time;
+ return m_timeObject;
+ }
+
+ public override string ToString()
+ {
+ if (m_timeObject is Asn1UtcTime utcTime)
+ return utcTime.ToDateTime(2049).ToString(@"yyyyMMddHHmmssK", DateTimeFormatInfo.InvariantInfo);
+
+ return ((Asn1GeneralizedTime)m_timeObject).GetTime();
}
}
}
diff --git a/crypto/src/asn1/esf/CrlIdentifier.cs b/crypto/src/asn1/esf/CrlIdentifier.cs
index 44c99170c..4a2480b4b 100644
--- a/crypto/src/asn1/esf/CrlIdentifier.cs
+++ b/crypto/src/asn1/esf/CrlIdentifier.cs
@@ -20,36 +20,38 @@ namespace Org.BouncyCastle.Asn1.Esf
public class CrlIdentifier
: Asn1Encodable
{
- private readonly X509Name crlIssuer;
+ private readonly X509Name crlIssuer;
private readonly Asn1UtcTime crlIssuedTime;
- private readonly DerInteger crlNumber;
+ private readonly DerInteger crlNumber;
- public static CrlIdentifier GetInstance(
- object obj)
+ public static CrlIdentifier GetInstance(object obj)
{
- if (obj == null || obj is CrlIdentifier)
- return (CrlIdentifier) obj;
+ if (obj == null)
+ return null;
- if (obj is Asn1Sequence)
- return new CrlIdentifier((Asn1Sequence) obj);
+ if (obj is CrlIdentifier crlIdentifier)
+ return crlIdentifier;
- throw new ArgumentException(
- "Unknown object in 'CrlIdentifier' factory: "
- + Platform.GetTypeName(obj),
- "obj");
+ if (obj is Asn1Sequence asn1Sequence)
+ return new CrlIdentifier(asn1Sequence);
+
+ throw new ArgumentException("Unknown object in 'CrlIdentifier' factory: " + Platform.GetTypeName(obj),
+ nameof(obj));
}
- private CrlIdentifier(
- Asn1Sequence seq)
+ private CrlIdentifier(Asn1Sequence seq)
{
if (seq == null)
- throw new ArgumentNullException("seq");
+ throw new ArgumentNullException(nameof(seq));
if (seq.Count < 2 || seq.Count > 3)
- throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
+ throw new ArgumentException("Bad sequence size: " + seq.Count, nameof(seq));
this.crlIssuer = X509Name.GetInstance(seq[0]);
this.crlIssuedTime = Asn1UtcTime.GetInstance(seq[1]);
+ // Validate crlIssuedTime is in the appropriate year range
+ crlIssuedTime.ToDateTime(2049);
+
if (seq.Count > 2)
{
this.crlNumber = DerInteger.GetInstance(seq[2]);
@@ -62,7 +64,7 @@ namespace Org.BouncyCastle.Asn1.Esf
}
public CrlIdentifier(X509Name crlIssuer, DateTime crlIssuedTime, BigInteger crlNumber)
- : this(crlIssuer, new Asn1UtcTime(crlIssuedTime), crlNumber)
+ : this(crlIssuer, new Asn1UtcTime(crlIssuedTime, 2049), crlNumber)
{
}
@@ -75,6 +77,11 @@ namespace Org.BouncyCastle.Asn1.Esf
{
if (crlIssuer == null)
throw new ArgumentNullException(nameof(crlIssuer));
+ if (crlIssuedTime == null)
+ throw new ArgumentNullException(nameof(crlIssuedTime));
+
+ // Validate crlIssuedTime is in the appropriate year range
+ crlIssuedTime.ToDateTime(2049);
this.crlIssuer = crlIssuer;
this.crlIssuedTime = crlIssuedTime;
@@ -92,7 +99,7 @@ namespace Org.BouncyCastle.Asn1.Esf
public DateTime CrlIssuedTime
{
- get { return crlIssuedTime.ToAdjustedDateTime(); }
+ get { return crlIssuedTime.ToDateTime(2049); }
}
public BigInteger CrlNumber
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index 8260043aa..7f2d43315 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -8,24 +8,41 @@ namespace Org.BouncyCastle.Asn1.X509
public class Time
: Asn1Encodable, IAsn1Choice
{
- private readonly Asn1Object time;
+ public static Time GetInstance(object obj)
+ {
+ if (obj == null)
+ return null;
+ if (obj is Time time)
+ return time;
+ if (obj is Asn1UtcTime utcTime)
+ return new Time(utcTime);
+ if (obj is Asn1GeneralizedTime generalizedTime)
+ return new Time(generalizedTime);
+
+ throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), nameof(obj));
+ }
- public static Time GetInstance(
- Asn1TaggedObject obj,
- bool explicitly)
+ public static Time GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
{
- return GetInstance(obj.GetObject());
+ return GetInstance(taggedObject.GetObject());
}
- public Time(
- Asn1Object time)
+ private readonly Asn1Object m_timeObject;
+
+ public Time(Asn1GeneralizedTime generalizedTime)
{
- if (time == null)
- throw new ArgumentNullException("time");
- if (!(time is Asn1UtcTime) && !(time is Asn1GeneralizedTime))
- throw new ArgumentException("unknown object passed to Time");
+ this.m_timeObject = generalizedTime ?? throw new ArgumentNullException(nameof(generalizedTime));
+ }
- this.time = time;
+ public Time(Asn1UtcTime utcTime)
+ {
+ if (utcTime == null)
+ throw new ArgumentNullException(nameof(utcTime));
+
+ // Validate utcTime is in the appropriate year range
+ utcTime.ToDateTime(2049);
+
+ this.m_timeObject = utcTime;
}
/**
@@ -35,40 +52,18 @@ namespace Org.BouncyCastle.Asn1.X509
*/
public Time(DateTime date)
{
- DateTime d = date.ToUniversalTime();
+ DateTime utc = date.ToUniversalTime();
- if (d.Year < 1950 || d.Year > 2049)
+ if (utc.Year < 1950 || utc.Year > 2049)
{
- time = new DerGeneralizedTime(d);
+ m_timeObject = new DerGeneralizedTime(utc);
}
else
{
- time = new DerUtcTime(d);
+ m_timeObject = new DerUtcTime(utc, 2049);
}
}
- public static Time GetInstance(object obj)
- {
- if (obj == null)
- return null;
- if (obj is Time time)
- return time;
- if (obj is Asn1UtcTime utcTime)
- return new Time(utcTime);
- if (obj is Asn1GeneralizedTime generalizedTime)
- return new Time(generalizedTime);
-
- throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
- }
-
- public string GetTime()
- {
- if (time is Asn1UtcTime utcTime)
- return utcTime.AdjustedTimeString;
-
- return ((Asn1GeneralizedTime)time).GetTime();
- }
-
///
/// Return our time as DateTime.
///
@@ -77,10 +72,10 @@ namespace Org.BouncyCastle.Asn1.X509
{
try
{
- if (time is Asn1UtcTime utcTime)
- return utcTime.ToAdjustedDateTime();
+ if (m_timeObject is Asn1UtcTime utcTime)
+ return utcTime.ToDateTime(2049);
- return ((Asn1GeneralizedTime)time).ToDateTime();
+ return ((Asn1GeneralizedTime)m_timeObject).ToDateTime();
}
catch (FormatException e)
{
@@ -99,12 +94,15 @@ namespace Org.BouncyCastle.Asn1.X509
*/
public override Asn1Object ToAsn1Object()
{
- return time;
+ return m_timeObject;
}
public override string ToString()
{
- return GetTime();
+ if (m_timeObject is Asn1UtcTime utcTime)
+ return utcTime.ToDateTime(2049).ToString(@"yyyyMMddHHmmssK", DateTimeFormatInfo.InvariantInfo);
+
+ return ((Asn1GeneralizedTime)m_timeObject).GetTime();
}
}
}
diff --git a/crypto/src/cms/SignerInformation.cs b/crypto/src/cms/SignerInformation.cs
index 99d300121..6454e6d3d 100644
--- a/crypto/src/cms/SignerInformation.cs
+++ b/crypto/src/cms/SignerInformation.cs
@@ -651,7 +651,7 @@ namespace Org.BouncyCastle.Cms
Asn1.Cms.Time signingTime = GetSigningTime();
if (signingTime != null)
{
- cert.CheckValidity(signingTime.Date);
+ cert.CheckValidity(signingTime.ToDateTime());
}
return DoVerify(cert.GetPublicKey());
diff --git a/crypto/src/x509/X509Certificate.cs b/crypto/src/x509/X509Certificate.cs
index 627903e1f..510f95b01 100644
--- a/crypto/src/x509/X509Certificate.cs
+++ b/crypto/src/x509/X509Certificate.cs
@@ -205,9 +205,9 @@ namespace Org.BouncyCastle.X509
DateTime time)
{
if (time.CompareTo(NotAfter) > 0)
- throw new CertificateExpiredException("certificate expired on " + c.EndDate.GetTime());
+ throw new CertificateExpiredException("certificate expired on " + c.EndDate);
if (time.CompareTo(NotBefore) < 0)
- throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate.GetTime());
+ throw new CertificateNotYetValidException("certificate not valid until " + c.StartDate);
}
///
diff --git a/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs b/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
index 83f30fd73..fb035de18 100644
--- a/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
+++ b/crypto/test/src/asn1/test/EqualsAndHashCodeTest.cs
@@ -28,8 +28,8 @@ namespace Org.BouncyCastle.Asn1.Tests
DerBoolean.True,
DerBoolean.False,
new DerEnumerated(100),
- new DerGeneralizedTime("20070315173729Z"),
- new DerGeneralString("hello world"),
+ new DerGeneralizedTime("20070315173729Z"),
+ new DerGeneralString("hello world"),
new DerIA5String("hello"),
new DerInteger(1000),
DerNull.Instance,
@@ -43,7 +43,10 @@ namespace Org.BouncyCastle.Asn1.Tests
new DerT61String("hello world"),
new DerTaggedObject(0, new DerPrintableString("hello world")),
new DerUniversalString(data),
- new DerUtcTime(new DateTime()),
+#pragma warning disable CS0618 // Type or member is obsolete
+ new DerUtcTime(DateTime.Now),
+#pragma warning restore CS0618 // Type or member is obsolete
+ new DerUtcTime(DateTime.Now, 2049),
new DerUtf8String("hello world"),
new DerVisibleString("hello world"),
new DerGraphicString(Hex.Decode("deadbeef")),
diff --git a/crypto/test/src/asn1/test/TimeTest.cs b/crypto/test/src/asn1/test/TimeTest.cs
index 04dac3c9e..4d080b535 100644
--- a/crypto/test/src/asn1/test/TimeTest.cs
+++ b/crypto/test/src/asn1/test/TimeTest.cs
@@ -17,11 +17,10 @@ namespace Org.BouncyCastle.Asn1.Tests
// Time classes only have a resolution of seconds
now = SimpleTest.MakeUtcDateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second);
- Org.BouncyCastle.Asn1.Cms.Time cmsTime = new Org.BouncyCastle.Asn1.Cms.Time(now);
- Org.BouncyCastle.Asn1.X509.Time x509Time = new Org.BouncyCastle.Asn1.X509.Time(now);
+ Cms.Time cmsTime = new Cms.Time(now);
+ X509.Time x509Time = new X509.Time(now);
-// Assert.AreEqual(cmsTime.Date, x509Time.ToDateTime());
- Assert.AreEqual(now, cmsTime.Date);
+ Assert.AreEqual(now, cmsTime.ToDateTime());
Assert.AreEqual(now, x509Time.ToDateTime());
}
}
diff --git a/crypto/test/src/asn1/test/UTCTimeTest.cs b/crypto/test/src/asn1/test/UTCTimeTest.cs
index bac0e1188..fcf72a4fc 100644
--- a/crypto/test/src/asn1/test/UTCTimeTest.cs
+++ b/crypto/test/src/asn1/test/UTCTimeTest.cs
@@ -1,4 +1,4 @@
-using System;
+using System.Globalization;
using NUnit.Framework;
@@ -18,56 +18,39 @@ namespace Org.BouncyCastle.Asn1.Tests
"020122122220Z",
"020122122220-1000",
"020122122220+1000",
- "020122122220+00",
"0201221222Z",
"0201221222-1000",
"0201221222+1000",
- "0201221222+00",
"550122122220Z",
- "5501221222Z"
- };
+ "5501221222Z",
+ "4007270730Z",
+ };
- private static readonly string[] output =
- {
- "20020122122220GMT+00:00",
- "20020122122220GMT-10:00",
- "20020122122220GMT+10:00",
- "20020122122220GMT+00:00",
- "20020122122200GMT+00:00",
- "20020122122200GMT-10:00",
- "20020122122200GMT+10:00",
- "20020122122200GMT+00:00",
- "19550122122220GMT+00:00",
- "19550122122200GMT+00:00"
- };
-
- private static readonly string[] zOutput1 =
+ private static readonly string[] outputPre2040 =
{
"20020122122220Z",
"20020122222220Z",
"20020122022220Z",
- "20020122122220Z",
"20020122122200Z",
"20020122222200Z",
"20020122022200Z",
- "20020122122200Z",
"19550122122220Z",
- "19550122122200Z"
- };
+ "19550122122200Z",
+ "19400727073000Z",
+ };
- private static readonly string[] zOutput2 =
+ private static readonly string[] outputPost2040 =
{
"20020122122220Z",
"20020122222220Z",
"20020122022220Z",
- "20020122122220Z",
"20020122122200Z",
"20020122222200Z",
"20020122022200Z",
- "20020122122200Z",
"19550122122220Z",
- "19550122122200Z"
- };
+ "19550122122200Z",
+ "20400727073000Z",
+ };
public override string Name
{
@@ -76,34 +59,29 @@ namespace Org.BouncyCastle.Asn1.Tests
public override void PerformTest()
{
-// SimpleDateFormat yyyyF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
-// SimpleDateFormat yyF = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
-
-// yyyyF.setTimeZone(new SimpleTimeZone(0,"Z"));
-// yyF.setTimeZone(new SimpleTimeZone(0,"Z"));
+ bool pre2040 = DateTimeFormatInfo.InvariantInfo.Calendar.TwoDigitYearMax < 2040;
+ string[] outputDefault = pre2040 ? outputPre2040 : outputPost2040;
- for (int i = 0; i != input.Length; i++)
+ for (int i = 0; i != input.Length; i++)
{
DerUtcTime t = new DerUtcTime(input[i]);
- if (!t.AdjustedTimeString.Equals(output[i]))
+ if (!t.ToDateTime().ToString(@"yyyyMMddHHmmssK").Equals(outputDefault[i]))
{
- Fail("failed conversion test " + i);
+ Fail("failed date shortened conversion test " + i);
}
-// if (!yyyyF.format(t.getAdjustedDate()).Equals(zOutput1[i]))
- if (!t.ToAdjustedDateTime().ToString(@"yyyyMMddHHmmss\Z").Equals(zOutput1[i]))
- {
- Fail("failed date conversion test " + i);
- }
+ if (!t.ToDateTime(2029).ToString(@"yyyyMMddHHmmssK").Equals(outputPre2040[i]))
+ {
+ Fail("failed date conversion test " + i);
+ }
-// if (!yyF.format(t.getDate()).Equals(zOutput2[i]))
- if (!t.ToDateTime().ToString(@"yyyyMMddHHmmss\Z").Equals(zOutput2[i]))
- {
- Fail("failed date shortened conversion test " + i);
- }
- }
- }
+ if (!t.ToDateTime(2049).ToString(@"yyyyMMddHHmmssK").Equals(outputPost2040[i]))
+ {
+ Fail("failed date conversion test " + i);
+ }
+ }
+ }
[Test]
public void TestFunction()
diff --git a/crypto/test/src/test/nist/NistCertPathTest.cs b/crypto/test/src/test/nist/NistCertPathTest.cs
index bdcd1b51f..c8bf44566 100644
--- a/crypto/test/src/test/nist/NistCertPathTest.cs
+++ b/crypto/test/src/test/nist/NistCertPathTest.cs
@@ -116,7 +116,7 @@ namespace Org.BouncyCastle.Tests.Nist
new string[] { "BadnotBeforeDateCACert", "InvalidCAnotBeforeDateTest1EE" },
new string[] { TRUST_ANCHOR_ROOT_CRL, "BadnotBeforeDateCACRL" },
1,
- "Could not validate certificate: certificate not valid until 20470101120100GMT+00:00");
+ "Could not validate certificate: certificate not valid until 20470101120100Z");
}
[Test]
@@ -126,7 +126,7 @@ namespace Org.BouncyCastle.Tests.Nist
new string[] { GOOD_CA_CERT, "InvalidEEnotBeforeDateTest2EE" },
new string[] { TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL },
0,
- "Could not validate certificate: certificate not valid until 20470101120100GMT+00:00");
+ "Could not validate certificate: certificate not valid until 20470101120100Z");
}
[Test]
@@ -152,7 +152,7 @@ namespace Org.BouncyCastle.Tests.Nist
new string[] { "BadnotAfterDateCACert", "InvalidCAnotAfterDateTest5EE" },
new string[] { TRUST_ANCHOR_ROOT_CRL, "BadnotAfterDateCACRL" },
1,
- "Could not validate certificate: certificate expired on 20020101120100GMT+00:00");
+ "Could not validate certificate: certificate expired on 20020101120100Z");
}
[Test]
@@ -162,7 +162,7 @@ namespace Org.BouncyCastle.Tests.Nist
new string[] { GOOD_CA_CERT, "InvalidEEnotAfterDateTest6EE" },
new string[] { TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL },
0,
- "Could not validate certificate: certificate expired on 20020101120100GMT+00:00");
+ "Could not validate certificate: certificate expired on 20020101120100Z");
}
[Test]
@@ -172,7 +172,7 @@ namespace Org.BouncyCastle.Tests.Nist
new string[] { GOOD_CA_CERT, "Invalidpre2000UTCEEnotAfterDateTest7EE" },
new string[] { TRUST_ANCHOR_ROOT_CRL, GOOD_CA_CRL },
0,
- "Could not validate certificate: certificate expired on 19990101120100GMT+00:00");
+ "Could not validate certificate: certificate expired on 19990101120100Z");
}
[Test]
--
cgit 1.4.1