2 // ASN1Convert.cs: Abstract Syntax Notation 1 convertion routines
5 // Sebastien Pouliot <sebastien@ximian.com>
6 // Jesper Pedersen <jep@itplus.dk>
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)
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
33 using System.Collections;
34 using System.Globalization;
35 using System.Security.Cryptography;
38 namespace Mono.Security {
41 // a. ITU ASN.1 standards (free download)
42 // http://www.itu.int/ITU-T/studygroups/com17/languages/
50 static class ASN1Convert {
52 sealed class ASN1Convert {
54 private ASN1Convert ()
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.
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)
69 return new ASN1 (0x17, Encoding.ASCII.GetBytes (
70 dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
71 CultureInfo.InvariantCulture) + "Z"));
75 return new ASN1 (0x18, Encoding.ASCII.GetBytes (
76 dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss",
77 CultureInfo.InvariantCulture) + "Z"));
81 static public ASN1 FromInt32 (Int32 value)
83 byte[] integer = BitConverterLE.GetBytes (value);
84 Array.Reverse (integer);
86 while ((x < integer.Length) && (integer [x] == 0x00))
88 ASN1 asn1 = new ASN1 (0x02);
94 asn1.Value = new byte [1];
97 byte[] smallerInt = new byte [4 - x];
98 Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
99 asn1.Value = smallerInt;
105 static public ASN1 FromOid (string oid)
108 throw new ArgumentNullException ("oid");
110 return new ASN1 (CryptoConfig.EncodeOID (oid));
113 static public ASN1 FromUnsignedBigInteger (byte[] big)
116 throw new ArgumentNullException ("big");
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);
128 return new ASN1 (0x02, big);
131 static public int ToInt32 (ASN1 asn1)
134 throw new ArgumentNullException ("asn1");
135 if (asn1.Tag != 0x02)
136 throw new FormatException ("Only integer can be converted");
139 for (int i=0; i < asn1.Value.Length; i++)
140 x = (x << 8) + asn1.Value [i];
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)
149 throw new ArgumentNullException ("asn1");
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);
157 // Handle special case for large y if x = 2
158 y += (byte) ((x - 2) * 40);
161 sb.Append (x.ToString (CultureInfo.InvariantCulture));
163 sb.Append (y.ToString (CultureInfo.InvariantCulture));
165 for (x = 1; x < aOID.Length; x++) {
166 val = ((val << 7) | ((byte) (aOID [x] & 0x7F)));
167 if ( !((aOID [x] & 0x80) == 0x80)) {
169 sb.Append (val.ToString (CultureInfo.InvariantCulture));
173 return sb.ToString ();
176 static public DateTime ToDateTime (ASN1 time)
179 throw new ArgumentNullException ("time");
181 string t = Encoding.ASCII.GetString (time.Value);
182 // to support both UTCTime and GeneralizedTime (and not so common format)
188 // illegal format, still it's supported for compatibility
189 mask = "yyMMddHHmmZ";
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.
202 mask = "yyyyMMddHHmmssZ";
205 mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
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";
220 return DateTime.ParseExact (t, mask, null, DateTimeStyles.AdjustToUniversal);
222 DateTime result = DateTime.ParseExact (t, mask, null);
225 return result.ToUniversalTime ();