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