diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index d67935833010f7..315ac1acdb694a 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -1172,18 +1172,18 @@ public static X509Certificate2 CreateFromPem(ReadOnlySpan certPem, ReadOnl s_DsaPublicKeyPrivateKeyLabels, static keyPem => CreateAndImport(keyPem, DSA.Create), certificate.CopyWithPrivateKey), - Oids.EcPublicKey when IsECDsa(certificate) => - ExtractKeyFromPem( - keyPem, - s_EcPublicKeyPrivateKeyLabels, - static keyPem => CreateAndImport(keyPem, ECDsa.Create), - certificate.CopyWithPrivateKey), Oids.EcPublicKey when IsECDiffieHellman(certificate) => ExtractKeyFromPem( keyPem, s_EcPublicKeyPrivateKeyLabels, static keyPem => CreateAndImport(keyPem, ECDiffieHellman.Create), certificate.CopyWithPrivateKey), + Oids.EcPublicKey when IsECDsa(certificate) => + ExtractKeyFromPem( + keyPem, + s_EcPublicKeyPrivateKeyLabels, + static keyPem => CreateAndImport(keyPem, ECDsa.Create), + certificate.CopyWithPrivateKey), Oids.MlKem512 or Oids.MlKem768 or Oids.MlKem1024 => ExtractKeyFromPem( keyPem, @@ -1259,18 +1259,18 @@ public static X509Certificate2 CreateFromEncryptedPem(ReadOnlySpan certPem password, static (keyPem, password) => CreateAndImportEncrypted(keyPem, password, DSA.Create), certificate.CopyWithPrivateKey), - Oids.EcPublicKey when IsECDsa(certificate) => - ExtractKeyFromEncryptedPem( - keyPem, - password, - static (keyPem, password) => CreateAndImportEncrypted(keyPem, password, ECDsa.Create), - certificate.CopyWithPrivateKey), Oids.EcPublicKey when IsECDiffieHellman(certificate) => ExtractKeyFromEncryptedPem( keyPem, password, static (keyPem, password) => CreateAndImportEncrypted(keyPem, password, ECDiffieHellman.Create), certificate.CopyWithPrivateKey), + Oids.EcPublicKey when IsECDsa(certificate) => + ExtractKeyFromEncryptedPem( + keyPem, + password, + static (keyPem, password) => CreateAndImportEncrypted(keyPem, password, ECDsa.Create), + certificate.CopyWithPrivateKey), Oids.MlKem512 or Oids.MlKem768 or Oids.MlKem1024 => ExtractKeyFromEncryptedPem( keyPem, diff --git a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs index bf786fc7dc2b1c..f31fd688e79381 100644 --- a/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs +++ b/src/libraries/System.Security.Cryptography/tests/X509Certificates/X509Certificate2PemTests.cs @@ -268,6 +268,54 @@ public static void CreateFromPem_ECDH_Pkcs8_Success() } } + [Fact] + public static void CreateFromPem_EC_Pkcs8_Success() + { + // ecPublicKey certificates that have no key usage restrictions should be allowed to be used as both + // an ECDsa key and an ECDiffieHellman key. + + // For purposes of creating the certificate, it doesn't matter if we use an ECDSA or ECDH key, but starting + // with ECDSA means we can make a self-signed cert. + using ECDsa key = ECDsa.Create(); + key.ImportFromPem(TestData.EcDhPkcs8Key); + CertificateRequest req = new("CN=radish", key, HashAlgorithmName.SHA256); + using X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); + string pemAggregate = $"{cert.ExportCertificatePem()}\n{TestData.EcDhPkcs8Key}"; + using X509Certificate2 reLoaded = X509Certificate2.CreateFromPem(pemAggregate, pemAggregate); + + AssertKeysMatch(TestData.EcDhPkcs8Key, reLoaded.GetECDiffieHellmanPrivateKey); + AssertKeysMatch(TestData.EcDhPkcs8Key, reLoaded.GetECDsaPrivateKey); + AssertExtensions.SequenceEqual(cert.SerialNumberBytes.Span, reLoaded.SerialNumberBytes.Span); + } + + [Fact] + public static void CreateFromEncryptedPem_EC_Pkcs8_Success() + { + // ecPublicKey certificates that have no key usage restrictions should be allowed to be used as both + // an ECDsa key and an ECDiffieHellman key. + + // For purposes of creating the certificate, it doesn't matter if we use an ECDSA or ECDH key, but starting + // with ECDSA means we can make a self-signed cert. + using ECDsa key = ECDsa.Create(); + key.ImportFromPem(TestData.EcDhPkcs8Key); + CertificateRequest req = new("CN=radish", key, HashAlgorithmName.SHA256); + using X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(1)); + + PbeParameters pbe = new(PbeEncryptionAlgorithm.Aes128Cbc, HashAlgorithmName.SHA1, 32); + const string Password = "PLACEHOLDER"; + + string encryptedPrivateKey = PemEncoding.WriteString( + "ENCRYPTED PRIVATE KEY", + key.ExportEncryptedPkcs8PrivateKey(Password, pbe)); + + string pemAggregate = $"{cert.ExportCertificatePem()}\n{encryptedPrivateKey}"; + using X509Certificate2 reLoaded = X509Certificate2.CreateFromEncryptedPem(pemAggregate, pemAggregate, Password); + + AssertKeysMatch(encryptedPrivateKey, reLoaded.GetECDiffieHellmanPrivateKey, Password); + AssertKeysMatch(encryptedPrivateKey, reLoaded.GetECDsaPrivateKey, Password); + AssertExtensions.SequenceEqual(cert.SerialNumberBytes.Span, reLoaded.SerialNumberBytes.Span); + } + [ConditionalFact(typeof(MLKem), nameof(MLKem.IsSupported))] public static void CreateFromPem_MLKem_Pkcs8_Success() {