New tests.
[mono.git] / mcs / class / corlib / Mono.Security / ASN1Convert.cs
index f8e5269dbfd5f1896bd3be1234414103f14007f0..3a1cf930183b92155e5fd1b872a803f6496de0e5 100644 (file)
@@ -6,8 +6,27 @@
 //     Jesper Pedersen  <jep@itplus.dk>
 //
 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// (C) 2004 Novell (http://www.novell.com)
 // (C) 2004 IT+ A/S (http://www.itplus.dk)
+// Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 using System;
@@ -27,62 +46,75 @@ namespace Mono.Security {
 #else
        public
 #endif
-       sealed class ASN1Convert {
-
-               private ASN1Convert ()
-               {
-               }
-
+       static class ASN1Convert {
                // RFC3280, section 4.2.1.5
                // CAs conforming to this profile MUST always encode certificate
                // validity dates through the year 2049 as UTCTime; certificate validity
                // dates in 2050 or later MUST be encoded as GeneralizedTime.
+
+               // Under 1.x this API requires a Local datetime to be provided
+               // Under 2.0 it will also accept a Utc datetime
                static public ASN1 FromDateTime (DateTime dt) 
                {
                        if (dt.Year < 2050) {
                                // UTCTIME
                                return new ASN1 (0x17, Encoding.ASCII.GetBytes (
-                                       dt.ToString ("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z"));
+                                       dt.ToUniversalTime ().ToString ("yyMMddHHmmss",
+                                       CultureInfo.InvariantCulture) + "Z"));
                        }
                        else {
                                // GENERALIZEDTIME
                                return new ASN1 (0x18, Encoding.ASCII.GetBytes (
-                                       dt.ToString ("yyyyMMddHHmmss", CultureInfo.InvariantCulture) + "Z"));
+                                       dt.ToUniversalTime ().ToString ("yyyyMMddHHmmss", 
+                                       CultureInfo.InvariantCulture) + "Z"));
                        }
                }
 
                static public ASN1 FromInt32 (Int32 value) 
                {
                        byte[] integer = BitConverterLE.GetBytes (value);
-                       int x = 3;
-                       while (integer [x] == 0x00)
-                               x--;
+                       Array.Reverse (integer);
+                       int x = 0;
+                       while ((x < integer.Length) && (integer [x] == 0x00))
+                               x++;
                        ASN1 asn1 = new ASN1 (0x02);
-
-                       byte[] smallerInt = new byte [x + 1];
-                       int index = smallerInt.Length - 1;
-                       for (int i = 0; i < smallerInt.Length; i++) {
-                               smallerInt [index] = integer [i];
-                               index--;
+                       switch (x) {
+                       case 0:
+                               asn1.Value = integer;
+                               break;
+                       case 4:
+                               asn1.Value = new byte [1];
+                               break;
+                       default:
+                               byte[] smallerInt = new byte [4 - x];
+                               Buffer.BlockCopy (integer, x, smallerInt, 0, smallerInt.Length);
+                               asn1.Value = smallerInt;
+                               break;
                        }
-                       asn1.Value = smallerInt;
-
                        return asn1;
                }
 
                static public ASN1 FromOid (string oid) 
                {
+                       if (oid == null)
+                               throw new ArgumentNullException ("oid");
+
                        return new ASN1 (CryptoConfig.EncodeOID (oid));
                }
 
                static public ASN1 FromUnsignedBigInteger (byte[] big) 
                {
-                       if (big [0] == 0x00) {
-                               // this first byte is added so we're sure it's an unsigned integer
-                               // however we can't feed it into RSAParameters or DSAParameters
+                       if (big == null)
+                               throw new ArgumentNullException ("big");
+
+                       // check for numbers that could be interpreted as negative (first bit)
+                       if (big [0] >= 0x80) {
+                               // in thie cas we add a new, empty, byte (position 0) so we're
+                               // sure this will always be interpreted an unsigned integer.
+                               // However we can't feed it into RSAParameters or DSAParameters
                                int length = big.Length + 1;
                                byte[] uinteger = new byte [length];
-                               Buffer.BlockCopy (big, 0, uinteger, 1, length);
+                               Buffer.BlockCopy (big, 0, uinteger, 1, length - 1);
                                big = uinteger;
                        }
                        return new ASN1 (0x02, big);
@@ -90,8 +122,11 @@ namespace Mono.Security {
 
                static public int ToInt32 (ASN1 asn1) 
                {
+                       if (asn1 == null)
+                               throw new ArgumentNullException ("asn1");
                        if (asn1.Tag != 0x02)
-                               throw new NotSupportedException ("Only integer can be converted");
+                               throw new FormatException ("Only integer can be converted");
+
                        int x = 0;
                        for (int i=0; i < asn1.Value.Length; i++)
                                x = (x << 8) + asn1.Value [i];
@@ -102,6 +137,9 @@ namespace Mono.Security {
                // an OID (IETF style). Based on DUMPASN1.C from Peter Gutmann.
                static public string ToOid (ASN1 asn1) 
                {
+                       if (asn1 == null)
+                               throw new ArgumentNullException ("asn1");
+
                        byte[] aOID = asn1.Value;
                        StringBuilder sb = new StringBuilder ();
                        // Pick apart the OID
@@ -129,16 +167,21 @@ namespace Mono.Security {
 
                static public DateTime ToDateTime (ASN1 time) 
                {
+                       if (time == null)
+                               throw new ArgumentNullException ("time");
+
                        string t = Encoding.ASCII.GetString (time.Value);
                        // to support both UTCTime and GeneralizedTime (and not so common format)
                        string mask = null;
+                       int year;
                        switch (t.Length) {
                                case 11:
-                                       mask = "yyMMddHHmmZ"; // illegal I think ... must check
+                                       // illegal format, still it's supported for compatibility
+                                       mask = "yyMMddHHmmZ";
                                        break;
                                case 13: 
                                        // RFC3280: 4.1.2.5.1  UTCTime
-                                       int year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
+                                       year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
                                        // Where YY is greater than or equal to 50, the 
                                        // year SHALL be interpreted as 19YY; and 
                                        // Where YY is less than 50, the year SHALL be 
@@ -152,8 +195,18 @@ namespace Mono.Security {
                                case 15:
                                        mask = "yyyyMMddHHmmssZ"; // GeneralizedTime
                                        break;
+                               case 17:
+                                       // another illegal format (990630000000+1000), again supported for compatibility
+                                       year = Convert.ToInt16 (t.Substring (0, 2), CultureInfo.InvariantCulture);
+                                       string century = (year >= 50) ? "19" : "20";
+                                       // ASN.1 (see ITU X.680 section 43.3) deals with offset differently than .NET
+                                       char sign = (t[12] == '+') ? '-' : '+';
+                                       t = String.Format ("{0}{1}{2}{3}{4}:{5}{6}", century, t.Substring (0, 12), sign, 
+                                               t[13], t[14], t[15], t[16]);
+                                       mask = "yyyyMMddHHmmsszzz";
+                                       break;
                        }
-                       return DateTime.ParseExact (t, mask, null);
+                       return DateTime.ParseExact (t, mask, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
                }
        }
 }