DerData.cs 11 KB

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