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