PemReader.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Encoders;
  7. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO.Pem
  8. {
  9. public class PemReader
  10. {
  11. private readonly TextReader reader;
  12. private readonly MemoryStream buffer;
  13. private readonly StreamWriter textBuffer;
  14. private readonly IList pushback = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
  15. int c = 0;
  16. public PemReader(TextReader reader)
  17. {
  18. if (reader == null)
  19. throw new ArgumentNullException("reader");
  20. buffer = new MemoryStream();
  21. textBuffer = new StreamWriter(buffer);
  22. this.reader = reader;
  23. }
  24. public TextReader Reader
  25. {
  26. get { return reader; }
  27. }
  28. /// <returns>
  29. /// A <see cref="PemObject"/>
  30. /// </returns>
  31. /// <exception cref="IOException"></exception>
  32. public PemObject ReadPemObject()
  33. {
  34. //
  35. // Look for BEGIN
  36. //
  37. for (; ; )
  38. {
  39. // Seek a leading dash, ignore anything up to that point.
  40. if (!seekDash())
  41. {
  42. // There are no pem objects here.
  43. return null;
  44. }
  45. // consume dash [-----]BEGIN ...
  46. if (!consumeDash())
  47. {
  48. throw new IOException("no data after consuming leading dashes");
  49. }
  50. skipWhiteSpace();
  51. if (!expect("BEGIN"))
  52. {
  53. continue;
  54. }
  55. break;
  56. }
  57. skipWhiteSpace();
  58. //
  59. // Consume type, accepting whitespace
  60. //
  61. if (!bufferUntilStopChar('-',false) )
  62. {
  63. throw new IOException("ran out of data before consuming type");
  64. }
  65. string type = bufferedString().Trim();
  66. // Consume dashes after type.
  67. if (!consumeDash())
  68. {
  69. throw new IOException("ran out of data consuming header");
  70. }
  71. skipWhiteSpace();
  72. //
  73. // Read ahead looking for headers.
  74. // Look for a colon for up to 64 characters, as an indication there might be a header.
  75. //
  76. IList headers = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
  77. while (seekColon(64))
  78. {
  79. if (!bufferUntilStopChar(':',false))
  80. {
  81. throw new IOException("ran out of data reading header key value");
  82. }
  83. string key = bufferedString().Trim();
  84. c = Read();
  85. if (c != ':')
  86. {
  87. throw new IOException("expected colon");
  88. }
  89. //
  90. // We are going to look for well formed headers, if they do not end with a "LF" we cannot
  91. // discern where they end.
  92. //
  93. if (!bufferUntilStopChar('\n', false)) // Now read to the end of the line.
  94. {
  95. throw new IOException("ran out of data before consuming header value");
  96. }
  97. skipWhiteSpace();
  98. string value = bufferedString().Trim();
  99. headers.Add(new PemHeader(key,value));
  100. }
  101. //
  102. // Consume payload, ignoring all white space until we encounter a '-'
  103. //
  104. skipWhiteSpace();
  105. if (!bufferUntilStopChar('-',true))
  106. {
  107. throw new IOException("ran out of data before consuming payload");
  108. }
  109. string payload = bufferedString();
  110. // Seek the start of the end.
  111. if (!seekDash())
  112. {
  113. throw new IOException("did not find leading '-'");
  114. }
  115. if (!consumeDash())
  116. {
  117. throw new IOException("no data after consuming trailing dashes");
  118. }
  119. if (!expect("END "+type))
  120. {
  121. throw new IOException("END "+type+" was not found.");
  122. }
  123. if (!seekDash())
  124. {
  125. throw new IOException("did not find ending '-'");
  126. }
  127. // consume trailing dashes.
  128. consumeDash();
  129. return new PemObject(type, headers, Base64.Decode(payload));
  130. }
  131. private string bufferedString()
  132. {
  133. textBuffer.Flush();
  134. string value = Strings.FromUtf8ByteArray(buffer.ToArray());
  135. buffer.Position = 0;
  136. buffer.SetLength(0);
  137. return value;
  138. }
  139. private bool seekDash()
  140. {
  141. c = 0;
  142. while((c = Read()) >=0)
  143. {
  144. if (c == '-')
  145. {
  146. break;
  147. }
  148. }
  149. PushBack(c);
  150. return c == '-';
  151. }
  152. /// <summary>
  153. /// Seek ':" up to the limit.
  154. /// </summary>
  155. /// <param name="upTo"></param>
  156. /// <returns></returns>
  157. private bool seekColon(int upTo)
  158. {
  159. c = 0;
  160. bool colonFound = false;
  161. IList read = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.CreateArrayList();
  162. for (; upTo>=0 && c >=0; upTo--)
  163. {
  164. c = Read();
  165. read.Add(c);
  166. if (c == ':')
  167. {
  168. colonFound = true;
  169. break;
  170. }
  171. }
  172. while(read.Count>0)
  173. {
  174. PushBack((int)read[read.Count-1]);
  175. read.RemoveAt(read.Count-1);
  176. }
  177. return colonFound;
  178. }
  179. /// <summary>
  180. /// Consume the dashes
  181. /// </summary>
  182. /// <returns></returns>
  183. private bool consumeDash()
  184. {
  185. c = 0;
  186. while ((c = Read()) >= 0)
  187. {
  188. if (c != '-')
  189. {
  190. break;
  191. }
  192. }
  193. PushBack(c);
  194. return c != -1;
  195. }
  196. /// <summary>
  197. /// Skip white space leave char in stream.
  198. /// </summary>
  199. private void skipWhiteSpace()
  200. {
  201. while ((c = Read()) >= 0)
  202. {
  203. if (c > ' ')
  204. {
  205. break;
  206. }
  207. }
  208. PushBack(c);
  209. }
  210. /// <summary>
  211. /// Read forward consuming the expected string.
  212. /// </summary>
  213. /// <param name="value">expected string</param>
  214. /// <returns>false if not consumed</returns>
  215. private bool expect(String value)
  216. {
  217. for (int t=0; t<value.Length; t++)
  218. {
  219. c = Read();
  220. if (c == value[t])
  221. {
  222. continue;
  223. } else
  224. {
  225. return false;
  226. }
  227. }
  228. return true;
  229. }
  230. /// <summary>
  231. /// Consume until dash.
  232. /// </summary>
  233. /// <returns>true if stream end not met</returns>
  234. private bool bufferUntilStopChar(char stopChar, bool skipWhiteSpace)
  235. {
  236. while ((c = Read()) >= 0)
  237. {
  238. if (skipWhiteSpace && c <=' ')
  239. {
  240. continue;
  241. }
  242. if (c != stopChar)
  243. {
  244. textBuffer.Write((char)c);
  245. textBuffer.Flush();
  246. } else
  247. {
  248. PushBack(c);
  249. break;
  250. }
  251. }
  252. return c > -1;
  253. }
  254. private void PushBack(int value)
  255. {
  256. if (pushback.Count == 0)
  257. {
  258. pushback.Add(value);
  259. } else
  260. {
  261. pushback.Insert(0, value);
  262. }
  263. }
  264. private int Read()
  265. {
  266. if (pushback.Count>0)
  267. {
  268. int i = (int)pushback[0];
  269. pushback.RemoveAt(0);
  270. return i;
  271. }
  272. return reader.Read();
  273. }
  274. }
  275. }
  276. #pragma warning restore
  277. #endif