Fixing Pkcs9SigningTime behavior on the X.509 Time UtcTime range [1950;2049)
authorMaxim Lipnin <mlipnin@gmail.com>
Wed, 11 Jul 2018 13:24:45 +0000 (16:24 +0300)
committerJeremy Barton <jbarton@microsoft.com>
Wed, 11 Jul 2018 13:24:45 +0000 (06:24 -0700)
Add AsnWriter.WriteUtcTime overload to align the behavior of Pkcs9SigningTime type on the X.509 Time range [1950;2049)

Commit migrated from https://github.com/dotnet/corefx/commit/ba36ab8bcf157b8181e52a7b29a226d9202bf92d

src/libraries/Common/src/System/Security/Cryptography/AsnWriter.cs
src/libraries/System.Security.Cryptography.Pkcs/src/Internal/Cryptography/Pal/AnyOS/ManagedPal.Asn.cs
src/libraries/System.Security.Cryptography.Pkcs/tests/Pkcs9AttributeTests.cs

index cfce517..44c77a5 100644 (file)
@@ -1182,6 +1182,19 @@ namespace System.Security.Cryptography.Asn1
             WriteUtcTimeCore(tag.AsPrimitive(), value);
         }
 
+        public void WriteUtcTime(DateTimeOffset value, int minLegalYear)
+        {
+            // ensure that value is bounded within a century
+            if (minLegalYear <= value.Year && value.Year < minLegalYear + 100)
+            {
+                WriteUtcTime(value);
+            }
+            else
+            {
+                throw new ArgumentOutOfRangeException(nameof(value));
+            }
+        }
+
         // T-REC-X.680-201508 sec 47
         // T-REC-X.690-201508 sec 11.8
         private void WriteUtcTimeCore(Asn1Tag tag, DateTimeOffset value)
index bd7463c..a02c2b1 100644 (file)
@@ -81,23 +81,31 @@ namespace Internal.Cryptography.Pal.AnyOS
 
         public override byte[] EncodeUtcTime(DateTime utcTime)
         {
+            const int minLegalYear = 1950;
             // Write using DER to support the most readers.
             using (AsnWriter writer = new AsnWriter(AsnEncodingRules.DER))
             {
-                // Sending the DateTime through ToLocalTime here will cause the right normalization
-                // of DateTimeKind.Unknown.
-                //
-                // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows)
-                if (utcTime.Kind == DateTimeKind.Unspecified)
+                try 
                 {
-                    writer.WriteUtcTime(utcTime.ToLocalTime());
+                    // Sending the DateTime through ToLocalTime here will cause the right normalization
+                    // of DateTimeKind.Unknown.
+                    //
+                    // Unknown => Local (adjust) => UTC (adjust "back", add Z marker; matches Windows)
+                    if (utcTime.Kind == DateTimeKind.Unspecified)
+                    {
+                        writer.WriteUtcTime(utcTime.ToLocalTime(), minLegalYear);
+                    }
+                    else
+                    {
+                        writer.WriteUtcTime(utcTime, minLegalYear);
+                    }
+
+                    return writer.Encode();
                 }
-                else
+                catch (ArgumentException ex)
                 {
-                    writer.WriteUtcTime(utcTime);
+                    throw new CryptographicException(ex.Message, ex);
                 }
-
-                return writer.Encode();
             }
         }
 
index 8efcc12..c5fcc19 100644 (file)
@@ -27,6 +27,69 @@ namespace System.Security.Cryptography.Pkcs.Tests
             Assert.Throws<ArgumentNullException>(() => ign = new Pkcs9AttributeObject(a));
         }
 
+        [Fact]
+        public static void InputDateTimeAsX509TimeBefore1950_Utc()
+        {
+            DateTime dt = new DateTime(1949, 12, 31, 23, 59, 59, DateTimeKind.Utc);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeBefore1950_Unspecified()
+        {
+            DateTime dt = new DateTime(1949, 12, 30);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeBefore1950_Local()
+        {
+            DateTime dt = new DateTime(1949, 12, 30, 00, 00, 00, DateTimeKind.Local);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeAfter2049_Utc()
+        {
+            DateTime dt = new DateTime(2050, 01, 01, 00, 00, 00, DateTimeKind.Utc);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeAfter2049_Unspecified()
+        {
+            DateTime dt = new DateTime(2050, 01, 02);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeAfter2049_Local()
+        {
+            DateTime dt = new DateTime(2050, 01, 02, 00, 00, 00, DateTimeKind.Local);
+            Assert.ThrowsAny<CryptographicException>(() => new Pkcs9SigningTime(dt));
+        }
+
+        [Fact]
+        public static void InputDateTimeAsX509TimeBetween1950And2049_Utc()
+        {
+            var exception = Record.Exception(() => {
+                DateTime dt = new DateTime(1950, 1, 1, 00, 00, 00, DateTimeKind.Utc);
+                Pkcs9SigningTime st = new Pkcs9SigningTime(dt);
+                dt = new DateTime(2049, 12, 31, 23, 59, 59, DateTimeKind.Utc);
+                st = new Pkcs9SigningTime(dt);
+
+                dt = new DateTime(1950, 1, 2);
+                st = new Pkcs9SigningTime(dt);
+                dt = new DateTime(2049, 12, 30);
+                st = new Pkcs9SigningTime(dt); 
+
+                dt = new DateTime(1950, 1, 2, 00, 00, 00, DateTimeKind.Local);
+                st = new Pkcs9SigningTime(dt);
+                dt = new DateTime(2049, 12, 30, 23, 59, 59, DateTimeKind.Local);
+                st = new Pkcs9SigningTime(dt);
+            });
+            Assert.Null(exception);
+        }
 
         [Fact]
         public static void Pkcs9AttributeAsnEncodedDataCtorNullOidValue()
@@ -202,7 +265,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
             Assert.Equal(dateTime, cookedData.ToLocalTime());
             Assert.Equal(DateTimeKind.Utc, cookedData.Kind);
         }
-        
+
         [Fact]
         public static void SigningTimeFromCookedData_Utc()
         {