Permit trailing data when parsing an X.509 certificate by DER content (#82688)
authorKevin Jones <kevin@vcsjones.com>
Mon, 27 Feb 2023 08:52:52 +0000 (03:52 -0500)
committerGitHub <noreply@github.com>
Mon, 27 Feb 2023 08:52:52 +0000 (09:52 +0100)
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateData.ManagedDecode.cs
src/libraries/System.Security.Cryptography/tests/X509Certificates/CertTests.cs

index c35b33b..2c7b527 100644 (file)
@@ -75,9 +75,18 @@ namespace System.Security.Cryptography.X509Certificates
         try
         {
 #endif
-            RawData = rawData;
-            certificate = CertificateAsn.Decode(rawData, AsnEncodingRules.DER);
+            // Windows and Unix permit trailing data after the DER contents of the certificate, so we will allow
+            // it here, too.
+            AsnValueReader reader = new AsnValueReader(rawData, AsnEncodingRules.DER);
+            ReadOnlySpan<byte> encodedValue = reader.PeekEncodedValue();
+
+            CertificateAsn.Decode(ref reader, rawData, out certificate);
             certificate.TbsCertificate.ValidateVersion();
+
+            // Use of == on Span is intentional. If the encodedValue is identical to the rawData, then we can use
+            // raw data as-is, meaning it had no trailing data. Otherwise, use the encodedValue.
+            RawData = encodedValue == rawData ? rawData : encodedValue.ToArray();
+
             Issuer = new X500DistinguishedName(certificate.TbsCertificate.Issuer.Span);
             Subject = new X500DistinguishedName(certificate.TbsCertificate.Subject.Span);
             IssuerName = Issuer.Name;
index 1e7ffd9..b83841f 100644 (file)
@@ -571,6 +571,22 @@ namespace System.Security.Cryptography.X509Certificates.Tests
             }
         }
 
+        [Fact]
+        public static void CertificateWithTrailingDataCanBeRead()
+        {
+            byte[] certData = new byte[TestData.MsCertificate.Length + 100];
+            TestData.MsCertificate.AsSpan().CopyTo(certData);
+            certData.AsSpan(TestData.MsCertificate.Length).Fill(0xFF);
+
+            using (X509Certificate2 cert = new X509Certificate2(certData))
+            {
+                Assert.Equal("CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", cert.Subject);
+                Assert.Equal("CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US", cert.Issuer);
+
+                Assert.Equal(TestData.MsCertificate, cert.RawData);
+            }
+        }
+
         [ConditionalFact(typeof(PlatformSupport), nameof(PlatformSupport.PlatformCryptoProviderFunctional))]
         [OuterLoop("Hardware backed key generation takes several seconds.")]
         public static void CreateCertificate_MicrosoftPlatformCryptoProvider_EcdsaKey()