ASN1Convert.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. //
  2. // ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines
  3. //
  4. // Authors:
  5. // Sebastien Pouliot <sebastien@ximian.com>
  6. // Jesper Pedersen <jep@itplus.dk>
  7. //
  8. // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
  9. // (C) 2004 IT+ A/S (http://www.itplus.dk)
  10. // Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
  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.Globalization;
  34. using System.Security.Cryptography;
  35. using System.Text;
  36. namespace Mono.Security {
  37. // References:
  38. // a. ITU ASN.1 standards (free download)
  39. // http://www.itu.int/ITU-T/studygroups/com17/languages/
  40. #if INSIDE_CORLIB
  41. internal
  42. #else
  43. public
  44. #endif
  45. static class ASN1Convert {
  46. // RFC3280, section 4.2.1.5
  47. // CAs conforming to this profile MUST always encode certificate
  48. // validity dates through the year 2049 as UTCTime; certificate validity
  49. // dates in 2050 or later MUST be encoded as GeneralizedTime.
  50. // Under 1.x this API requires a Local datetime to be provided
  51. // Under 2.0 it will also accept a Utc datetime
  52. static public ASN1 FromDateTime (DateTime dt)
  53. {
  54. if (dt.Year < 2050) {
  55. // UTCTIME
  56. return new ASN1 (0x17, Encoding.ASCII.GetBytes (
  57. dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
  58. CultureInfo.InvariantCulture) + "Z"));
  59. }
  60. else {
  61. // GENERALIZEDTIME
  62. return new ASN1 (0x18, Encoding.ASCII.GetBytes (
  63. dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss",
  64. CultureInfo.InvariantCulture) + "Z"));
  65. }
  66. }
  67. static public ASN1 FromInt32 (Int32 value)
  68. {
  69. byte[] integer = BitConverterLE.GetBytes (value);
  70. Array.Reverse (integer);
  71. int x = 0;
  72. while ((x < integer.Length) && (integer [x] == 0x00))
  73. x++;
  74. ASN1 asn1 = new ASN1 (0x02);
  75. switch (x) {
  76. case 0:
  77. asn1.Value = integer;
  78. break;
  79. case 4:
  80. asn1.Value = new byte [1];
  81. break;
  82. default:
  83. byte[] smallerInt = new byte [4 - x];
  84. Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
  85. asn1.Value = smallerInt;
  86. break;
  87. }
  88. return asn1;
  89. }
  90. static public ASN1 FromOid (string oid)
  91. {
  92. if (oid == null)
  93. throw new ArgumentNullException ("oid");
  94. return new ASN1 (CryptoConfig.EncodeOID (oid));
  95. }
  96. static public ASN1 FromUnsignedBigInteger (byte[] big)
  97. {
  98. if (big == null)
  99. throw new ArgumentNullException ("big");
  100. // check for numbers that could be interpreted as negative (first bit)
  101. if (big [0] >= 0x80) {
  102. // in thie cas we add a new, empty, byte (position 0) so we're
  103. // sure this will always be interpreted an unsigned integer.
  104. // However we can't feed it into RSAParameters or DSAParameters
  105. int length = big.Length + 1;
  106. byte[] uinteger = new byte [length];
  107. Buffer.BlockCopy (big, 0, uinteger, 1, length - 1);
  108. big = uinteger;
  109. }
  110. return new ASN1 (0x02, big);
  111. }
  112. static public int ToInt32 (ASN1 asn1)
  113. {
  114. if (asn1 == null)
  115. throw new ArgumentNullException ("asn1");
  116. if (asn1.Tag != 0x02)
  117. throw new FormatException ("Only integer can be converted");
  118. int x = 0;
  119. for (int i=0; i < asn1.Value.Length; i++)
  120. x = (x << 8) + asn1.Value [i];
  121. return x;
  122. }
  123. // Convert a binary encoded OID to human readable string representation of
  124. // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
  125. static public string ToOid (ASN1 asn1)
  126. {
  127. if (asn1 == null)
  128. throw new ArgumentNullException ("asn1");
  129. byte[] aOID = asn1.Value;
  130. StringBuilder sb = new StringBuilder ();
  131. // Pick apart the OID
  132. byte x = (byte) (aOID[0] / 40);
  133. byte y = (byte) (aOID[0] % 40);
  134. if (x > 2) {
  135. // Handle special case for large y if x = 2
  136. y += (byte) ((x - 2) * 40);
  137. x = 2;
  138. }
  139. sb.Append (x.ToString (CultureInfo.InvariantCulture));
  140. sb.Append (".");
  141. sb.Append (y.ToString (CultureInfo.InvariantCulture));
  142. ulong val = 0;
  143. for (x = 1; x < aOID.Length; x++) {
  144. val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
  145. if ( !((aOID [x] & 0x80) == 0x80)) {
  146. sb.Append (".");
  147. sb.Append (val.ToString (CultureInfo.InvariantCulture));
  148. val = 0;
  149. }
  150. }
  151. return sb.ToString ();
  152. }
  153. static public DateTime ToDateTime (ASN1 time)
  154. {
  155. if (time == null)
  156. throw new ArgumentNullException ("time");
  157. string t = Encoding.ASCII.GetString (time.Value);
  158. // to support both UTCTime and GeneralizedTime (and not so common format)
  159. string mask = null;
  160. int year;
  161. switch (t.Length) {
  162. case 11:
  163. // illegal format, still it's supported for compatibility
  164. mask = "yyMMddHHmmZ";
  165. break;
  166. case 13:
  167. // RFC3280: 4.1.2.5.1 UTCTime
  168. year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
  169. // Where YY is greater than or equal to 50, the
  170. // year SHALL be interpreted as 19YY; and
  171. // Where YY is less than 50, the year SHALL be
  172. // interpreted as 20YY.
  173. if (year >= 50)
  174. t = "19" + t;
  175. else
  176. t = "20" + t;
  177. mask = "yyyyMMddHHmmssZ";
  178. break;
  179. case 15:
  180. mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
  181. break;
  182. case 17:
  183. // another illegal format (990630000000+1000), again supported for compatibility
  184. year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
  185. string century = (year >= 50) ? "19" : "20";
  186. // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET
  187. char sign = (t[12] == '+') ? '-' : '+';
  188. t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign,
  189. t[13], t[14], t[15], t[16]);
  190. mask = "yyyyMMddHHmmsszzz";
  191. break;
  192. }
  193. return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
  194. }
  195. }
  196. }