DerObjectIdentifier.cs 10 KB

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