Add licensing info
[mono.git] / mcs / class / Mono.Security / 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 Novell (http://www.novell.com)
10 // (C) 2004 IT+ A/S (http://www.itplus.dk)
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Collections;
36 using System.Globalization;
37 using System.Security.Cryptography;
38 using System.Text;
39
40 namespace Mono.Security {
41
42         // References:
43         // a.   ITU ASN.1 standards (free download)
44         //      http://www.itu.int/ITU-T/studygroups/com17/languages/
45
46 #if INSIDE_CORLIB
47         internal
48 #else
49         public
50 #endif
51         sealed class ASN1Convert {
52
53                 private ASN1Convert ()
54                 {
55                 }
56
57                 // RFC3280, section 4.2.1.5
58                 // CAs conforming to this profile MUST always encode certificate
59                 // validity dates through the year 2049 as UTCTime; certificate validity
60                 // dates in 2050 or later MUST be encoded as GeneralizedTime.
61                 static public ASN1 FromDateTime (DateTime dt) 
62                 {
63                         if (dt.Year < 2050) {
64                                 // UTCTIME
65                                 return new ASN1 (0x17, Encoding.ASCII.GetBytes (
66                                         dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
67                                         CultureInfo.InvariantCulture) + "Z"));
68                         }
69                         else {
70                                 // GENERALIZEDTIME
71                                 return new ASN1 (0x18, Encoding.ASCII.GetBytes (
72                                         dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss", 
73                                         CultureInfo.InvariantCulture) + "Z"));
74                         }
75                 }
76
77                 static public ASN1 FromInt32 (Int32 value) 
78                 {
79                         byte[] integer = BitConverterLE.GetBytes (value);
80                         Array.Reverse (integer);
81                         int x = 0;
82                         while ((x < integer.Length) && (integer [x] == 0x00))
83                                 x++;
84                         ASN1 asn1 = new ASN1 (0x02);
85                         switch (x) {
86                         case 0:
87                                 asn1.Value = integer;
88                                 break;
89                         case 4:
90                                 asn1.Value = new byte [0];
91                                 break;
92                         default:
93                                 byte[] smallerInt = new byte [4 - x];
94                                 Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
95                                 asn1.Value = smallerInt;
96                                 break;
97                         }
98                         return asn1;
99                 }
100
101                 static public ASN1 FromOid (string oid) 
102                 {
103                         if (oid == null)
104                                 throw new ArgumentNullException ("oid");
105
106                         return new ASN1 (CryptoConfig.EncodeOID (oid));
107                 }
108
109                 static public ASN1 FromUnsignedBigInteger (byte[] big) 
110                 {
111                         if (big == null)
112                                 throw new ArgumentNullException ("big");
113                                 
114                         if (big [0] != 0x00) {
115                                 // this first byte is added so we're sure this is an unsigned integer
116                                 // however we can't feed it into RSAParameters or DSAParameters
117                                 int length = big.Length + 1;
118                                 byte[] uinteger = new byte [length];
119                                 Buffer.BlockCopy (big, 0, uinteger, 1, length - 1);
120                                 big = uinteger;
121                         }
122                         return new ASN1 (0x02, big);
123                 }
124
125                 static public int ToInt32 (ASN1 asn1) 
126                 {
127                         if (asn1 == null)
128                                 throw new ArgumentNullException ("asn1");
129                         if (asn1.Tag != 0x02)
130                                 throw new FormatException ("Only integer can be converted");
131
132                         int x = 0;
133                         for (int i=0; i < asn1.Value.Length; i++)
134                                 x = (x << 8) + asn1.Value [i];
135                         return x;
136                 }
137
138                 // Convert a binary encoded OID to human readable string representation of 
139                 // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
140                 static public string ToOid (ASN1 asn1) 
141                 {
142                         if (asn1 == null)
143                                 throw new ArgumentNullException ("asn1");
144
145                         byte[] aOID = asn1.Value;
146                         StringBuilder sb = new StringBuilder ();
147                         // Pick apart the OID
148                         byte x = (byte) (aOID[0] / 40);
149                         byte y = (byte) (aOID[0] % 40);
150                         if (x > 2) {
151                                 // Handle special case for large y if x = 2
152                                 y += (byte) ((x - 2) * 40);
153                                 x = 2;
154                         }
155                         sb.Append (x.ToString (CultureInfo.InvariantCulture));
156                         sb.Append (".");
157                         sb.Append (y.ToString (CultureInfo.InvariantCulture));
158                         ulong val = 0;
159                         for (x = 1; x < aOID.Length; x++) {
160                                 val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
161                                 if ( !((aOID [x] & 0x80) == 0x80)) {
162                                         sb.Append (".");
163                                         sb.Append (val.ToString (CultureInfo.InvariantCulture));
164                                         val = 0;
165                                 }
166                         }
167                         return sb.ToString ();
168                 }
169
170                 static public DateTime ToDateTime (ASN1 time) 
171                 {
172                         if (time == null)
173                                 throw new ArgumentNullException ("time");
174
175                         string t = Encoding.ASCII.GetString (time.Value);
176                         // to support both UTCTime and GeneralizedTime (and not so common format)
177                         string mask = null;
178                         switch (t.Length) {
179                                 case 11:
180                                         mask = "yyMMddHHmmZ"; // illegal I think ... must check
181                                         break;
182                                 case 13: 
183                                         // RFC3280: 4.1.2.5.1  UTCTime
184                                         int year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
185                                         // Where YY is greater than or equal to 50, the 
186                                         // year SHALL be interpreted as 19YY; and 
187                                         // Where YY is less than 50, the year SHALL be 
188                                         // interpreted as 20YY.
189                                         if (year >= 50)
190                                                 t = "19" + t;
191                                         else
192                                                 t = "20" + t;
193                                         mask = "yyyyMMddHHmmssZ";
194                                         break;
195                                 case 15:
196                                         mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
197                                         break;
198                         }
199                         return DateTime.ParseExact (t, mask, null);
200                 }
201         }
202 }