2004-04-08 Bernie Solomon <bernard@ugsolutions.com>
[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 Novell (http://www.novell.com)
10 // (C) 2004 IT+ A/S (http://www.itplus.dk)
11 //
12
13 using System;
14 using System.Collections;
15 using System.Security.Cryptography;
16 using System.Text;
17
18 namespace Mono.Security {
19
20         // References:
21         // a.   ITU ASN.1 standards (free download)
22         //      http://www.itu.int/ITU-T/studygroups/com17/languages/
23
24 #if INSIDE_CORLIB
25         internal
26 #else
27         public
28 #endif
29         class ASN1Convert {
30
31                 // RFC3280, section 4.2.1.5
32                 // CAs conforming to this profile MUST always encode certificate
33                 // validity dates through the year 2049 as UTCTime; certificate validity
34                 // dates in 2050 or later MUST be encoded as GeneralizedTime.
35                 static public ASN1 FromDateTime (DateTime dt) 
36                 {
37                         if (dt.Year < 2050) {
38                                 // UTCTIME
39                                 return new ASN1 (0x17, Encoding.ASCII.GetBytes (dt.ToString ("yyMMddHHmmss") + "Z"));
40                         }
41                         else {
42                                 // GENERALIZEDTIME
43                                 return new ASN1 (0x18, Encoding.ASCII.GetBytes (dt.ToString ("yyyyMMddHHmmss") + "Z"));
44                         }
45                 }
46
47                 static public ASN1 FromInt32 (Int32 value) 
48                 {
49                         byte[] integer = BitConverterLE.GetBytes (value);
50                         int x = 3;
51                         while (integer [x] == 0x00)
52                                 x--;
53                         ASN1 asn1 = new ASN1 (0x02);
54
55                         byte[] smallerInt = new byte [x + 1];
56                         int index = smallerInt.Length - 1;
57                         for (int i = 0; i < smallerInt.Length; i++) {
58                                 smallerInt [index] = integer [i];
59                                 index--;
60                         }
61                         asn1.Value = smallerInt;
62
63                         return asn1;
64                 }
65
66                 static public ASN1 FromOID (string oid) 
67                 {
68                         return new ASN1 (CryptoConfig.EncodeOID (oid));
69                 }
70
71                 static public ASN1 FromUnsignedBigInteger (byte[] integer) 
72                 {
73                         if (integer [0] == 0x00) {
74                                 // this first byte is added so we're sure it's an unsigned integer
75                                 // however we can't feed it into RSAParameters or DSAParameters
76                                 int length = integer.Length + 1;
77                                 byte[] uinteger = new byte [length];
78                                 Array.Copy (integer, 0, uinteger, 1, length);
79                                 integer = uinteger;
80                         }
81                         return new ASN1 (0x02, integer);
82                 }
83
84                 static public int ToInt32 (ASN1 asn1) 
85                 {
86                         if (asn1.Tag != 0x02)
87                                 throw new NotSupportedException ("Only integer can be converted");
88                         int x = 0;
89                         for (int i=0; i < asn1.Value.Length; i++)
90                                 x = (x << 8) + asn1.Value [i];
91                         return x;
92                 }
93
94                 // Convert a binary encoded OID to human readable string representation of 
95                 // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
96                 static public string ToOID (ASN1 asn1) 
97                 {
98                         byte[] aOID = asn1.Value;
99                         StringBuilder sb = new StringBuilder ();
100                         // Pick apart the OID
101                         byte x = (byte) (aOID[0] / 40);
102                         byte y = (byte) (aOID[0] % 40);
103                         if (x > 2) {
104                                 // Handle special case for large y if x = 2
105                                 y += (byte) ((x - 2) * 40);
106                                 x = 2;
107                         }
108                         sb.Append (x.ToString ());
109                         sb.Append (".");
110                         sb.Append (y.ToString ());
111                         ulong val = 0;
112                         for (x = 1; x < aOID.Length; x++) {
113                                 val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
114                                 if ( !((aOID [x] & 0x80) == 0x80)) {
115                                         sb.Append (".");
116                                         sb.Append (val.ToString ());
117                                         val = 0;
118                                 }
119                         }
120                         return sb.ToString ();
121                 }
122
123                 static public DateTime ToDateTime (ASN1 time) 
124                 {
125                         string t = Encoding.ASCII.GetString (time.Value);
126                         // to support both UTCTime and GeneralizedTime (and not so common format)
127                         string mask = null;
128                         switch (t.Length) {
129                                 case 11:
130                                         mask = "yyMMddHHmmZ"; // illegal I think ... must check
131                                         break;
132                                 case 13: 
133                                         // RFC3280: 4.1.2.5.1  UTCTime
134                                         int year = Convert.ToInt16 (t.Substring (0, 2));
135                                         // Where YY is greater than or equal to 50, the 
136                                         // year SHALL be interpreted as 19YY; and 
137                                         // Where YY is less than 50, the year SHALL be 
138                                         // interpreted as 20YY.
139                                         if (year >= 50)
140                                                 t = "19" + t;
141                                         else
142                                                 t = "20" + t;
143                                         mask = "yyyyMMddHHmmssZ";
144                                         break;
145                                 case 15:
146                                         mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
147                                         break;
148                         }
149                         return DateTime.ParseExact (t, mask, null);
150                 }
151         }
152 }