ASN1.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //
  2. // ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator
  3. //
  4. // Authors:
  5. // Sebastien Pouliot <sebastien@ximian.com>
  6. // Jesper Pedersen <jep@itplus.dk>
  7. //
  8. // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
  9. // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
  10. // (C) 2004 IT+ A/S (http://www.itplus.dk)
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. using System;
  32. using System.Collections;
  33. using System.IO;
  34. using System.Text;
  35. namespace Mono.Security {
  36. // References:
  37. // a. ITU ASN.1 standards (free download)
  38. // http://www.itu.int/ITU-T/studygroups/com17/languages/
  39. #if INSIDE_CORLIB
  40. internal
  41. #else
  42. public
  43. #endif
  44. class ASN1 {
  45. private byte m_nTag;
  46. private byte[] m_aValue;
  47. private ArrayList elist;
  48. public ASN1 () : this (0x00, null) {}
  49. public ASN1 (byte tag) : this (tag, null) {}
  50. public ASN1 (byte tag, byte[] data)
  51. {
  52. m_nTag = tag;
  53. m_aValue = data;
  54. }
  55. public ASN1 (byte[] data)
  56. {
  57. m_nTag = data [0];
  58. int nLenLength = 0;
  59. int nLength = data [1];
  60. if (nLength > 0x80) {
  61. // composed length
  62. nLenLength = nLength - 0x80;
  63. nLength = 0;
  64. for (int i = 0; i < nLenLength; i++) {
  65. nLength *= 256;
  66. nLength += data [i + 2];
  67. }
  68. }
  69. else if (nLength == 0x80) {
  70. // undefined length encoding
  71. throw new NotSupportedException ("Undefined length encoding.");
  72. }
  73. m_aValue = new byte [nLength];
  74. Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength);
  75. if ((m_nTag & 0x20) == 0x20) {
  76. int nStart = (2 + nLenLength);
  77. Decode (data, ref nStart, data.Length);
  78. }
  79. }
  80. public int Count {
  81. get {
  82. if (elist == null)
  83. return 0;
  84. return elist.Count;
  85. }
  86. }
  87. public byte Tag {
  88. get { return m_nTag; }
  89. }
  90. public int Length {
  91. get {
  92. if (m_aValue != null)
  93. return m_aValue.Length;
  94. else
  95. return 0;
  96. }
  97. }
  98. public byte[] Value {
  99. get {
  100. if (m_aValue == null)
  101. GetBytes ();
  102. return (byte[]) m_aValue.Clone ();
  103. }
  104. set {
  105. if (value != null)
  106. m_aValue = (byte[]) value.Clone ();
  107. }
  108. }
  109. private bool CompareArray (byte[] array1, byte[] array2)
  110. {
  111. bool bResult = (array1.Length == array2.Length);
  112. if (bResult) {
  113. for (int i = 0; i < array1.Length; i++) {
  114. if (array1[i] != array2[i])
  115. return false;
  116. }
  117. }
  118. return bResult;
  119. }
  120. public bool Equals (byte[] asn1)
  121. {
  122. return CompareArray (this.GetBytes (), asn1);
  123. }
  124. public bool CompareValue (byte[] value)
  125. {
  126. return CompareArray (m_aValue, value);
  127. }
  128. public ASN1 Add (ASN1 asn1)
  129. {
  130. if (asn1 != null) {
  131. if (elist == null)
  132. elist = new ArrayList ();
  133. elist.Add (asn1);
  134. }
  135. return asn1;
  136. }
  137. public virtual byte[] GetBytes ()
  138. {
  139. byte[] val = null;
  140. if (Count > 0) {
  141. int esize = 0;
  142. ArrayList al = new ArrayList ();
  143. foreach (ASN1 a in elist) {
  144. byte[] item = a.GetBytes ();
  145. al.Add (item);
  146. esize += item.Length;
  147. }
  148. val = new byte [esize];
  149. int pos = 0;
  150. for (int i=0; i < elist.Count; i++) {
  151. byte[] item = (byte[]) al[i];
  152. Buffer.BlockCopy (item, 0, val, pos, item.Length);
  153. pos += item.Length;
  154. }
  155. } else if (m_aValue != null) {
  156. val = m_aValue;
  157. }
  158. byte[] der;
  159. int nLengthLen = 0;
  160. if (val != null) {
  161. int nLength = val.Length;
  162. // special for length > 127
  163. if (nLength > 127) {
  164. if (nLength <= Byte.MaxValue) {
  165. der = new byte [3 + nLength];
  166. Buffer.BlockCopy (val, 0, der, 3, nLength);
  167. nLengthLen = 0x81;
  168. der[2] = (byte)(nLength);
  169. }
  170. else if (nLength <= UInt16.MaxValue) {
  171. der = new byte [4 + nLength];
  172. Buffer.BlockCopy (val, 0, der, 4, nLength);
  173. nLengthLen = 0x82;
  174. der[2] = (byte)(nLength >> 8);
  175. der[3] = (byte)(nLength);
  176. }
  177. else if (nLength <= 0xFFFFFF) {
  178. // 24 bits
  179. der = new byte [5 + nLength];
  180. Buffer.BlockCopy (val, 0, der, 5, nLength);
  181. nLengthLen = 0x83;
  182. der [2] = (byte)(nLength >> 16);
  183. der [3] = (byte)(nLength >> 8);
  184. der [4] = (byte)(nLength);
  185. }
  186. else {
  187. // max (Length is an integer) 32 bits
  188. der = new byte [6 + nLength];
  189. Buffer.BlockCopy (val, 0, der, 6, nLength);
  190. nLengthLen = 0x84;
  191. der [2] = (byte)(nLength >> 24);
  192. der [3] = (byte)(nLength >> 16);
  193. der [4] = (byte)(nLength >> 8);
  194. der [5] = (byte)(nLength);
  195. }
  196. }
  197. else {
  198. // basic case (no encoding)
  199. der = new byte [2 + nLength];
  200. Buffer.BlockCopy (val, 0, der, 2, nLength);
  201. nLengthLen = nLength;
  202. }
  203. if (m_aValue == null)
  204. m_aValue = val;
  205. }
  206. else
  207. der = new byte[2];
  208. der[0] = m_nTag;
  209. der[1] = (byte)nLengthLen;
  210. return der;
  211. }
  212. // Note: Recursive
  213. protected void Decode (byte[] asn1, ref int anPos, int anLength)
  214. {
  215. byte nTag;
  216. int nLength;
  217. byte[] aValue;
  218. // minimum is 2 bytes (tag + length of 0)
  219. while (anPos < anLength - 1) {
  220. DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
  221. // sometimes we get trailing 0
  222. if (nTag == 0)
  223. continue;
  224. ASN1 elm = Add (new ASN1 (nTag, aValue));
  225. if ((nTag & 0x20) == 0x20) {
  226. int nConstructedPos = anPos;
  227. elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
  228. }
  229. anPos += nLength; // value length
  230. }
  231. }
  232. // TLV : Tag - Length - Value
  233. protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content)
  234. {
  235. tag = asn1 [pos++];
  236. length = asn1 [pos++];
  237. // special case where L contains the Length of the Length + 0x80
  238. if ((length & 0x80) == 0x80) {
  239. int nLengthLen = length & 0x7F;
  240. length = 0;
  241. for (int i = 0; i < nLengthLen; i++)
  242. length = length * 256 + asn1 [pos++];
  243. }
  244. content = new byte [length];
  245. Buffer.BlockCopy (asn1, pos, content, 0, length);
  246. }
  247. public ASN1 this [int index] {
  248. get {
  249. try {
  250. if ((elist == null) || (index >= elist.Count))
  251. return null;
  252. return (ASN1) elist [index];
  253. }
  254. catch (ArgumentOutOfRangeException) {
  255. return null;
  256. }
  257. }
  258. }
  259. public ASN1 Element (int index, byte anTag)
  260. {
  261. try {
  262. if ((elist == null) || (index >= elist.Count))
  263. return null;
  264. ASN1 elm = (ASN1) elist [index];
  265. if (elm.Tag == anTag)
  266. return elm;
  267. else
  268. return null;
  269. }
  270. catch (ArgumentOutOfRangeException) {
  271. return null;
  272. }
  273. }
  274. public override string ToString()
  275. {
  276. StringBuilder hexLine = new StringBuilder ();
  277. // Add tag
  278. hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine);
  279. // Add length
  280. hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine);
  281. // Add value
  282. hexLine.Append ("Value: ");
  283. hexLine.Append (Environment.NewLine);
  284. for (int i = 0; i < Value.Length; i++) {
  285. hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2"));
  286. if ((i+1) % 16 == 0)
  287. hexLine.AppendFormat (Environment.NewLine);
  288. }
  289. return hexLine.ToString ();
  290. }
  291. public void SaveToFile (string filename)
  292. {
  293. if (filename == null)
  294. throw new ArgumentNullException ("filename");
  295. using (FileStream fs = File.Create (filename)) {
  296. byte[] data = GetBytes ();
  297. fs.Write (data, 0, data.Length);
  298. }
  299. }
  300. }
  301. }