Asn1UtcTime.cs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Text;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities.Date;
  9. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1
  10. {
  11. /// <summary>UTCTime ASN.1 type</summary>
  12. public class Asn1UtcTime
  13. : Asn1Object
  14. {
  15. internal class Meta : Asn1UniversalType
  16. {
  17. internal static readonly Asn1UniversalType Instance = new Meta();
  18. private Meta() : base(typeof(Asn1UtcTime), Asn1Tags.UtcTime) {}
  19. internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
  20. {
  21. return CreatePrimitive(octetString.GetOctets());
  22. }
  23. }
  24. /**
  25. * return a UTC Time from the passed in object.
  26. *
  27. * @exception ArgumentException if the object cannot be converted.
  28. */
  29. public static Asn1UtcTime GetInstance(object obj)
  30. {
  31. if (obj == null)
  32. return null;
  33. if (obj is Asn1UtcTime asn1UtcTime)
  34. return asn1UtcTime;
  35. if (obj is IAsn1Convertible asn1Convertible)
  36. {
  37. Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
  38. if (asn1Object is Asn1UtcTime converted)
  39. return converted;
  40. }
  41. else if (obj is byte[] bytes)
  42. {
  43. try
  44. {
  45. return (Asn1UtcTime)Meta.Instance.FromByteArray(bytes);
  46. }
  47. catch (IOException e)
  48. {
  49. throw new ArgumentException("failed to construct UTC time from byte[]: " + e.Message);
  50. }
  51. }
  52. throw new ArgumentException("illegal object in GetInstance: " + Org.BouncyCastle.Utilities.Platform.GetTypeName(obj), nameof(obj));
  53. }
  54. public static Asn1UtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
  55. {
  56. return (Asn1UtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
  57. }
  58. private readonly string m_timeString;
  59. private readonly DateTime m_dateTime;
  60. private readonly bool m_dateTimeLocked;
  61. private readonly int m_twoDigitYearMax;
  62. public Asn1UtcTime(string timeString)
  63. {
  64. m_timeString = timeString ?? throw new ArgumentNullException(nameof(timeString));
  65. try
  66. {
  67. m_dateTime = FromString(timeString, out m_twoDigitYearMax);
  68. m_dateTimeLocked = false;
  69. }
  70. catch (FormatException e)
  71. {
  72. throw new ArgumentException("invalid date string: " + e.Message);
  73. }
  74. }
  75. public Asn1UtcTime(DateTime dateTime)
  76. {
  77. dateTime = DateTimeUtilities.WithPrecisionSecond(dateTime.ToUniversalTime());
  78. m_dateTime = dateTime;
  79. m_dateTimeLocked = true;
  80. m_timeString = ToStringCanonical(dateTime, out m_twoDigitYearMax);
  81. }
  82. public Asn1UtcTime(DateTime dateTime, int twoDigitYearMax)
  83. {
  84. dateTime = DateTimeUtilities.WithPrecisionSecond(dateTime.ToUniversalTime());
  85. Validate(dateTime, twoDigitYearMax);
  86. m_dateTime = dateTime;
  87. m_dateTimeLocked = true;
  88. m_timeString = ToStringCanonical(dateTime);
  89. m_twoDigitYearMax = twoDigitYearMax;
  90. }
  91. internal Asn1UtcTime(byte[] contents)
  92. // NOTE: Non-ASCII characters will produce '?' characters, which will fail DateTime parsing
  93. : this(Encoding.ASCII.GetString(contents))
  94. {
  95. }
  96. public string TimeString => m_timeString;
  97. public DateTime ToDateTime()
  98. {
  99. return m_dateTime;
  100. }
  101. public DateTime ToDateTime(int twoDigitYearMax)
  102. {
  103. if (InRange(m_dateTime, twoDigitYearMax))
  104. return m_dateTime;
  105. if (m_dateTimeLocked)
  106. throw new InvalidOperationException();
  107. int twoDigitYear = m_dateTime.Year % 100;
  108. int twoDigitYearCutoff = twoDigitYearMax % 100;
  109. int diff = twoDigitYear - twoDigitYearCutoff;
  110. int newYear = twoDigitYearMax + diff;
  111. if (diff > 0)
  112. {
  113. newYear -= 100;
  114. }
  115. return m_dateTime.AddYears(newYear - m_dateTime.Year);
  116. }
  117. public DateTime ToDateTime(Calendar calendar)
  118. {
  119. return ToDateTime(calendar.TwoDigitYearMax);
  120. }
  121. /// <summary>Return an adjusted date in the range of 1950 - 2049.</summary>
  122. public DateTime ToAdjustedDateTime()
  123. {
  124. return ToDateTime(2049);
  125. }
  126. public int TwoDigitYearMax => m_twoDigitYearMax;
  127. internal byte[] GetContents(int encoding)
  128. {
  129. if (encoding == Asn1OutputStream.EncodingDer && m_timeString.Length != 13)
  130. {
  131. string canonical = ToStringCanonical(m_dateTime);
  132. return Encoding.ASCII.GetBytes(canonical);
  133. }
  134. return Encoding.ASCII.GetBytes(m_timeString);
  135. }
  136. internal override IAsn1Encoding GetEncoding(int encoding)
  137. {
  138. return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime, GetContents(encoding));
  139. }
  140. internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
  141. {
  142. return new PrimitiveEncoding(tagClass, tagNo, GetContents(encoding));
  143. }
  144. protected override bool Asn1Equals(Asn1Object asn1Object)
  145. {
  146. if (!(asn1Object is Asn1UtcTime that))
  147. return false;
  148. // TODO Performance
  149. return Arrays.AreEqual(
  150. this.GetContents(Asn1OutputStream.EncodingDer),
  151. that.GetContents(Asn1OutputStream.EncodingDer));
  152. }
  153. protected override int Asn1GetHashCode()
  154. {
  155. // TODO Performance
  156. return Arrays.GetHashCode(
  157. this.GetContents(Asn1OutputStream.EncodingDer));
  158. }
  159. public override string ToString()
  160. {
  161. return m_timeString;
  162. }
  163. internal static Asn1UtcTime CreatePrimitive(byte[] contents)
  164. {
  165. return new Asn1UtcTime(contents);
  166. }
  167. private static DateTime FromString(string s, out int twoDigitYearMax)
  168. {
  169. var provider = DateTimeFormatInfo.InvariantInfo;
  170. twoDigitYearMax = provider.Calendar.TwoDigitYearMax;
  171. switch (s.Length)
  172. {
  173. case 11:
  174. return DateTime.ParseExact(s, @"yyMMddHHmm\Z", provider,
  175. DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
  176. case 13:
  177. return DateTime.ParseExact(s, @"yyMMddHHmmss\Z", provider,
  178. DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
  179. case 15:
  180. return DateTime.ParseExact(s, @"yyMMddHHmmzzz", provider, DateTimeStyles.AdjustToUniversal);
  181. case 17:
  182. return DateTime.ParseExact(s, @"yyMMddHHmmsszzz", provider, DateTimeStyles.AdjustToUniversal);
  183. default:
  184. throw new FormatException();
  185. }
  186. }
  187. private static bool InRange(DateTime dateTime, int twoDigitYearMax)
  188. {
  189. return (uint)(twoDigitYearMax - dateTime.Year) < 100;
  190. }
  191. private static string ToStringCanonical(DateTime dateTime, out int twoDigitYearMax)
  192. {
  193. var provider = DateTimeFormatInfo.InvariantInfo;
  194. twoDigitYearMax = provider.Calendar.TwoDigitYearMax;
  195. Validate(dateTime, twoDigitYearMax);
  196. return dateTime.ToString(@"yyMMddHHmmss\Z", provider);
  197. }
  198. private static string ToStringCanonical(DateTime dateTime)
  199. {
  200. return dateTime.ToString(@"yyMMddHHmmss\Z", DateTimeFormatInfo.InvariantInfo);
  201. }
  202. private static void Validate(DateTime dateTime, int twoDigitYearMax)
  203. {
  204. if (!InRange(dateTime, twoDigitYearMax))
  205. throw new ArgumentOutOfRangeException(nameof(dateTime));
  206. }
  207. }
  208. }
  209. #pragma warning restore
  210. #endif