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/
49 static class ASN1Convert
{
50 // RFC3280, section 4.2.1.5
51 // CAs conforming to this profile MUST always encode certificate
52 // validity dates through the year 2049 as UTCTime; certificate validity
53 // dates in 2050 or later MUST be encoded as GeneralizedTime.
55 // Under 1.x this API requires a Local datetime to be provided
56 // Under 2.0 it will also accept a Utc datetime
57 static public ASN1
FromDateTime (DateTime dt
)
61 return new ASN1 (0x17, Encoding
.ASCII
.GetBytes (
62 dt
.ToUniversalTime ().ToString ("yyMMddHHmmss",
63 CultureInfo
.InvariantCulture
) + "Z"));
67 return new ASN1 (0x18, Encoding
.ASCII
.GetBytes (
68 dt
.ToUniversalTime ().ToString ("yyyyMMddHHmmss",
69 CultureInfo
.InvariantCulture
) + "Z"));
73 static public ASN1
FromInt32 (Int32
value)
75 byte[] integer
= BitConverterLE
.GetBytes (value);
76 Array
.Reverse (integer
);
78 while ((x
< integer
.Length
) && (integer
[x
] == 0x00))
80 ASN1 asn1
= new ASN1 (0x02);
86 asn1
.Value
= new byte [1];
89 byte[] smallerInt
= new byte [4 - x
];
90 Buffer
.BlockCopy (integer
, x
, smallerInt
, 0, smallerInt
.Length
);
91 asn1
.Value
= smallerInt
;
97 static public ASN1
FromOid (string oid
)
100 throw new ArgumentNullException ("oid");
102 return new ASN1 (CryptoConfig
.EncodeOID (oid
));
105 static public ASN1
FromUnsignedBigInteger (byte[] big
)
108 throw new ArgumentNullException ("big");
110 // check for numbers that could be interpreted as negative (first bit)
111 if (big
[0] >= 0x80) {
112 // in thie cas we add a new, empty, byte (position 0) so we're
113 // sure this will always be interpreted an unsigned integer.
114 // However we can't feed it into RSAParameters or DSAParameters
115 int length
= big
.Length
+ 1;
116 byte[] uinteger
= new byte [length
];
117 Buffer
.BlockCopy (big
, 0, uinteger
, 1, length
- 1);
120 return new ASN1 (0x02, big
);
123 static public int ToInt32 (ASN1 asn1
)
126 throw new ArgumentNullException ("asn1");
127 if (asn1
.Tag
!= 0x02)
128 throw new FormatException ("Only integer can be converted");
131 for (int i
=0; i
< asn1
.Value
.Length
; i
++)
132 x
= (x
<< 8) + asn1
.Value
[i
];
136 // Convert a binary encoded OID to human readable string representation of
137 // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
138 static public string ToOid (ASN1 asn1
)
141 throw new ArgumentNullException ("asn1");
143 byte[] aOID
= asn1
.Value
;
144 StringBuilder sb
= new StringBuilder ();
145 // Pick apart the OID
146 byte x
= (byte) (aOID
[0] / 40);
147 byte y
= (byte) (aOID
[0] % 40);
149 // Handle special case for large y if x = 2
150 y
+= (byte) ((x
- 2) * 40);
153 sb
.Append (x
.ToString (CultureInfo
.InvariantCulture
));
155 sb
.Append (y
.ToString (CultureInfo
.InvariantCulture
));
157 for (x
= 1; x
< aOID
.Length
; x
++) {
158 val
= ((val
<< 7) | ((byte) (aOID
[x
] & 0x7F)));
159 if ( !((aOID
[x
] & 0x80) == 0x80)) {
161 sb
.Append (val
.ToString (CultureInfo
.InvariantCulture
));
165 return sb
.ToString ();
168 static public DateTime
ToDateTime (ASN1 time
)
171 throw new ArgumentNullException ("time");
173 string t
= Encoding
.ASCII
.GetString (time
.Value
);
174 // to support both UTCTime and GeneralizedTime (and not so common format)
179 // illegal format, still it's supported for compatibility
180 mask
= "yyMMddHHmmZ";
183 // RFC3280: 4.1.2.5.1 UTCTime
184 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.
193 mask
= "yyyyMMddHHmmssZ";
196 mask
= "yyyyMMddHHmmssZ"; // GeneralizedTime
199 // another illegal format (990630000000+1000), again supported for compatibility
200 year
= Convert
.ToInt16 (t
.Substring (0, 2), CultureInfo
.InvariantCulture
);
201 string century
= (year
>= 50) ? "19" : "20";
202 // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET
203 char sign
= (t
[12] == '+') ? '-' : '+';
204 t
= String
.Format ("{0}{1}{2}{3}{4}:{5}{6}", century
, t
.Substring (0, 12), sign
,
205 t
[13], t
[14], t
[15], t
[16]);
206 mask
= "yyyyMMddHHmmsszzz";
209 return DateTime
.ParseExact (t
, mask
, CultureInfo
.InvariantCulture
, DateTimeStyles
.AdjustToUniversal
);