DerObjectIdentifier.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Math;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Asn1
  10. {
  11. public class DerObjectIdentifier
  12. : Asn1Object
  13. {
  14. private readonly string identifier;
  15. private byte[] body = null;
  16. /**
  17. * return an Oid from the passed in object
  18. *
  19. * @exception ArgumentException if the object cannot be converted.
  20. */
  21. public static DerObjectIdentifier GetInstance(object obj)
  22. {
  23. if (obj == null || obj is DerObjectIdentifier)
  24. return (DerObjectIdentifier) obj;
  25. if (obj is byte[])
  26. return FromOctetString((byte[])obj);
  27. throw new ArgumentException("illegal object in GetInstance: " + BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.GetTypeName(obj), "obj");
  28. }
  29. /**
  30. * return an object Identifier from a tagged object.
  31. *
  32. * @param obj the tagged object holding the object we want
  33. * @param explicitly true if the object is meant to be explicitly
  34. * tagged false otherwise.
  35. * @exception ArgumentException if the tagged object cannot
  36. * be converted.
  37. */
  38. public static DerObjectIdentifier GetInstance(
  39. Asn1TaggedObject obj,
  40. bool explicitly)
  41. {
  42. Asn1Object o = obj.GetObject();
  43. if (explicitly || o is DerObjectIdentifier)
  44. {
  45. return GetInstance(o);
  46. }
  47. return FromOctetString(Asn1OctetString.GetInstance(o).GetOctets());
  48. }
  49. public DerObjectIdentifier(
  50. string identifier)
  51. {
  52. if (identifier == null)
  53. throw new ArgumentNullException("identifier");
  54. if (!IsValidIdentifier(identifier))
  55. throw new FormatException("string " + identifier + " not an OID");
  56. this.identifier = identifier;
  57. }
  58. internal DerObjectIdentifier(DerObjectIdentifier oid, string branchID)
  59. {
  60. if (!IsValidBranchID(branchID, 0))
  61. throw new ArgumentException("string " + branchID + " not a valid OID branch", "branchID");
  62. this.identifier = oid.Id + "." + branchID;
  63. }
  64. // TODO Change to ID?
  65. public string Id
  66. {
  67. get { return identifier; }
  68. }
  69. public virtual DerObjectIdentifier Branch(string branchID)
  70. {
  71. return new DerObjectIdentifier(this, branchID);
  72. }
  73. /**
  74. * Return true if this oid is an extension of the passed in branch, stem.
  75. * @param stem the arc or branch that is a possible parent.
  76. * @return true if the branch is on the passed in stem, false otherwise.
  77. */
  78. public virtual bool On(DerObjectIdentifier stem)
  79. {
  80. string id = Id, stemId = stem.Id;
  81. return id.Length > stemId.Length && id[stemId.Length] == '.' && BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.StartsWith(id, stemId);
  82. }
  83. internal DerObjectIdentifier(byte[] bytes)
  84. {
  85. this.identifier = MakeOidStringFromBytes(bytes);
  86. this.body = Arrays.Clone(bytes);
  87. }
  88. private void WriteField(
  89. Stream outputStream,
  90. long fieldValue)
  91. {
  92. byte[] result = new byte[9];
  93. int pos = 8;
  94. result[pos] = (byte)(fieldValue & 0x7f);
  95. while (fieldValue >= (1L << 7))
  96. {
  97. fieldValue >>= 7;
  98. result[--pos] = (byte)((fieldValue & 0x7f) | 0x80);
  99. }
  100. outputStream.Write(result, pos, 9 - pos);
  101. }
  102. private void WriteField(
  103. Stream outputStream,
  104. BigInteger fieldValue)
  105. {
  106. int byteCount = (fieldValue.BitLength + 6) / 7;
  107. if (byteCount == 0)
  108. {
  109. outputStream.WriteByte(0);
  110. }
  111. else
  112. {
  113. BigInteger tmpValue = fieldValue;
  114. byte[] tmp = new byte[byteCount];
  115. for (int i = byteCount-1; i >= 0; i--)
  116. {
  117. tmp[i] = (byte) ((tmpValue.IntValue & 0x7f) | 0x80);
  118. tmpValue = tmpValue.ShiftRight(7);
  119. }
  120. tmp[byteCount-1] &= 0x7f;
  121. outputStream.Write(tmp, 0, tmp.Length);
  122. }
  123. }
  124. private void DoOutput(MemoryStream bOut)
  125. {
  126. OidTokenizer tok = new OidTokenizer(identifier);
  127. string token = tok.NextToken();
  128. int first = int.Parse(token) * 40;
  129. token = tok.NextToken();
  130. if (token.Length <= 18)
  131. {
  132. WriteField(bOut, first + Int64.Parse(token));
  133. }
  134. else
  135. {
  136. WriteField(bOut, new BigInteger(token).Add(BigInteger.ValueOf(first)));
  137. }
  138. while (tok.HasMoreTokens)
  139. {
  140. token = tok.NextToken();
  141. if (token.Length <= 18)
  142. {
  143. WriteField(bOut, Int64.Parse(token));
  144. }
  145. else
  146. {
  147. WriteField(bOut, new BigInteger(token));
  148. }
  149. }
  150. }
  151. internal byte[] GetBody()
  152. {
  153. lock (this)
  154. {
  155. if (body == null)
  156. {
  157. MemoryStream bOut = new MemoryStream();
  158. DoOutput(bOut);
  159. body = bOut.ToArray();
  160. }
  161. }
  162. return body;
  163. }
  164. internal override void Encode(
  165. DerOutputStream derOut)
  166. {
  167. derOut.WriteEncoded(Asn1Tags.ObjectIdentifier, GetBody());
  168. }
  169. protected override int Asn1GetHashCode()
  170. {
  171. return identifier.GetHashCode();
  172. }
  173. protected override bool Asn1Equals(
  174. Asn1Object asn1Object)
  175. {
  176. DerObjectIdentifier other = asn1Object as DerObjectIdentifier;
  177. if (other == null)
  178. return false;
  179. return this.identifier.Equals(other.identifier);
  180. }
  181. public override string ToString()
  182. {
  183. return identifier;
  184. }
  185. private static bool IsValidBranchID(
  186. String branchID, int start)
  187. {
  188. bool periodAllowed = false;
  189. int pos = branchID.Length;
  190. while (--pos >= start)
  191. {
  192. char ch = branchID[pos];
  193. // TODO Leading zeroes?
  194. if ('0' <= ch && ch <= '9')
  195. {
  196. periodAllowed = true;
  197. continue;
  198. }
  199. if (ch == '.')
  200. {
  201. if (!periodAllowed)
  202. return false;
  203. periodAllowed = false;
  204. continue;
  205. }
  206. return false;
  207. }
  208. return periodAllowed;
  209. }
  210. private static bool IsValidIdentifier(string identifier)
  211. {
  212. if (identifier.Length < 3 || identifier[1] != '.')
  213. return false;
  214. char first = identifier[0];
  215. if (first < '0' || first > '2')
  216. return false;
  217. return IsValidBranchID(identifier, 2);
  218. }
  219. private const long LONG_LIMIT = (long.MaxValue >> 7) - 0x7f;
  220. private static string MakeOidStringFromBytes(
  221. byte[] bytes)
  222. {
  223. StringBuilder objId = new StringBuilder();
  224. long value = 0;
  225. BigInteger bigValue = null;
  226. bool first = true;
  227. for (int i = 0; i != bytes.Length; i++)
  228. {
  229. int b = bytes[i];
  230. if (value <= LONG_LIMIT)
  231. {
  232. value += (b & 0x7f);
  233. if ((b & 0x80) == 0) // end of number reached
  234. {
  235. if (first)
  236. {
  237. if (value < 40)
  238. {
  239. objId.Append('0');
  240. }
  241. else if (value < 80)
  242. {
  243. objId.Append('1');
  244. value -= 40;
  245. }
  246. else
  247. {
  248. objId.Append('2');
  249. value -= 80;
  250. }
  251. first = false;
  252. }
  253. objId.Append('.');
  254. objId.Append(value);
  255. value = 0;
  256. }
  257. else
  258. {
  259. value <<= 7;
  260. }
  261. }
  262. else
  263. {
  264. if (bigValue == null)
  265. {
  266. bigValue = BigInteger.ValueOf(value);
  267. }
  268. bigValue = bigValue.Or(BigInteger.ValueOf(b & 0x7f));
  269. if ((b & 0x80) == 0)
  270. {
  271. if (first)
  272. {
  273. objId.Append('2');
  274. bigValue = bigValue.Subtract(BigInteger.ValueOf(80));
  275. first = false;
  276. }
  277. objId.Append('.');
  278. objId.Append(bigValue);
  279. bigValue = null;
  280. value = 0;
  281. }
  282. else
  283. {
  284. bigValue = bigValue.ShiftLeft(7);
  285. }
  286. }
  287. }
  288. return objId.ToString();
  289. }
  290. private static readonly DerObjectIdentifier[] cache = new DerObjectIdentifier[1024];
  291. internal static DerObjectIdentifier FromOctetString(byte[] enc)
  292. {
  293. int hashCode = Arrays.GetHashCode(enc);
  294. int first = hashCode & 1023;
  295. lock (cache)
  296. {
  297. DerObjectIdentifier entry = cache[first];
  298. if (entry != null && Arrays.AreEqual(enc, entry.GetBody()))
  299. {
  300. return entry;
  301. }
  302. return cache[first] = new DerObjectIdentifier(enc);
  303. }
  304. }
  305. }
  306. }
  307. #pragma warning restore
  308. #endif