Asn1GeneralizedTime.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Asn1
  9. {
  10. /// <summary>GeneralizedTime ASN.1 type</summary>
  11. public class Asn1GeneralizedTime
  12. : Asn1Object
  13. {
  14. internal class Meta : Asn1UniversalType
  15. {
  16. internal static readonly Asn1UniversalType Instance = new Meta();
  17. private Meta() : base(typeof(Asn1GeneralizedTime), Asn1Tags.GeneralizedTime) { }
  18. internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
  19. {
  20. return CreatePrimitive(octetString.GetOctets());
  21. }
  22. }
  23. public static Asn1GeneralizedTime GetInstance(object obj)
  24. {
  25. if (obj == null)
  26. return null;
  27. if (obj is Asn1GeneralizedTime asn1GeneralizedTime)
  28. return asn1GeneralizedTime;
  29. if (obj is IAsn1Convertible asn1Convertible)
  30. {
  31. Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
  32. if (asn1Object is Asn1GeneralizedTime converted)
  33. return converted;
  34. }
  35. else if (obj is byte[] bytes)
  36. {
  37. try
  38. {
  39. return (Asn1GeneralizedTime)Meta.Instance.FromByteArray(bytes);
  40. }
  41. catch (IOException e)
  42. {
  43. throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
  44. }
  45. }
  46. throw new ArgumentException("illegal object in GetInstance: " + Org.BouncyCastle.Utilities.Platform.GetTypeName(obj), nameof(obj));
  47. }
  48. public static Asn1GeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
  49. {
  50. return (Asn1GeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
  51. }
  52. private readonly string m_timeString;
  53. private readonly bool m_timeStringCanonical;
  54. private readonly DateTime m_dateTime;
  55. public Asn1GeneralizedTime(string timeString)
  56. {
  57. m_timeString = timeString ?? throw new ArgumentNullException(nameof(timeString));
  58. m_timeStringCanonical = false; // TODO Dynamic check?
  59. try
  60. {
  61. m_dateTime = FromString(timeString);
  62. }
  63. catch (FormatException e)
  64. {
  65. throw new ArgumentException("invalid date string: " + e.Message);
  66. }
  67. }
  68. public Asn1GeneralizedTime(DateTime dateTime)
  69. {
  70. dateTime = dateTime.ToUniversalTime();
  71. m_dateTime = dateTime;
  72. m_timeString = ToStringCanonical(dateTime);
  73. m_timeStringCanonical = true;
  74. }
  75. // TODO TimeZoneInfo or other locale-specific constructors?
  76. internal Asn1GeneralizedTime(byte[] contents)
  77. : this(Encoding.ASCII.GetString(contents))
  78. {
  79. }
  80. public string TimeString => m_timeString;
  81. public DateTime ToDateTime()
  82. {
  83. return m_dateTime;
  84. }
  85. internal byte[] GetContents(int encoding)
  86. {
  87. if (encoding == Asn1OutputStream.EncodingDer && !m_timeStringCanonical)
  88. return Encoding.ASCII.GetBytes(ToStringCanonical(m_dateTime));
  89. return Encoding.ASCII.GetBytes(m_timeString);
  90. }
  91. internal override IAsn1Encoding GetEncoding(int encoding)
  92. {
  93. return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetContents(encoding));
  94. }
  95. internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
  96. {
  97. return new PrimitiveEncoding(tagClass, tagNo, GetContents(encoding));
  98. }
  99. protected override bool Asn1Equals(Asn1Object asn1Object)
  100. {
  101. if (!(asn1Object is Asn1GeneralizedTime that))
  102. return false;
  103. // TODO Performance
  104. return Arrays.AreEqual(
  105. this.GetContents(Asn1OutputStream.EncodingDer),
  106. that.GetContents(Asn1OutputStream.EncodingDer));
  107. }
  108. protected override int Asn1GetHashCode()
  109. {
  110. // TODO Performance
  111. return Arrays.GetHashCode(
  112. this.GetContents(Asn1OutputStream.EncodingDer));
  113. }
  114. internal static Asn1GeneralizedTime CreatePrimitive(byte[] contents)
  115. {
  116. return new Asn1GeneralizedTime(contents);
  117. }
  118. private static DateTime FromString(string s)
  119. {
  120. if (s.Length < 10)
  121. throw new FormatException();
  122. s = s.Replace(',', '.');
  123. if (Org.BouncyCastle.Utilities.Platform.EndsWith(s, "Z"))
  124. {
  125. switch (s.Length)
  126. {
  127. case 11: return ParseUtc(s, @"yyyyMMddHH\Z");
  128. case 13: return ParseUtc(s, @"yyyyMMddHHmm\Z");
  129. case 15: return ParseUtc(s, @"yyyyMMddHHmmss\Z");
  130. case 17: return ParseUtc(s, @"yyyyMMddHHmmss.f\Z");
  131. case 18: return ParseUtc(s, @"yyyyMMddHHmmss.ff\Z");
  132. case 19: return ParseUtc(s, @"yyyyMMddHHmmss.fff\Z");
  133. case 20: return ParseUtc(s, @"yyyyMMddHHmmss.ffff\Z");
  134. case 21: return ParseUtc(s, @"yyyyMMddHHmmss.fffff\Z");
  135. case 22: return ParseUtc(s, @"yyyyMMddHHmmss.ffffff\Z");
  136. case 23: return ParseUtc(s, @"yyyyMMddHHmmss.fffffff\Z");
  137. default:
  138. throw new FormatException();
  139. }
  140. }
  141. int signIndex = IndexOfSign(s, System.Math.Max(10, s.Length - 5));
  142. if (signIndex < 0)
  143. {
  144. switch (s.Length)
  145. {
  146. case 10: return ParseLocal(s, @"yyyyMMddHH");
  147. case 12: return ParseLocal(s, @"yyyyMMddHHmm");
  148. case 14: return ParseLocal(s, @"yyyyMMddHHmmss");
  149. case 16: return ParseLocal(s, @"yyyyMMddHHmmss.f");
  150. case 17: return ParseLocal(s, @"yyyyMMddHHmmss.ff");
  151. case 18: return ParseLocal(s, @"yyyyMMddHHmmss.fff");
  152. case 19: return ParseLocal(s, @"yyyyMMddHHmmss.ffff");
  153. case 20: return ParseLocal(s, @"yyyyMMddHHmmss.fffff");
  154. case 21: return ParseLocal(s, @"yyyyMMddHHmmss.ffffff");
  155. case 22: return ParseLocal(s, @"yyyyMMddHHmmss.fffffff");
  156. default:
  157. throw new FormatException();
  158. }
  159. }
  160. if (signIndex == s.Length - 5)
  161. {
  162. switch (s.Length)
  163. {
  164. case 15: return ParseTimeZone(s, @"yyyyMMddHHzzz");
  165. case 17: return ParseTimeZone(s, @"yyyyMMddHHmmzzz");
  166. case 19: return ParseTimeZone(s, @"yyyyMMddHHmmsszzz");
  167. case 21: return ParseTimeZone(s, @"yyyyMMddHHmmss.fzzz");
  168. case 22: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffzzz");
  169. case 23: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffzzz");
  170. case 24: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffzzz");
  171. case 25: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffzzz");
  172. case 26: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffffzzz");
  173. case 27: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffffzzz");
  174. default:
  175. throw new FormatException();
  176. }
  177. }
  178. if (signIndex == s.Length - 3)
  179. {
  180. switch (s.Length)
  181. {
  182. case 13: return ParseTimeZone(s, @"yyyyMMddHHzz");
  183. case 15: return ParseTimeZone(s, @"yyyyMMddHHmmzz");
  184. case 17: return ParseTimeZone(s, @"yyyyMMddHHmmsszz");
  185. case 19: return ParseTimeZone(s, @"yyyyMMddHHmmss.fzz");
  186. case 20: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffzz");
  187. case 21: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffzz");
  188. case 22: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffzz");
  189. case 23: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffzz");
  190. case 24: return ParseTimeZone(s, @"yyyyMMddHHmmss.ffffffzz");
  191. case 25: return ParseTimeZone(s, @"yyyyMMddHHmmss.fffffffzz");
  192. default:
  193. throw new FormatException();
  194. }
  195. }
  196. throw new FormatException();
  197. }
  198. private static int IndexOfSign(string s, int startIndex)
  199. {
  200. int index = Org.BouncyCastle.Utilities.Platform.IndexOf(s, '+', startIndex);
  201. if (index < 0)
  202. {
  203. index = Org.BouncyCastle.Utilities.Platform.IndexOf(s, '-', startIndex);
  204. }
  205. return index;
  206. }
  207. private static DateTime ParseLocal(string s, string format)
  208. {
  209. return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AssumeLocal);
  210. }
  211. private static DateTime ParseTimeZone(string s, string format)
  212. {
  213. return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.AdjustToUniversal);
  214. }
  215. private static DateTime ParseUtc(string s, string format)
  216. {
  217. return DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo,
  218. DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal);
  219. }
  220. private static string ToStringCanonical(DateTime dateTime)
  221. {
  222. return dateTime.ToUniversalTime().ToString(@"yyyyMMddHHmmss.FFFFFFFK", DateTimeFormatInfo.InvariantInfo);
  223. }
  224. }
  225. }
  226. #pragma warning restore
  227. #endif