macOS: Allow loading first X509 certificate from PEM sequence
authorKevin Jones <kevin@vcsjones.com>
Fri, 15 Feb 2019 01:36:23 +0000 (20:36 -0500)
committerJeremy Barton <jbarton@microsoft.com>
Fri, 15 Feb 2019 01:36:23 +0000 (17:36 -0800)
* macOS X509 PAL support for concatenated PEM.

* GetCertContentType should throw on macOS to be consistent with Unix and Windows.

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

src/libraries/Native/Unix/System.Security.Cryptography.Native.Apple/pal_x509.c
src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.OSX/X509Pal.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/ContentTypeTests.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/LoadFromFileTests.cs
src/libraries/System.Security.Cryptography.X509Certificates/tests/TestData.cs

index 5efa775..ecd5b6c 100644 (file)
@@ -92,7 +92,7 @@ PAL_X509ContentType AppleCryptoNative_X509GetContentType(uint8_t* pbData, int32_
     // * X509 DER
     // * PKCS7 PEM/DER
     // * PKCS12 DER (or PEM if Apple has non-standard support for that)
-    // * X509 PEM (or DER, but that already matched)
+    // * X509 PEM or PEM aggregate (or DER, but that already matched)
     //
     // If the X509 PEM check is done first SecItemImport will erroneously match
     // some PKCS#7 blobs and say they were certificates.
@@ -168,7 +168,8 @@ PAL_X509ContentType AppleCryptoNative_X509GetContentType(uint8_t* pbData, int32_
 
     if (osStatus == noErr)
     {
-        if (actualType == itemType && actualFormat == dataFormat)
+        if ((actualType == itemType && actualFormat == dataFormat) ||
+            (actualType == kSecItemTypeAggregate && actualFormat == kSecFormatPEMSequence))
         {
             CFRelease(cfData);
             return PAL_Certificate;
index 0b96506..84478a9 100644 (file)
@@ -143,12 +143,22 @@ namespace Internal.Cryptography.Pal
 
             public X509ContentType GetCertContentType(byte[] rawData)
             {
+                const int errSecUnknownFormat = -25257;
                 if (rawData == null || rawData.Length == 0)
                 {
-                    return X509ContentType.Unknown;
+                    // Throw to match Windows and Unix behavior.
+                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat);
                 }
                 
-                return Interop.AppleCrypto.X509GetContentType(rawData, rawData.Length);
+                X509ContentType contentType = Interop.AppleCrypto.X509GetContentType(rawData, rawData.Length);
+
+                if (contentType == X509ContentType.Unknown)
+                {
+                    // Throw to match Windows and Unix behavior.
+                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(errSecUnknownFormat);
+                }
+
+                return contentType;
             }
 
             public X509ContentType GetCertContentType(string fileName)
index 9546745..82ff33d 100644 (file)
@@ -28,6 +28,13 @@ namespace System.Security.Cryptography.X509Certificates.Tests
             Assert.Equal(contentType, blobType);
         }
 
+        [Fact]
+        public static void TestThrowsWhenGivenInvalidContent()
+        {
+            byte[] blob = new byte[] { 0x00, 0xFF, 0x00, 0xFF };
+            Assert.ThrowsAny<CryptographicException>(() => X509Certificate2.GetCertContentType(blob));
+        }
+
         public static IEnumerable<object[]> GetContentBlobsWithType()
         {
             return new[]
@@ -44,6 +51,7 @@ namespace System.Security.Cryptography.X509Certificates.Tests
                 new object[] { "EmptyPfx", TestData.EmptyPfx, X509ContentType.Pkcs12 },
                 new object[] { "MultiPrivatePfx", TestData.MultiPrivateKeyPfx, X509ContentType.Pkcs12 },
                 new object[] { "ChainPfx", TestData.ChainPfxBytes, X509ContentType.Pkcs12 },
+                new object[] { "ConcatenatedPem", TestData.ConcatenatedPemFile, X509ContentType.Cert }
             };
         }
     }
index ee7556c..d780633 100644 (file)
@@ -252,6 +252,16 @@ namespace System.Security.Cryptography.X509Certificates.Tests
             }
         }
 
+        [Fact]
+        public static void TestLoadConcatenatedPemFile()
+        {
+            using (X509Certificate2 c = new X509Certificate2(TestData.ConcatenatedPemFile))
+            {
+                string firstCertifiateThumbprint = "3CFD4BEECFB3F8C4DC71AD9E46EC81C2CCE71CE6";
+                Assert.Equal(firstCertifiateThumbprint, c.GetCertHashString());
+            }
+        }
+
         private static X509Certificate2 LoadCertificateFromFile()
         {
             string path = Path.Combine("TestData", "MS.cer");
index 3eb11cd..b2ed49f 100644 (file)
@@ -1701,6 +1701,66 @@ suIwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgMwADAtAhUAxMT7z8lLv7hgWmGh
             "39366d5a59590a734f65735a513d3d0a2d2d2d2d2d454e442043455254494649" +
             "434154452d2d2d2d2d0a").HexToByteArray();
 
+        internal static byte[] ConcatenatedPemFile = ByteUtils.AsciiBytes(
+            @"-----BEGIN CERTIFICATE-----
+MIIFcDCCBFigAwIBAgIQB6krbZc11OZ5l2/FnU3CpTANBgkqhkiG9w0BAQsFADBG
+MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRUwEwYDVQQLEwxTZXJ2ZXIg
+Q0EgMUIxDzANBgNVBAMTBkFtYXpvbjAeFw0xODA2MTMwMDAwMDBaFw0xOTA2MTMx
+MjAwMDBaMB4xHDAaBgNVBAMTE2VjaG8tYXBpLmFtYXpvbi5jb20wggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCFTKdiYc8w7wt59nCfHzpT9xy8mDk8spkC
+ECPzIC1Qim9T7dSRsT7tcUJIOHMPXxrlxyySSK1qB7LLdoDFuhW/CtUdD4c6t73y
+ryNHQzhPZ7fQvb8jMWf5VWPTNsS1FBAKZdTe6n0pjIAS4nypxfF+eXMaQrHiH4Ib
+iV+aZP7Men40j/YucEeii8ukmfmlQ8L351BUZmCD1FZlXD+fLb5YgbZjC+c6TB0K
+WI2oe3qK0zFKGigaFvNBoZl1A+v0V7AFWZ+tYKfCvyVBuwase5pK4770GKNfqXaX
+a/q1p5N1M3D6qa6j/U01IOtn9gJqB+PvVKBVZ/TcfBJVHtdDj+aPAgMBAAGjggKA
+MIICfDAfBgNVHSMEGDAWgBRZpGYGUqB7lZI8o5QHJ5Z0W/k90DAdBgNVHQ4EFgQU
+PmgdPNeivSJ4aVavb6e7hsUZfg0wHgYDVR0RBBcwFYITZWNoby1hcGkuYW1hem9u
+LmNvbTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
+BwMCMDsGA1UdHwQ0MDIwMKAuoCyGKmh0dHA6Ly9jcmwuc2NhMWIuYW1hem9udHJ1
+c3QuY29tL3NjYTFiLmNybDAgBgNVHSAEGTAXMAsGCWCGSAGG/WwBAjAIBgZngQwB
+AgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsGAQUFBzABhiFodHRwOi8vb2NzcC5zY2Ex
+Yi5hbWF6b250cnVzdC5jb20wNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuc2NhMWIu
+YW1hem9udHJ1c3QuY29tL3NjYTFiLmNydDAMBgNVHRMBAf8EAjAAMIIBBQYKKwYB
+BAHWeQIEAgSB9gSB8wDxAHcAu9nfvB+KcbWTlCOXqpJ7RzhXlQqrUugakJZkNo4e
+0YUAAAFj+wW/FAAABAMASDBGAiEAo5ajttPYYb/u06ZYvQ1A+wXljlscciiJQO2J
+q+aZmwQCIQDTytC4r5crkEOvnIu/SVEQF83XnnXoqa1Hc8GZubzjSgB2AId1v+dZ
+fPiMQ5lfvfNu/1aNR1Y2/0q1YMG06v9eoIMPAAABY/sFv6UAAAQDAEcwRQIgc1ob
+hd3vnx2CPLjFqNy/98zvyfh6LkZhSRJgp/kOv1oCIQDPB9E24+ifg9btt7F4ae0e
+v7x2QubFqHTV0mbbPIdRmzANBgkqhkiG9w0BAQsFAAOCAQEAnxhNKdhvKOmcY1xZ
+f0C2BBfsezzIm1MlxSThk8UXhMgNdnFAjhb6PUneR7ea/ls/KuyLhVvE5A1i/z5Y
+P3jiwq8qfCa/WQeRpZ4wxCqOWwK0hWR1iDZeL7z6+YSmOkrJru2TpOMf9DaExaVs
+jVgzC6N0FAOgosicCUojJGZKHDwgh/2UoXdLSKuvXJcOGijZ+v1/7BjBwlaYecEN
+0Gx1UkhBoJLFjUgAuEdiGLN8SyZce2geddK+ekTfAGmAvXe1ILFQid/CIoqVUO8b
+EpQT6CLezn1LfucP7FsHjUqUeknlsMi3KSvCPccM8VWwoM7PM2krWqSYveDC4dVx
+4xynhA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIESTCCAzGgAwIBAgITBn+UV4WH6Kx33rJTMlu8mYtWDTANBgkqhkiG9w0BAQsF
+ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6
+b24gUm9vdCBDQSAxMB4XDTE1MTAyMjAwMDAwMFoXDTI1MTAxOTAwMDAwMFowRjEL
+MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEVMBMGA1UECxMMU2VydmVyIENB
+IDFCMQ8wDQYDVQQDEwZBbWF6b24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
+AoIBAQDCThZn3c68asg3Wuw6MLAd5tES6BIoSMzoKcG5blPVo+sDORrMd4f2AbnZ
+cMzPa43j4wNxhplty6aUKk4T1qe9BOwKFjwK6zmxxLVYo7bHViXsPlJ6qOMpFge5
+blDP+18x+B26A0piiQOuPkfyDyeR4xQghfj66Yo19V+emU3nazfvpFA+ROz6WoVm
+B5x+F2pV8xeKNR7u6azDdU5YVX1TawprmxRC1+WsAYmz6qP+z8ArDITC2FMVy2fw
+0IjKOtEXc/VfmtTFch5+AfGYMGMqqvJ6LcXiAhqG5TI+Dr0RtM88k+8XUBCeQ8IG
+KuANaL7TiItKZYxK1MMuTJtV9IblAgMBAAGjggE7MIIBNzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUWaRmBlKge5WSPKOUByeW
+dFv5PdAwHwYDVR0jBBgwFoAUhBjMhTTsvAyUlC4IWZzHshBOCggwewYIKwYBBQUH
+AQEEbzBtMC8GCCsGAQUFBzABhiNodHRwOi8vb2NzcC5yb290Y2ExLmFtYXpvbnRy
+dXN0LmNvbTA6BggrBgEFBQcwAoYuaHR0cDovL2NydC5yb290Y2ExLmFtYXpvbnRy
+dXN0LmNvbS9yb290Y2ExLmNlcjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3Js
+LnJvb3RjYTEuYW1hem9udHJ1c3QuY29tL3Jvb3RjYTEuY3JsMBMGA1UdIAQMMAow
+CAYGZ4EMAQIBMA0GCSqGSIb3DQEBCwUAA4IBAQCFkr41u3nPo4FCHOTjY3NTOVI1
+59Gt/a6ZiqyJEi+752+a1U5y6iAwYfmXss2lJwJFqMp2PphKg5625kXg8kP2CN5t
+6G7bMQcT8C8xDZNtYTd7WPD8UZiRKAJPBXa30/AbwuZe0GaFEQ8ugcYQgSn+IGBI
+8/LwhBNTZTUVEWuCUUBVV18YtbAiPq3yXqMB48Oz+ctBWuZSkbvkNodPLamkB2g1
+upRyzQ7qDn1X8nn8N8V7YJ6y68AtkHcNSRAnpTitxBKjtKPISLMVCx7i4hncxHZS
+yLyKQXhw2W2Xs0qLeC1etA+jTGDK4UfLeC0SF7FSi8o5LL21L8IzApar2pR/
+-----END CERTIFICATE-----"
+        );
+
         internal static DSAParameters GetDSA1024Params()
         {
             DSAParameters p = new DSAParameters();