DerData.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. namespace Renci.SshNet.Common
  6. {
  7. /// <summary>
  8. /// Base class for DER encoded data.
  9. /// </summary>
  10. public class DerData
  11. {
  12. private const byte CONSTRUCTED = 0x20;
  13. private const byte BOOLEAN = 0x01;
  14. private const byte INTEGER = 0x02;
  15. //private const byte BITSTRING = 0x03;
  16. private const byte OCTETSTRING = 0x04;
  17. private const byte NULL = 0x05;
  18. private const byte OBJECTIDENTIFIER = 0x06;
  19. //private const byte EXTERNAL = 0x08;
  20. //private const byte ENUMERATED = 0x0a;
  21. private const byte SEQUENCE = 0x10;
  22. //private const byte SEQUENCEOF = 0x10; // for completeness
  23. //private const byte SET = 0x11;
  24. //private const byte SETOF = 0x11; // for completeness
  25. //private const byte NUMERICSTRING = 0x12;
  26. //private const byte PRINTABLESTRING = 0x13;
  27. //private const byte T61STRING = 0x14;
  28. //private const byte VIDEOTEXSTRING = 0x15;
  29. //private const byte IA5STRING = 0x16;
  30. //private const byte UTCTIME = 0x17;
  31. //private const byte GENERALIZEDTIME = 0x18;
  32. //private const byte GRAPHICSTRING = 0x19;
  33. //private const byte VISIBLESTRING = 0x1a;
  34. //private const byte GENERALSTRING = 0x1b;
  35. //private const byte UNIVERSALSTRING = 0x1c;
  36. //private const byte BMPSTRING = 0x1e;
  37. //private const byte UTF8STRING = 0x0c;
  38. //private const byte APPLICATION = 0x40;
  39. //private const byte TAGGED = 0x80;
  40. private List<byte> _data;
  41. private int _readerIndex = 0;
  42. private int _lastIndex;
  43. /// <summary>
  44. /// Gets a value indicating whether end of data is reached.
  45. /// </summary>
  46. /// <value>
  47. /// <c>true</c> if end of data is reached; otherwise, <c>false</c>.
  48. /// </value>
  49. public bool IsEndOfData
  50. {
  51. get
  52. {
  53. return this._readerIndex >= this._lastIndex;
  54. }
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="DerData"/> class.
  58. /// </summary>
  59. public DerData()
  60. {
  61. this._data = new List<byte>();
  62. }
  63. /// <summary>
  64. /// Initializes a new instance of the <see cref="DerData"/> class.
  65. /// </summary>
  66. /// <param name="data">DER encoded data.</param>
  67. public DerData(byte[] data)
  68. {
  69. this._data = new List<byte>(data);
  70. var dataType = this.ReadByte();
  71. var length = this.ReadLength();
  72. this._lastIndex = this._readerIndex + length;
  73. }
  74. /// <summary>
  75. /// Encodes written data as DER byte array.
  76. /// </summary>
  77. /// <returns></returns>
  78. public byte[] Encode()
  79. {
  80. var length = this._data.Count();
  81. var lengthBytes = this.GetLength(length);
  82. this._data.InsertRange(0, lengthBytes);
  83. this._data.Insert(0, CONSTRUCTED | SEQUENCE);
  84. return this._data.ToArray();
  85. }
  86. /// <summary>
  87. /// Reads next mpint data type from internal buffer.
  88. /// </summary>
  89. /// <returns>mpint read.</returns>
  90. public BigInteger ReadBigInteger()
  91. {
  92. var type = this.ReadByte();
  93. if (type != INTEGER)
  94. throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected.");
  95. var length = this.ReadLength();
  96. var data = this.ReadBytes(length);
  97. return new BigInteger(data.Reverse().ToArray());
  98. }
  99. /// <summary>
  100. /// Reads next int data type from internal buffer.
  101. /// </summary>
  102. /// <returns>int read.</returns>
  103. public int ReadInteger()
  104. {
  105. var type = this.ReadByte();
  106. if (type != INTEGER)
  107. throw new InvalidOperationException("Invalid data type, INTEGER(02) is expected.");
  108. var length = this.ReadLength();
  109. var data = this.ReadBytes(length);
  110. if (length > 4)
  111. throw new InvalidOperationException("Integer type cannot occupy more then 4 bytes");
  112. var result = 0;
  113. var shift = (length - 1) * 8;
  114. for (int i = 0; i < length; i++)
  115. {
  116. result |= data[i] << shift;
  117. shift -= 8;
  118. }
  119. //return (int)(data[0] << 56 | data[1] << 48 | data[2] << 40 | data[3] << 32 | data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]);
  120. return result;
  121. }
  122. /// <summary>
  123. /// Writes BOOLEAN data into internal buffer.
  124. /// </summary>
  125. /// <param name="data">UInt32 data to write.</param>
  126. public void Write(bool data)
  127. {
  128. this._data.Add(BOOLEAN);
  129. this._data.Add(1);
  130. this._data.Add((byte)(data ? 1 : 0));
  131. }
  132. /// <summary>
  133. /// Writes UInt32 data into internal buffer.
  134. /// </summary>
  135. /// <param name="data">UInt32 data to write.</param>
  136. public void Write(UInt32 data)
  137. {
  138. var bytes = data.GetBytes();
  139. this._data.Add(INTEGER);
  140. var length = this.GetLength(bytes.Length);
  141. this.WriteBytes(length);
  142. this.WriteBytes(bytes);
  143. }
  144. /// <summary>
  145. /// Writes INTEGER data into internal buffer.
  146. /// </summary>
  147. /// <param name="data">BigInteger data to write.</param>
  148. public void Write(BigInteger data)
  149. {
  150. var bytes = data.ToByteArray().Reverse().ToList();
  151. this._data.Add(INTEGER);
  152. var length = this.GetLength(bytes.Count);
  153. this.WriteBytes(length);
  154. this.WriteBytes(bytes);
  155. }
  156. /// <summary>
  157. /// Writes OCTETSTRING data into internal buffer.
  158. /// </summary>
  159. /// <param name="data">The data.</param>
  160. public void Write(byte[] data)
  161. {
  162. this._data.Add(OCTETSTRING);
  163. var length = this.GetLength(data.Length);
  164. this.WriteBytes(length);
  165. this.WriteBytes(data);
  166. }
  167. /// <summary>
  168. /// Writes OBJECTIDENTIFIER data into internal buffer.
  169. /// </summary>
  170. /// <param name="identifiers">The identifiers.</param>
  171. public void Write(ObjectIdentifier identifier)
  172. {
  173. var temp = new ulong[identifier.Identifiers.Length - 1];
  174. temp[0] = identifier.Identifiers[0] * 40 + identifier.Identifiers[1];
  175. Array.Copy(identifier.Identifiers, 2, temp, 1, identifier.Identifiers.Length - 2);
  176. var bytes = new List<byte>();
  177. foreach (var subidentifier in temp)
  178. {
  179. var item = subidentifier;
  180. var buffer = new byte[8];
  181. var bufferIndex = buffer.Length - 1;
  182. var current = (byte)(item & 0x7F);
  183. do
  184. {
  185. buffer[bufferIndex] = current;
  186. if (bufferIndex < buffer.Length - 1)
  187. buffer[bufferIndex] |= (byte)0x80;
  188. item >>= 7;
  189. current = (byte)(item & 0x7F);
  190. bufferIndex--;
  191. }
  192. while (current > 0);
  193. for (int i = bufferIndex + 1; i < buffer.Length; i++)
  194. {
  195. bytes.Add(buffer[i]);
  196. }
  197. }
  198. this._data.Add(OBJECTIDENTIFIER);
  199. var length = this.GetLength(bytes.Count);
  200. this.WriteBytes(length);
  201. this.WriteBytes(bytes);
  202. }
  203. /// <summary>
  204. /// Writes NULL data into internal buffer.
  205. /// </summary>
  206. public void WriteNull()
  207. {
  208. this._data.Add(NULL);
  209. this._data.Add(0);
  210. }
  211. /// <summary>
  212. /// Writes DerData data into internal buffer.
  213. /// </summary>
  214. /// <param name="data">DerData data to write.</param>
  215. public void Write(DerData data)
  216. {
  217. var bytes = data.Encode();
  218. this._data.AddRange(bytes);
  219. }
  220. private byte[] GetLength(int length)
  221. {
  222. if (length > 127)
  223. {
  224. int size = 1;
  225. int val = length;
  226. while ((val >>= 8) != 0)
  227. size++;
  228. var data = new byte[size];
  229. data[0] = (byte)(size | 0x80);
  230. for (int i = (size - 1) * 8, j = 1; i >= 0; i -= 8, j++)
  231. {
  232. data[j] = (byte)(length >> i);
  233. }
  234. return data;
  235. }
  236. else
  237. {
  238. return new byte[] { (byte)length };
  239. }
  240. }
  241. private int ReadLength()
  242. {
  243. int length = this.ReadByte();
  244. if (length == 0x80)
  245. {
  246. throw new NotSupportedException("Indefinite-length encoding is not supported.");
  247. }
  248. if (length > 127)
  249. {
  250. int size = length & 0x7f;
  251. // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
  252. if (size > 4)
  253. throw new InvalidOperationException(string.Format("DER length is '{0}' and cannot be more than 4 bytes.", size));
  254. length = 0;
  255. for (int i = 0; i < size; i++)
  256. {
  257. int next = this.ReadByte();
  258. length = (length << 8) + next;
  259. }
  260. if (length < 0)
  261. throw new InvalidOperationException("Corrupted data - negative length found");
  262. //if (length >= limit) // after all we must have read at least 1 byte
  263. // throw new IOException("Corrupted stream - out of bounds length found");
  264. }
  265. return length;
  266. }
  267. private void WriteBytes(IEnumerable<byte> data)
  268. {
  269. this._data.AddRange(data);
  270. }
  271. private byte ReadByte()
  272. {
  273. if (this._readerIndex > this._data.Count)
  274. throw new InvalidOperationException("Read out of boundaries.");
  275. return this._data[this._readerIndex++];
  276. }
  277. private byte[] ReadBytes(int length)
  278. {
  279. if (this._readerIndex + length > this._data.Count)
  280. throw new InvalidOperationException("Read out of boundaries.");
  281. var result = new byte[length];
  282. this._data.CopyTo(this._readerIndex, result, 0, length);
  283. this._readerIndex += length;
  284. return result;
  285. }
  286. }
  287. }