From 879bb29bb0058a0723326de6edebb201e4cbc0b8 Mon Sep 17 00:00:00 2001
From: Peter Dettman
Date: Tue, 8 Nov 2022 13:18:17 +0700
Subject: Overhaul GeneralizedTime classes
---
crypto/src/asn1/Asn1GeneralizedTime.cs | 422 ++++++---------------
crypto/src/asn1/Asn1UtcTime.cs | 18 +-
crypto/src/asn1/DerGeneralizedTime.cs | 17 +-
crypto/src/asn1/cms/Time.cs | 5 +-
crypto/src/asn1/util/Asn1Dump.cs | 2 +-
crypto/src/asn1/x509/Time.cs | 5 +-
crypto/src/tsp/TimeStampTokenGenerator.cs | 61 +--
crypto/src/tsp/TimeStampTokenInfo.cs | 21 +-
crypto/src/util/Platform.cs | 15 +
crypto/src/util/date/DateTimeUtilities.cs | 28 +-
crypto/test/src/asn1/test/GeneralizedTimeTest.cs | 131 +------
crypto/test/src/tsp/test/NewTspTest.cs | 10 +-
crypto/test/src/tsp/test/TimeStampTokenInfoTest.cs | 21 +-
13 files changed, 238 insertions(+), 518 deletions(-)
diff --git a/crypto/src/asn1/Asn1GeneralizedTime.cs b/crypto/src/asn1/Asn1GeneralizedTime.cs
index 139384c1a..beabbdfa5 100644
--- a/crypto/src/asn1/Asn1GeneralizedTime.cs
+++ b/crypto/src/asn1/Asn1GeneralizedTime.cs
@@ -7,35 +7,7 @@ using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Asn1
{
- /**
- * Base class representing the ASN.1 GeneralizedTime type.
- *
- * The main difference between these and UTC time is a 4 digit year.
- *
- * One second resolution date+time on UTC timezone (Z)
- * with 4 digit year (valid from 0001 to 9999).
- *
- * Timestamp format is: yyyymmddHHMMSS'Z'
- *
- *
X.690
- * This is what is called "restricted string",
- * and it uses ASCII characters to encode digits and supplemental data.
- *
- * 11: Restrictions on BER employed by both CER and DER
- * 11.7 GeneralizedTime
- *
- * 11.7.1 The encoding shall terminate with a "Z",
- * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
- * GeneralizedTime.
- *
- * 11.7.2 The seconds element shall always be present.
- *
- * 11.7.3 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.
- *
- */
+ /// GeneralizedTime ASN.1 type
public class Asn1GeneralizedTime
: Asn1Object
{
@@ -85,15 +57,18 @@ namespace Org.BouncyCastle.Asn1
return (Asn1GeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
}
- internal readonly byte[] m_contents;
+ private readonly string m_timeString;
+ private readonly bool m_timeStringCanonical;
+ private readonly DateTime m_dateTime;
- public Asn1GeneralizedTime(string time)
+ public Asn1GeneralizedTime(string timeString)
{
- m_contents = Strings.ToByteArray(time);
+ m_timeString = timeString ?? throw new ArgumentNullException(nameof(timeString));
+ m_timeStringCanonical = false; // TODO Dynamic check?
try
{
- ToDateTime();
+ m_dateTime = FromString(timeString);
}
catch (FormatException e)
{
@@ -101,238 +76,45 @@ namespace Org.BouncyCastle.Asn1
}
}
- public Asn1GeneralizedTime(DateTime time)
+ public Asn1GeneralizedTime(DateTime dateTime)
{
- 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";
- }
+ dateTime = dateTime.ToUniversalTime();
- return stime + CalculateGmtOffset(stime);
+ m_dateTime = dateTime;
+ m_timeString = ToStringCanonical(dateTime);
+ m_timeStringCanonical = true;
}
- 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);
- }
+ // TODO TimeZoneInfo or other locale-specific constructors?
- private string CalculateGmtFormatString(string d)
+ internal Asn1GeneralizedTime(byte[] contents)
+ : this(Encoding.ASCII.GetString(contents))
{
- 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 string TimeString => m_timeString;
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);
+ return m_dateTime;
}
- protected bool HasMinutes()
+ internal byte[] GetContents(int encoding)
{
- return IsDigit(10) && IsDigit(11);
- }
+ if (encoding == Asn1OutputStream.EncodingDer && !m_timeStringCanonical)
+ return Encoding.ASCII.GetBytes(ToStringCanonical(m_dateTime));
- private bool IsDigit(int pos)
- {
- return m_contents.Length > pos && m_contents[pos] >= '0' && m_contents[pos] <= '9';
+ return Encoding.ASCII.GetBytes(m_timeString);
}
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);
+ return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetContents(encoding));
}
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);
+ return new PrimitiveEncoding(tagClass, tagNo, GetContents(encoding));
}
protected override bool Asn1Equals(Asn1Object asn1Object)
@@ -340,12 +122,17 @@ namespace Org.BouncyCastle.Asn1
if (!(asn1Object is Asn1GeneralizedTime that))
return false;
- return Arrays.AreEqual(m_contents, that.m_contents);
+ // TODO Performance
+ return Arrays.AreEqual(
+ this.GetContents(Asn1OutputStream.EncodingDer),
+ that.GetContents(Asn1OutputStream.EncodingDer));
}
protected override int Asn1GetHashCode()
{
- return Arrays.GetHashCode(m_contents);
+ // TODO Performance
+ return Arrays.GetHashCode(
+ this.GetContents(Asn1OutputStream.EncodingDer));
}
internal static Asn1GeneralizedTime CreatePrimitive(byte[] contents)
@@ -353,86 +140,123 @@ namespace Org.BouncyCastle.Asn1
return new Asn1GeneralizedTime(contents);
}
- internal byte[] GetDerTime()
+ private static DateTime FromString(string s)
{
- if (m_contents[m_contents.Length - 1] != 'Z')
- {
- return m_contents; // TODO: is there a better way?
- }
+ if (s.Length < 10)
+ throw new FormatException();
- if (!HasMinutes())
- {
- byte[] derTime = new byte[m_contents.Length + 4];
+ s = s.Replace(',', '.');
- 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())
+ if (Platform.EndsWith(s, "Z"))
{
- byte[] derTime = new byte[m_contents.Length + 2];
+ switch (s.Length)
+ {
+ case 11: return ParseUtc(s, @"yyyyMMddHH\Z");
+ case 13: return ParseUtc(s, @"yyyyMMddHHmm\Z");
+ case 15: return ParseUtc(s, @"yyyyMMddHHmmss\Z");
+ case 17: return ParseUtc(s, @"yyyyMMddHHmmss.f\Z");
+ case 18: return ParseUtc(s, @"yyyyMMddHHmmss.ff\Z");
+ case 19: return ParseUtc(s, @"yyyyMMddHHmmss.fff\Z");
+ case 20: return ParseUtc(s, @"yyyyMMddHHmmss.ffff\Z");
+ case 21: return ParseUtc(s, @"yyyyMMddHHmmss.fffff\Z");
+ case 22: return ParseUtc(s, @"yyyyMMddHHmmss.ffffff\Z");
+ case 23: return ParseUtc(s, @"yyyyMMddHHmmss.fffffff\Z");
+ default:
+ throw new FormatException();
+ }
+ }
- Array.Copy(m_contents, 0, derTime, 0, m_contents.Length - 1);
- Array.Copy(Strings.ToByteArray("00Z"), 0, derTime, m_contents.Length - 1, 3);
+ int signIndex = IndexOfSign(s, System.Math.Max(10, s.Length - 5));
- return derTime;
- }
- else if (HasFractionalSeconds())
+ if (signIndex < 0)
{
- int ind = m_contents.Length - 2;
- while (ind > 0 && m_contents[ind] == '0')
+ switch (s.Length)
{
- ind--;
+ case 10: return ParseLocal(s, @"yyyyMMddHH");
+ case 12: return ParseLocal(s, @"yyyyMMddHHmm");
+ case 14: return ParseLocal(s, @"yyyyMMddHHmmss");
+ case 16: return ParseLocal(s, @"yyyyMMddHHmmss.f");
+ case 17: return ParseLocal(s, @"yyyyMMddHHmmss.ff");
+ case 18: return ParseLocal(s, @"yyyyMMddHHmmss.fff");
+ case 19: return ParseLocal(s, @"yyyyMMddHHmmss.ffff");
+ case 20: return ParseLocal(s, @"yyyyMMddHHmmss.fffff");
+ case 21: return ParseLocal(s, @"yyyyMMddHHmmss.ffffff");
+ case 22: return ParseLocal(s, @"yyyyMMddHHmmss.fffffff");
+ default:
+ throw new FormatException();
}
+ }
- 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
+ if (signIndex == s.Length - 5)
+ {
+ switch (s.Length)
{
- byte[] derTime = new byte[ind + 2];
-
- Array.Copy(m_contents, 0, derTime, 0, ind + 1);
- derTime[ind + 1] = (byte)'Z';
-
- return derTime;
+ case 15: return ParseTimeZone(s, @"yyyyMMddHHzzz");
+ case 17: return ParseTimeZone(s, @"yyyyMMddHHmmzzz");
+ case 19: return ParseTimeZone(s, @"yyyyMMddHHmmsszzz");
+ case 21: return ParseTimeZone(s, @"yyyyMMddHHmmss.fzzz");
+ case 22: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffzzz");
+ case 23: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffzzz");
+ case 24: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffzzz");
+ case 25: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffzzz");
+ case 26: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffffzzz");
+ case 27: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffffzzz");
+ default:
+ throw new FormatException();
}
}
- else
+
+ if (signIndex == s.Length - 3)
{
- return m_contents;
+ switch (s.Length)
+ {
+ case 13: return ParseTimeZone(s, @"yyyyMMddHHzz");
+ case 15: return ParseTimeZone(s, @"yyyyMMddHHmmzz");
+ case 17: return ParseTimeZone(s, @"yyyyMMddHHmmsszz");
+ case 19: return ParseTimeZone(s, @"yyyyMMddHHmmss.fzz");
+ case 20: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffzz");
+ case 21: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffzz");
+ case 22: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffzz");
+ case 23: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffzz");
+ case 24: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffffzz");
+ case 25: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffffzz");
+ default:
+ throw new FormatException();
+ }
}
+
+ throw new FormatException();
}
- private static string FString(int count)
+ private static int IndexOfSign(string s, int startIndex)
{
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < count; ++i)
+ int index = Platform.IndexOf(s, '+', startIndex);
+ if (index < 0)
{
- sb.Append('f');
+ index = Platform.IndexOf(s, '-', startIndex);
}
- return sb.ToString();
+ return index;
}
- private static DateTime ParseDateString(string s, string format, bool makeUniversal)
+ private static DateTime ParseLocal(string s, string format)
{
- DateTimeStyles dateTimeStyles = DateTimeStyles.None;
- if (Platform.EndsWith(format, "Z"))
- {
- dateTimeStyles |= DateTimeStyles.AdjustToUniversal;
- dateTimeStyles |= DateTimeStyles.AssumeUniversal;
- }
+ return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeLocal);
+ }
- DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, dateTimeStyles);
+ private static DateTime ParseTimeZone(string s, string format)
+ {
+ return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal);
+ }
+
+ private static DateTime ParseUtc(string s, string format)
+ {
+ return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo,
+ DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
+ }
- return makeUniversal ? dt.ToUniversalTime() : dt;
+ private static string ToStringCanonical(DateTime dateTime)
+ {
+ return dateTime.ToUniversalTime().ToString(@"yyyyMMddHHmmss.FFFFFFFK", DateTimeFormatInfo.InvariantInfo);
}
}
}
diff --git a/crypto/src/asn1/Asn1UtcTime.cs b/crypto/src/asn1/Asn1UtcTime.cs
index 478b5c485..bd06a3258 100644
--- a/crypto/src/asn1/Asn1UtcTime.cs
+++ b/crypto/src/asn1/Asn1UtcTime.cs
@@ -4,6 +4,7 @@ using System.IO;
using System.Text;
using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
namespace Org.BouncyCastle.Asn1
{
@@ -69,10 +70,7 @@ namespace Org.BouncyCastle.Asn1
public Asn1UtcTime(string timeString)
{
- if (timeString == null)
- throw new ArgumentNullException(nameof(timeString));
-
- m_timeString = timeString;
+ m_timeString = timeString ?? throw new ArgumentNullException(nameof(timeString));
try
{
@@ -88,8 +86,7 @@ namespace Org.BouncyCastle.Asn1
[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);
+ dateTime = DateTimeUtilities.WithPrecisionSecond(dateTime.ToUniversalTime());
m_dateTime = dateTime;
m_dateTimeLocked = true;
@@ -98,8 +95,7 @@ namespace Org.BouncyCastle.Asn1
public Asn1UtcTime(DateTime dateTime, int twoDigitYearMax)
{
- DateTime utc = dateTime.ToUniversalTime();
- dateTime = new DateTime(utc.Year, utc.Month, utc.Day, utc.Hour, utc.Minute, utc.Second, DateTimeKind.Utc);
+ dateTime = DateTimeUtilities.WithPrecisionSecond(dateTime.ToUniversalTime());
Validate(dateTime, twoDigitYearMax);
@@ -220,11 +216,9 @@ namespace Org.BouncyCastle.Asn1
return DateTime.ParseExact(s, @"yyMMddHHmmss\Z", provider,
DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
case 15:
- return DateTime.ParseExact(s, @"yyMMddHHmmzzz", provider,
- DateTimeStyles.AdjustToUniversal);
+ return DateTime.ParseExact(s, @"yyMMddHHmmzzz", provider, DateTimeStyles.AdjustToUniversal);
case 17:
- return DateTime.ParseExact(s, @"yyMMddHHmmsszzz", provider,
- DateTimeStyles.AdjustToUniversal);
+ return DateTime.ParseExact(s, @"yyMMddHHmmsszzz", provider, DateTimeStyles.AdjustToUniversal);
default:
throw new FormatException();
}
diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs
index 0386ecb02..3d814de30 100644
--- a/crypto/src/asn1/DerGeneralizedTime.cs
+++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -5,29 +5,30 @@ namespace Org.BouncyCastle.Asn1
public class DerGeneralizedTime
: Asn1GeneralizedTime
{
- public DerGeneralizedTime(byte[] time)
- : base(time)
+ public DerGeneralizedTime(string timeString)
+ : base(timeString)
{
}
- public DerGeneralizedTime(DateTime time)
- : base(time)
+ public DerGeneralizedTime(DateTime dateTime)
+ : base(dateTime)
{
}
- public DerGeneralizedTime(string time)
- : base(time)
+ internal DerGeneralizedTime(byte[] contents)
+ : base(contents)
{
}
internal override IAsn1Encoding GetEncoding(int encoding)
{
- return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetDerTime());
+ return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime,
+ GetContents(Asn1OutputStream.EncodingDer));
}
internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
{
- return new PrimitiveEncoding(tagClass, tagNo, GetDerTime());
+ 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 f054e3809..7b6b49c30 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -101,7 +101,10 @@ namespace Org.BouncyCastle.Asn1.Cms
if (m_timeObject is Asn1UtcTime utcTime)
return utcTime.ToDateTime(2049).ToString(@"yyyyMMddHHmmssK", DateTimeFormatInfo.InvariantInfo);
- return ((Asn1GeneralizedTime)m_timeObject).GetTime();
+ if (m_timeObject is Asn1GeneralizedTime generalizedTime)
+ return generalizedTime.ToDateTime().ToString(@"yyyyMMddHHmmss.FFFFFFFK", DateTimeFormatInfo.InvariantInfo);
+
+ throw new InvalidOperationException();
}
}
}
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index 6c005f512..3a19f1276 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -214,7 +214,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
else if (obj is Asn1GeneralizedTime generalizedTime)
{
buf.Append(indent);
- buf.AppendLine("GeneralizedTime(" + generalizedTime.GetTime() + ")");
+ buf.AppendLine("GeneralizedTime(" + generalizedTime.TimeString + ")");
}
else if (obj is DerEnumerated en)
{
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index 7f2d43315..e03055f6d 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -102,7 +102,10 @@ namespace Org.BouncyCastle.Asn1.X509
if (m_timeObject is Asn1UtcTime utcTime)
return utcTime.ToDateTime(2049).ToString(@"yyyyMMddHHmmssK", DateTimeFormatInfo.InvariantInfo);
- return ((Asn1GeneralizedTime)m_timeObject).GetTime();
+ if (m_timeObject is Asn1GeneralizedTime generalizedTime)
+ return generalizedTime.ToDateTime().ToString(@"yyyyMMddHHmmss.FFFFFFFK", DateTimeFormatInfo.InvariantInfo);
+
+ throw new InvalidOperationException();
}
}
}
diff --git a/crypto/src/tsp/TimeStampTokenGenerator.cs b/crypto/src/tsp/TimeStampTokenGenerator.cs
index 9e6a21f9c..930463ca3 100644
--- a/crypto/src/tsp/TimeStampTokenGenerator.cs
+++ b/crypto/src/tsp/TimeStampTokenGenerator.cs
@@ -16,6 +16,7 @@ using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Collections;
+using Org.BouncyCastle.Utilities.Date;
using Org.BouncyCastle.X509;
namespace Org.BouncyCastle.Tsp
@@ -334,15 +335,7 @@ namespace Org.BouncyCastle.Tsp
respExtensions = extGen.Generate();
}
- Asn1GeneralizedTime timeStampTime;
- if (resolution == Resolution.R_SECONDS)
- {
- timeStampTime = new Asn1GeneralizedTime(genTime);
- }
- else
- {
- timeStampTime = CreateGeneralizedTime(genTime);
- }
+ var timeStampTime = new Asn1GeneralizedTime(WithResolution(genTime, resolution));
TstInfo tstInfo = new TstInfo(tsaPolicy, messageImprint,
new DerInteger(serialNumber), timeStampTime, accuracy,
@@ -385,53 +378,21 @@ namespace Org.BouncyCastle.Tsp
//}
}
- private Asn1GeneralizedTime CreateGeneralizedTime(DateTime genTime)
+ private static DateTime WithResolution(DateTime dateTime, Resolution resolution)
{
- string format = "yyyyMMddHHmmss.fff";
-
- StringBuilder sBuild = new StringBuilder(genTime.ToString(format));
- int dotIndex = sBuild.ToString().IndexOf(".");
-
- if (dotIndex <0)
- {
- sBuild.Append("Z");
- return new Asn1GeneralizedTime(sBuild.ToString());
- }
-
- switch(resolution)
+ switch (resolution)
{
+ case Resolution.R_SECONDS:
+ return DateTimeUtilities.WithPrecisionSecond(dateTime);
case Resolution.R_TENTHS_OF_SECONDS:
- if (sBuild.Length > dotIndex + 2)
- {
- sBuild.Remove(dotIndex + 2, sBuild.Length-(dotIndex+2));
- }
- break;
+ return DateTimeUtilities.WithPrecisionDecisecond(dateTime);
case Resolution.R_HUNDREDTHS_OF_SECONDS:
- if (sBuild.Length > dotIndex + 3)
- {
- sBuild.Remove(dotIndex + 3, sBuild.Length-(dotIndex+3));
- }
- break;
-
-
- case Resolution.R_SECONDS:
+ return DateTimeUtilities.WithPrecisionCentisecond(dateTime);
case Resolution.R_MILLISECONDS:
- // do nothing.
- break;
- }
-
- while (sBuild[sBuild.Length - 1] == '0')
- {
- sBuild.Remove(sBuild.Length - 1,1);
- }
-
- if (sBuild.Length - 1 == dotIndex)
- {
- sBuild.Remove(sBuild.Length - 1, 1);
+ return DateTimeUtilities.WithPrecisionMillisecond(dateTime);
+ default:
+ throw new InvalidOperationException();
}
-
- sBuild.Append("Z");
- return new Asn1GeneralizedTime(sBuild.ToString());
}
private class TableGen
diff --git a/crypto/src/tsp/TimeStampTokenInfo.cs b/crypto/src/tsp/TimeStampTokenInfo.cs
index cdef826bc..b9f0e3195 100644
--- a/crypto/src/tsp/TimeStampTokenInfo.cs
+++ b/crypto/src/tsp/TimeStampTokenInfo.cs
@@ -8,10 +8,27 @@ namespace Org.BouncyCastle.Tsp
{
public class TimeStampTokenInfo
{
- private TstInfo tstInfo;
+ private static TstInfo ParseTstInfo(byte[] tstInfoEncoding)
+ {
+ try
+ {
+ return TstInfo.GetInstance(tstInfoEncoding);
+ }
+ catch (Exception e)
+ {
+ throw new TspException("unable to parse TstInfo encoding: " + e.Message);
+ }
+ }
+
+ private TstInfo tstInfo;
private DateTime genTime;
- public TimeStampTokenInfo(
+ public TimeStampTokenInfo(byte[] tstInfoEncoding)
+ : this(ParseTstInfo(tstInfoEncoding))
+ {
+ }
+
+ public TimeStampTokenInfo(
TstInfo tstInfo)
{
this.tstInfo = tstInfo;
diff --git a/crypto/src/util/Platform.cs b/crypto/src/util/Platform.cs
index 118c29918..78ff6cee4 100644
--- a/crypto/src/util/Platform.cs
+++ b/crypto/src/util/Platform.cs
@@ -31,11 +31,26 @@ namespace Org.BouncyCastle.Utilities
d.Dispose();
}
+ internal static int IndexOf(string source, char value)
+ {
+ return InvariantCompareInfo.IndexOf(source, value, CompareOptions.Ordinal);
+ }
+
internal static int IndexOf(string source, string value)
{
return InvariantCompareInfo.IndexOf(source, value, CompareOptions.Ordinal);
}
+ internal static int IndexOf(string source, char value, int startIndex)
+ {
+ return InvariantCompareInfo.IndexOf(source, value, startIndex, CompareOptions.Ordinal);
+ }
+
+ internal static int IndexOf(string source, string value, int startIndex)
+ {
+ return InvariantCompareInfo.IndexOf(source, value, startIndex, CompareOptions.Ordinal);
+ }
+
internal static int LastIndexOf(string source, string value)
{
return InvariantCompareInfo.LastIndexOf(source, value, CompareOptions.Ordinal);
diff --git a/crypto/src/util/date/DateTimeUtilities.cs b/crypto/src/util/date/DateTimeUtilities.cs
index 3660e29c2..c46cde8db 100644
--- a/crypto/src/util/date/DateTimeUtilities.cs
+++ b/crypto/src/util/date/DateTimeUtilities.cs
@@ -53,5 +53,31 @@ namespace Org.BouncyCastle.Utilities.Date
{
return DateTimeToUnixMs(DateTime.UtcNow);
}
- }
+
+ public static DateTime WithPrecisionCentisecond(DateTime dateTime)
+ {
+ int millisecond = dateTime.Millisecond - (dateTime.Millisecond % 10);
+ return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day,
+ dateTime.Hour, dateTime.Minute, dateTime.Second, millisecond, dateTime.Kind);
+ }
+
+ public static DateTime WithPrecisionDecisecond(DateTime dateTime)
+ {
+ int millisecond = dateTime.Millisecond - (dateTime.Millisecond % 100);
+ return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day,
+ dateTime.Hour, dateTime.Minute, dateTime.Second, millisecond, dateTime.Kind);
+ }
+
+ public static DateTime WithPrecisionMillisecond(DateTime dateTime)
+ {
+ return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day,
+ dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Millisecond, dateTime.Kind);
+ }
+
+ public static DateTime WithPrecisionSecond(DateTime dateTime)
+ {
+ return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day,
+ dateTime.Hour, dateTime.Minute, dateTime.Second, dateTime.Kind);
+ }
+ }
}
diff --git a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
index 1cc1d8433..266ca2753 100644
--- a/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
+++ b/crypto/test/src/asn1/test/GeneralizedTimeTest.cs
@@ -40,56 +40,6 @@ namespace Org.BouncyCastle.Asn1.Tests
"20020122122220.0001+1000"
};
- private static readonly string[] output =
- {
- "20020122122220",
- "20020122122220GMT+00:00",
- "20020122122220GMT-10:00",
- "20020122122220GMT+00:00",
- "20020122122220.1",
- "20020122122220.1GMT+00:00",
- "20020122122220.1GMT-10:00",
- "20020122122220.1GMT+00:00",
- "20020122122220.01",
- "20020122122220.01GMT+00:00",
- "20020122122220.01GMT-10:00",
- "20020122122220.01GMT+00:00",
- "20020122122220.001",
- "20020122122220.001GMT+00:00",
- "20020122122220.001GMT-10:00",
- "20020122122220.001GMT+00:00",
- "20020122122220.0001",
- "20020122122220.0001GMT+00:00",
- "20020122122220.0001GMT-10:00",
- "20020122122220.0001GMT+00:00",
- "20020122122220.0001GMT+10:00"
- };
-
- private static readonly string[] zOutput =
- {
- "20020122122220Z",
- "20020122122220Z",
- "20020122222220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122222220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122222220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122222220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122122220Z",
- "20020122222220Z",
- "20020122122220Z",
- "20020122022220Z"
- };
-
private static readonly string[] mzOutput =
{
"20020122122220.000Z",
@@ -159,38 +109,6 @@ namespace Org.BouncyCastle.Asn1.Tests
public override void PerformTest()
{
- for (int i = 0; i != input.Length; i++)
- {
- string ii = input[i], oi = output[i];
-
- Asn1GeneralizedTime t = new Asn1GeneralizedTime(ii);
- DateTime dt = t.ToDateTime();
- string st = t.GetTime();
-
- if (oi.IndexOf('G') > 0) // don't check local time the same way
- {
- if (!st.Equals(oi))
- {
- 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 " + i);
- }
- }
- else
- {
- string offset = CalculateGmtOffset(dt);
- if (!st.Equals(oi + offset))
- {
- Fail("failed conversion test " + i);
- }
- }
- }
-
for (int i = 0; i != input.Length; i++)
{
Asn1GeneralizedTime t = new Asn1GeneralizedTime(input[i]);
@@ -209,7 +127,7 @@ namespace Org.BouncyCastle.Asn1.Tests
if (!AreEqual(t.GetEncoded(), new Asn1GeneralizedTime(derMzOutput[i]).GetEncoded()))
{
- Fail("der encoding wrong");
+ Fail("DER encoding wrong");
}
}
@@ -219,7 +137,7 @@ namespace Org.BouncyCastle.Asn1.Tests
if (!AreEqual(t.GetEncoded(), new Asn1GeneralizedTime(derTruncOutput[i]).GetEncoded()))
{
- Fail("trunc der encoding wrong");
+ Fail("trunc DER encoding wrong");
}
}
@@ -239,20 +157,14 @@ namespace Org.BouncyCastle.Asn1.Tests
IsTrue(Arrays.AreEqual(Hex.Decode("180f32303232303830393132313530305a"), der.GetEncoded(Asn1Encodable.Der)));
}
- {
- // check an actual GMT string comes back untampered
- Asn1GeneralizedTime time = new Asn1GeneralizedTime("20190704031318GMT+00:00");
-
- IsTrue("20190704031318GMT+00:00".Equals(time.GetTime()));
- }
-
try
{
- new DerGeneralizedTime(new byte[0]);
+ new DerGeneralizedTime(string.Empty);
+ Fail("Expected exception");
}
catch (ArgumentException e)
{
- IsTrue(e.Message.StartsWith("GeneralizedTime string too short"));
+ IsTrue(e.Message.StartsWith("invalid date string"));
}
/*
@@ -277,39 +189,6 @@ namespace Org.BouncyCastle.Asn1.Tests
}
}
- private string CalculateGmtOffset(DateTime date)
- {
- TimeZoneInfo timeZone = TimeZoneInfo.Local;
- TimeSpan offset = timeZone.BaseUtcOffset;
-
- 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);
- }
-
- private string Convert(int time)
- {
- if (time < 10)
- {
- return "0" + time;
- }
-
- return time.ToString();
- }
-
[Test]
public void TestFunction()
{
diff --git a/crypto/test/src/tsp/test/NewTspTest.cs b/crypto/test/src/tsp/test/NewTspTest.cs
index c98e5d191..69bec7958 100644
--- a/crypto/test/src/tsp/test/NewTspTest.cs
+++ b/crypto/test/src/tsp/test/NewTspTest.cs
@@ -787,12 +787,12 @@ namespace Org.BouncyCastle.Tsp.Tests
}
private void resolutionTest(AsymmetricKeyParameter privateKey, X509Certificate cert,
- IStore certs, Resolution resoution, string timeString)
+ IStore certs, Resolution resolution, string timeString)
{
TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
- privateKey, cert, TspAlgorithms.Sha1, "1.2");
+ privateKey, cert, TspAlgorithms.Sha1, "1.2");
- tsTokenGen.Resolution = resoution;
+ tsTokenGen.Resolution = resolution;
tsTokenGen.SetCertificates(certs);
TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
@@ -813,14 +813,14 @@ namespace Org.BouncyCastle.Tsp.Tests
tsToken = tsResp.TimeStampToken;
Assert.AreEqual("19700101000009Z", tsToken.TimeStampInfo.TstInfo.GenTime.TimeString);
- if ((int)resoution > (int)Resolution.R_HUNDREDTHS_OF_SECONDS)
+ if ((int)resolution > (int)Resolution.R_HUNDREDTHS_OF_SECONDS)
{
tsResp = tsRespGen.Generate(request, new BigInteger("23"), UnixEpoch.AddMilliseconds(9990));
tsToken = tsResp.TimeStampToken;
Assert.AreEqual("19700101000009.99Z", tsToken.TimeStampInfo.TstInfo.GenTime.TimeString);
}
- if ((int)resoution > (int)Resolution.R_TENTHS_OF_SECONDS)
+ if ((int)resolution > (int)Resolution.R_TENTHS_OF_SECONDS)
{
tsResp = tsRespGen.Generate(request, new BigInteger("23"), UnixEpoch.AddMilliseconds(9900));
tsToken = tsResp.TimeStampToken;
diff --git a/crypto/test/src/tsp/test/TimeStampTokenInfoTest.cs b/crypto/test/src/tsp/test/TimeStampTokenInfoTest.cs
index a1a7355e5..03f611beb 100644
--- a/crypto/test/src/tsp/test/TimeStampTokenInfoTest.cs
+++ b/crypto/test/src/tsp/test/TimeStampTokenInfoTest.cs
@@ -1,7 +1,5 @@
using NUnit.Framework;
-using Org.BouncyCastle.Asn1;
-using Org.BouncyCastle.Asn1.Tsp;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Date;
@@ -31,7 +29,7 @@ namespace Org.BouncyCastle.Tsp.Tests
[Test]
public void TestTstInfo1()
{
- TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo1);
+ TimeStampTokenInfo tstInfo = CreateTimeStampTokenInfo(tstInfo1);
//
// verify
@@ -60,7 +58,7 @@ namespace Org.BouncyCastle.Tsp.Tests
[Test]
public void TestTstInfo2()
{
- TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo2);
+ TimeStampTokenInfo tstInfo = CreateTimeStampTokenInfo(tstInfo2);
//
// verify
@@ -81,7 +79,9 @@ namespace Org.BouncyCastle.Tsp.Tests
Assert.AreEqual(tstInfo.Nonce, BigInteger.ValueOf(100));
- Assert.IsTrue(Arrays.AreEqual(Hex.Decode("ffffffffffffffffffffffffffffffffffffffff"), tstInfo.GetMessageImprintDigest()));
+ Assert.IsTrue(Arrays.AreEqual(
+ Hex.Decode("ffffffffffffffffffffffffffffffffffffffff"),
+ tstInfo.GetMessageImprintDigest()));
Assert.IsTrue(Arrays.AreEqual(tstInfo2, tstInfo.GetEncoded()));
}
@@ -89,7 +89,7 @@ namespace Org.BouncyCastle.Tsp.Tests
[Test]
public void TestTstInfo3()
{
- TimeStampTokenInfo tstInfo = getTimeStampTokenInfo(tstInfo3);
+ TimeStampTokenInfo tstInfo = CreateTimeStampTokenInfo(tstInfo3);
//
// verify
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Tsp.Tests
{
try
{
- getTimeStampTokenInfo(tstInfoDudDate);
+ CreateTimeStampTokenInfo(tstInfoDudDate);
Assert.Fail("dud date not detected.");
}
@@ -132,12 +132,9 @@ namespace Org.BouncyCastle.Tsp.Tests
}
}
- private TimeStampTokenInfo getTimeStampTokenInfo(
- byte[] tstInfo)
+ private TimeStampTokenInfo CreateTimeStampTokenInfo(byte[] tstInfo)
{
- return new TimeStampTokenInfo(
- TstInfo.GetInstance(
- Asn1Object.FromByteArray(tstInfo)));
+ return new TimeStampTokenInfo(tstInfo);
}
}
}
--
cgit 1.4.1