PemReader.cs 6.7 KB

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