diff --git a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kem.cs b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kem.cs
index 39d1597bc6190a..7fec2cd26ea838 100644
--- a/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kem.cs
+++ b/src/libraries/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Kem.cs
@@ -34,7 +34,9 @@ private static partial int CryptoNative_EvpKemDecapsulate(
         [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemGetPalId")]
         private static partial int CryptoNative_EvpKemGetPalId(
             SafeEvpPKeyHandle kem,
-            out PalKemAlgorithmId kemId);
+            out PalKemAlgorithmId kemId,
+            out int hasSeed,
+            out int hasDecapsulationKey);
 
         [LibraryImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpKemGeneratePkey", StringMarshalling = StringMarshalling.Utf8)]
         private static partial SafeEvpPKeyHandle CryptoNative_EvpKemGeneratePkey(
@@ -103,23 +105,31 @@ internal static SafeEvpPKeyHandle EvpKemGeneratePkey(string kemName, ReadOnlySpa
             return handle;
         }
 
-        internal static PalKemAlgorithmId EvpKemGetKemIdentifier(SafeEvpPKeyHandle key)
+        internal static PalKemAlgorithmId EvpKemGetKemIdentifier(
+            SafeEvpPKeyHandle key,
+            out bool hasSeed,
+            out bool hasDecapsulationKey)
         {
             const int Success = 1;
+            const int Yes = 1;
             const int Fail = 0;
-            int result = CryptoNative_EvpKemGetPalId(key, out PalKemAlgorithmId kemId);
-
-            return result switch
-            {
-                Success => kemId,
-                Fail => throw CreateOpenSslCryptographicException(),
-                int other => throw FailThrow(other),
-            };
+            int result = CryptoNative_EvpKemGetPalId(
+                key,
+                out PalKemAlgorithmId kemId,
+                out int pKeyHasSeed,
+                out int pKeyHasDecapsulationKey);
 
-            static Exception FailThrow(int result)
+            switch (result)
             {
-                Debug.Fail($"Unexpected return value {result} from {nameof(CryptoNative_EvpKemGetPalId)}.");
-                return new CryptographicException();
+                case Success:
+                    hasSeed = pKeyHasSeed == Yes;
+                    hasDecapsulationKey = pKeyHasDecapsulationKey == Yes;
+                    return kemId;
+                case Fail:
+                    throw CreateOpenSslCryptographicException();
+                default:
+                    Debug.Fail($"Unexpected return value {result} from {nameof(CryptoNative_EvpKemGetPalId)}.");
+                    throw new CryptographicException();
             }
         }
 
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs
index a10bf42040b589..9d8e35812bfefe 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLKem.cs
@@ -778,6 +778,115 @@ public byte[] ExportSubjectPublicKeyInfo()
             return ExportSubjectPublicKeyInfoCore().Encode();
         }
 
+        /// 
+        ///   Attempts to export the current key in the PKCS#8 PrivateKeyInfo format
+        ///   into the provided buffer.
+        /// 
+        /// 
+        ///   The buffer to receive the PKCS#8 PrivateKeyInfo value.
+        /// 
+        /// 
+        ///   When this method returns, contains the number of bytes written to the  buffer.
+        ///   This parameter is treated as uninitialized.
+        /// 
+        /// 
+        ///    if  was large enough to hold the result;
+        ///   otherwise, .
+        /// 
+        /// 
+        ///   This instance has been disposed.
+        /// 
+        /// 
+        ///   An error occurred while exporting the key.
+        /// 
+        public bool TryExportPkcs8PrivateKey(Span destination, out int bytesWritten)
+        {
+            ThrowIfDisposed();
+
+            // An ML-KEM-512 "seed" export with no attributes is 86 bytes. A buffer smaller than that cannot hold a
+            // PKCS#8 encoded key. If we happen to get a buffer smaller than that, it won't export.
+            const int MinimumPossiblePkcs8MLKemKey = 86;
+
+            if (destination.Length < MinimumPossiblePkcs8MLKemKey)
+            {
+                bytesWritten = 0;
+                return false;
+            }
+
+            return TryExportPkcs8PrivateKeyCore(destination, out bytesWritten);
+        }
+
+        /// 
+        ///   Export the current key in the PKCS#8 PrivateKeyInfo format.
+        /// 
+        /// 
+        ///   A byte array containing the PKCS#8 PrivateKeyInfo representation of the this key.
+        /// 
+        /// 
+        ///   This instance has been disposed.
+        /// 
+        /// 
+        ///   An error occurred while exporting the key.
+        /// 
+        public byte[] ExportPkcs8PrivateKey()
+        {
+            ThrowIfDisposed();
+
+            // A PKCS#8 ML-KEM-1024 ExpandedKey has an ASN.1 overhead of 28 bytes, assuming no attributes.
+            // Make it an even 32 and that should give a good starting point for a buffer size.
+            // Decapsulation keys are always larger than the seed, so if we end up with a seed export it should
+            // fit in the initial buffer.
+            int size = Algorithm.DecapsulationKeySizeInBytes + 32;
+            byte[] buffer = ArrayPool.Shared.Rent(size); // Released to callers, do not use CryptoPool.
+            int written;
+
+            while (!TryExportPkcs8PrivateKeyCore(buffer, out written))
+            {
+                ClearAndReturnToPool(buffer, written);
+                size = checked(size * 2);
+                buffer = ArrayPool.Shared.Rent(size);
+            }
+
+            if (written > buffer.Length)
+            {
+                // We got a nonsense value written back. Clear the buffer, but don't put it back in the pool.
+                CryptographicOperations.ZeroMemory(buffer);
+                throw new CryptographicException();
+            }
+
+            byte[] result = buffer.AsSpan(0, written).ToArray();
+            ClearAndReturnToPool(buffer, written);
+            return result;
+
+            static void ClearAndReturnToPool(byte[] buffer, int clearSize)
+            {
+                CryptographicOperations.ZeroMemory(buffer.AsSpan(0, clearSize));
+                ArrayPool.Shared.Return(buffer);
+            }
+        }
+
+        /// 
+        ///   When overridden in a derived class, attempts to export the current key in the PKCS#8 PrivateKeyInfo format
+        ///   into the provided buffer.
+        /// 
+        /// 
+        ///   The buffer to receive the PKCS#8 PrivateKeyInfo value.
+        /// 
+        /// 
+        ///   When this method returns, contains the number of bytes written to the  buffer.
+        /// 
+        /// 
+        ///    if  was large enough to hold the result;
+        ///   otherwise, .
+        /// 
+        /// 
+        ///   This instance has been disposed.
+        /// 
+        /// 
+        ///   An error occurred while exporting the key.
+        /// 
+        protected abstract bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten);
+
         /// 
         ///  Imports an ML-KEM encapsulation key from an X.509 SubjectPublicKeyInfo structure.
         /// 
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs
index 2aa6517bc2d169..db5b67f8b7d2f8 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLKemImplementation.NotSupported.cs
@@ -75,5 +75,11 @@ protected override void ExportEncapsulationKeyCore(Span destination)
             Debug.Fail("Caller should have checked platform availability.");
             throw new PlatformNotSupportedException();
         }
+
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            Debug.Fail("Caller should have checked platform availability.");
+            throw new PlatformNotSupportedException();
+        }
     }
 }
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLKemPkcs8.cs b/src/libraries/Common/src/System/Security/Cryptography/MLKemPkcs8.cs
new file mode 100644
index 00000000000000..6a3fef4b221b90
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLKemPkcs8.cs
@@ -0,0 +1,73 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Formats.Asn1;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography
+{
+    internal static class MLKemPkcs8
+    {
+        internal static bool TryExportPkcs8PrivateKey(
+            MLKem kem,
+            bool hasSeed,
+            bool hasDecapsulationKey,
+            Span destination,
+            out int bytesWritten)
+        {
+            AlgorithmIdentifierAsn algorithmIdentifier = new()
+            {
+                Algorithm = kem.Algorithm.Oid,
+                Parameters = default(ReadOnlyMemory?),
+            };
+
+            MLKemPrivateKeyAsn privateKeyAsn = default;
+            byte[]? rented = null;
+            int written = 0;
+
+            try
+            {
+                if (hasSeed)
+                {
+                    int seedSize = kem.Algorithm.PrivateSeedSizeInBytes;
+                    rented = CryptoPool.Rent(seedSize);
+                    Memory buffer = rented.AsMemory(0, seedSize);
+                    kem.ExportPrivateSeed(buffer.Span);
+                    written = buffer.Length;
+                    privateKeyAsn.Seed = buffer;
+                }
+                else if (hasDecapsulationKey)
+                {
+                    int decapsulationKeySize = kem.Algorithm.DecapsulationKeySizeInBytes;
+                    rented = CryptoPool.Rent(decapsulationKeySize);
+                    Memory buffer = rented.AsMemory(0, decapsulationKeySize);
+                    kem.ExportDecapsulationKey(buffer.Span);
+                    written = buffer.Length;
+                    privateKeyAsn.ExpandedKey = buffer;
+                }
+                else
+                {
+                    throw new CryptographicException(SR.Cryptography_NotValidPrivateKey);
+                }
+
+                AsnWriter algorithmWriter = new(AsnEncodingRules.DER);
+                algorithmIdentifier.Encode(algorithmWriter);
+                AsnWriter privateKeyWriter = new(AsnEncodingRules.DER);
+                privateKeyAsn.Encode(privateKeyWriter);
+                AsnWriter pkcs8Writer = KeyFormatHelper.WritePkcs8(algorithmWriter, privateKeyWriter);
+
+                bool result = pkcs8Writer.TryEncode(destination, out bytesWritten);
+                privateKeyWriter.Reset();
+                pkcs8Writer.Reset();
+                return result;
+            }
+            finally
+            {
+                if (rented is not null)
+                {
+                    CryptoPool.Return(rented, written);
+                }
+            }
+        }
+    }
+}
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs
index a3709ea60b5d85..6f4007bf85b8fc 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/MLKemBaseTests.cs
@@ -459,6 +459,65 @@ public void Encapsulate_Overlaps_WhenTrimmed_Works()
             AssertExtensions.SequenceEqual(sharedSecret.Slice(0, sharedSecretWritten), decapsulated);
         }
 
+        [Fact]
+        public void TryExportPkcs8PrivateKey_Seed_Roundtrip()
+        {
+            using MLKem kem = ImportPrivateSeed(MLKemAlgorithm.MLKem512, MLKemTestData.IncrementalSeed);
+
+            AssertExportPkcs8PrivateKey(kem, pkcs8 =>
+            {
+                using MLKem imported = MLKem.ImportPkcs8PrivateKey(pkcs8);
+                Assert.Equal(MLKemAlgorithm.MLKem512, imported.Algorithm);
+                AssertExtensions.SequenceEqual(MLKemTestData.IncrementalSeed, kem.ExportPrivateSeed());
+            });
+        }
+
+        [Fact]
+        public void ExportPkcs8PrivateKey_DecapsulationKey_Roundtrip()
+        {
+            using MLKem kem = ImportDecapsulationKey(MLKemAlgorithm.MLKem512, MLKemTestData.MLKem512DecapsulationKey);
+
+            AssertExportPkcs8PrivateKey(kem, pkcs8 =>
+            {
+                using MLKem imported = MLKem.ImportPkcs8PrivateKey(pkcs8);
+                Assert.Equal(MLKemAlgorithm.MLKem512, imported.Algorithm);
+
+                Assert.Throws(() => kem.ExportPrivateSeed());
+                AssertExtensions.SequenceEqual(MLKemTestData.MLKem512DecapsulationKey, kem.ExportDecapsulationKey());
+            });
+        }
+
+        [Fact]
+        public void TryExportPkcs8PrivateKey_EncapsulationKey_Fails()
+        {
+            using MLKem kem = ImportEncapsulationKey(MLKemAlgorithm.MLKem512, MLKemTestData.MLKem512EncapsulationKey);
+            Assert.Throws(() => DoTryUntilDone(kem.TryExportPkcs8PrivateKey));
+            Assert.Throws(() => kem.ExportPkcs8PrivateKey());
+        }
+
+        private static void AssertExportPkcs8PrivateKey(MLKem kem, Action callback)
+        {
+            byte[] pkcs8 = DoTryUntilDone(kem.TryExportPkcs8PrivateKey);
+            callback(pkcs8);
+            callback(kem.ExportPkcs8PrivateKey());
+        }
+
+        private delegate bool TryExportFunc(Span destination, out int bytesWritten);
+
+        private static byte[] DoTryUntilDone(TryExportFunc func)
+        {
+            byte[] buffer = new byte[512];
+            int written;
+
+            while (!func(buffer, out written))
+            {
+                Array.Resize(ref buffer, buffer.Length * 2);
+            }
+
+            return buffer.AsSpan(0, written).ToArray();
+        }
+
+
         private static void Tamper(Span buffer)
         {
             buffer[buffer.Length - 1] ^= 0xFF;
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs
index 07c8d498e6b087..a057b40d6e37e9 100644
--- a/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs
+++ b/src/libraries/Common/tests/System/Security/Cryptography/MLKemContractTests.cs
@@ -789,6 +789,121 @@ public static void ExportSubjectPublicKeyInfo_Disposed()
             Assert.Throws(() => kem.ExportSubjectPublicKeyInfo());
         }
 
+        [Fact]
+        public static void TryExportPkcs8PrivateKey_EarlyExitForSmallBuffer()
+        {
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512);
+            byte[] destination = new byte[85];
+            AssertExtensions.FalseExpression(kem.TryExportPkcs8PrivateKey(destination, out int written));
+            Assert.Equal(0, written);
+        }
+
+        [Fact]
+        public static void TryExportPkcs8PrivateKey()
+        {
+            Random random;
+#if NET
+            random = Random.Shared;
+#else
+            random = new Random();
+#endif
+            int bufferSize = random.Next(87, 1024);
+            int writtenSize = random.Next(86, bufferSize);
+            bool success = (writtenSize & 1) == 1;
+            byte[] buffer = new byte[bufferSize];
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512)
+            {
+                OnTryExportPkcs8PrivateKeyCore = (Span destination, out int bytesWritten) =>
+                {
+                    AssertSameBuffer(buffer, destination);
+                    bytesWritten = writtenSize;
+                    return success;
+                }
+            };
+
+            AssertExtensions.TrueExpression(success == kem.TryExportPkcs8PrivateKey(buffer, out int written));
+            Assert.Equal(writtenSize, written);
+        }
+
+        [Fact]
+        public static void ExportPkcs8PrivateKey_OneExportCall()
+        {
+            int size = -1;
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512)
+            {
+                OnTryExportPkcs8PrivateKeyCore = (Span destination, out int bytesWritten) =>
+                {
+                    destination.Fill(0x88);
+                    bytesWritten = destination.Length;
+                    size = destination.Length;
+                    return true;
+                }
+            };
+
+            byte[] exported = kem.ExportPkcs8PrivateKey();
+            AssertExtensions.FilledWith(0x88, exported);
+            Assert.Equal(size, exported.Length);
+        }
+
+        [Fact]
+        public static void ExportPkcs8PrivateKey_ExpandAndRetry()
+        {
+            const int TargetSize = 4567;
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512)
+            {
+                OnTryExportPkcs8PrivateKeyCore = (Span destination, out int bytesWritten) =>
+                {
+                    if (destination.Length < TargetSize)
+                    {
+                        bytesWritten = 0;
+                        return false;
+                    }
+
+                    destination.Fill(0x88);
+                    bytesWritten = TargetSize;
+                    return true;
+                }
+            };
+
+            byte[] exported = kem.ExportPkcs8PrivateKey();
+            AssertExtensions.FilledWith(0x88, exported);
+            Assert.Equal(TargetSize, exported.Length);
+
+            // The exact number of calls that made varies depending on the behavior of how the ArrayPool
+            // behaves. Though the algorithm is to double the buffer size, the pool may rent more than requested from
+            // the doubling. However we know it should be more than one.
+            AssertExtensions.GreaterThan(kem.TryExportPkcs8PrivateKeyCoreCount, 1);
+
+            // If the implementation follows a doubling scheme exactly, the ML-KEM 512 decapsulation key size
+            // should take no more than 3 calls to reach 4567. The initial size is 1,664 bytes.
+            AssertExtensions.LessThan(kem.TryExportPkcs8PrivateKeyCoreCount, 4);
+        }
+
+        [Fact]
+        public static void ExportPkcs8PrivateKey_MisbehavingBytesWritten()
+        {
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512)
+            {
+                OnTryExportPkcs8PrivateKeyCore = (Span destination, out int bytesWritten) =>
+                {
+                    // This is not possible and indiciates a derived type is misimplemented.
+                    bytesWritten = destination.Length + 1;
+                    return true;
+                }
+            };
+
+            Assert.Throws(() => kem.ExportPkcs8PrivateKey());
+        }
+
+        [Fact]
+        public static void ExportPkcs8PrivateKey_Disposed()
+        {
+            MLKemContract kem = new(MLKemAlgorithm.MLKem512);
+            kem.Dispose();
+            Assert.Throws(() => kem.ExportPkcs8PrivateKey());
+            Assert.Throws(() => kem.TryExportPkcs8PrivateKey(new byte[512], out _));
+        }
+
         private static string MapAlgorithmOid(MLKemAlgorithm algorithm)
         {
             if (algorithm == MLKemAlgorithm.MLKem512)
@@ -860,13 +975,15 @@ internal sealed class MLKemContract : MLKem
         internal ExportKeyCoreCallback OnExportPrivateSeedCore { get; set; }
         internal ExportKeyCoreCallback OnExportEncapsulationKeyCore { get; set; }
         internal ExportKeyCoreCallback OnExportDecapsulationKeyCore { get; set; }
+        internal TryExportPkcs8PrivateKeyCoreCallback OnTryExportPkcs8PrivateKeyCore { get; set; }
         internal Action OnDispose { get; set; } = (bool disposing) => { };
 
-        private int DecapsulateCoreCount { get; set; }
-        private int EncapsulateCoreCount { get; set; }
-        private int ExportPrivateSeedCoreCount { get; set; }
-        private int ExportEncapsulationKeyCoreCount { get; set; }
-        private int ExportDecapsulationKeyCoreCount { get; set; }
+        internal int DecapsulateCoreCount { get; set; }
+        internal int EncapsulateCoreCount { get; set; }
+        internal int ExportPrivateSeedCoreCount { get; set; }
+        internal int ExportEncapsulationKeyCoreCount { get; set; }
+        internal int ExportDecapsulationKeyCoreCount { get; set; }
+        internal int TryExportPkcs8PrivateKeyCoreCount { get; set; }
 
         private bool _disposed;
 
@@ -904,6 +1021,12 @@ protected override void ExportEncapsulationKeyCore(Span destination)
             GetCallback(OnExportEncapsulationKeyCore)(destination);
         }
 
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            TryExportPkcs8PrivateKeyCoreCount++;
+            return GetCallback(OnTryExportPkcs8PrivateKeyCore)(destination, out bytesWritten);
+        }
+
         protected override void Dispose(bool disposing)
         {
             GetCallback(OnDispose)(disposing);
@@ -933,11 +1056,16 @@ private void VerifyCalledOnDispose()
             {
                 Assert.Fail($"Expected call to {nameof(ExportEncapsulationKeyCore)}.");
             }
+            if (OnTryExportPkcs8PrivateKeyCore is not null && TryExportPkcs8PrivateKeyCoreCount == 0)
+            {
+                Assert.Fail($"Expected call to {nameof(TryExportPkcs8PrivateKeyCore)}.");
+            }
         }
 
         internal delegate void DecapsulateCoreCallback(ReadOnlySpan ciphertext, Span sharedSecret);
         internal delegate void EncapsulateCoreCallback(Span ciphertext, Span sharedSecret);
         internal delegate void ExportKeyCoreCallback(Span destination);
+        internal delegate bool TryExportPkcs8PrivateKeyCoreCallback(Span destination, out int bytesWritten);
 
         private T GetCallback(T callback, [CallerMemberNameAttribute]string caller = null) where T : Delegate
         {
diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
index a2775eeac3fc6f..0930925517942f 100644
--- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
+++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs
@@ -1874,6 +1874,7 @@ public void ExportDecapsulationKey(System.Span destination) { }
         public byte[] ExportEncapsulationKey() { throw null; }
         public void ExportEncapsulationKey(System.Span destination) { }
         protected abstract void ExportEncapsulationKeyCore(System.Span destination);
+        public byte[] ExportPkcs8PrivateKey() { throw null; }
         public byte[] ExportPrivateSeed() { throw null; }
         public void ExportPrivateSeed(System.Span destination) { }
         protected abstract void ExportPrivateSeedCore(System.Span destination);
@@ -1898,6 +1899,8 @@ public void ExportPrivateSeed(System.Span destination) { }
         public static System.Security.Cryptography.MLKem ImportSubjectPublicKeyInfo(byte[] source) { throw null; }
         public static System.Security.Cryptography.MLKem ImportSubjectPublicKeyInfo(System.ReadOnlySpan source) { throw null; }
         protected void ThrowIfDisposed() { }
+        public bool TryExportPkcs8PrivateKey(System.Span destination, out int bytesWritten) { throw null; }
+        protected abstract bool TryExportPkcs8PrivateKeyCore(System.Span destination, out int bytesWritten);
         public bool TryExportSubjectPublicKeyInfo(System.Span destination, out int bytesWritten) { throw null; }
     }
     [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006")]
@@ -1936,6 +1939,7 @@ protected override void EncapsulateCore(System.Span ciphertext, System.Spa
         protected override void ExportDecapsulationKeyCore(System.Span destination) { }
         protected override void ExportEncapsulationKeyCore(System.Span destination) { }
         protected override void ExportPrivateSeedCore(System.Span destination) { }
+        protected override bool TryExportPkcs8PrivateKeyCore(System.Span destination, out int bytesWritten) { throw null; }
     }
     public sealed partial class Oid
     {
diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
index 8ffe8bc69f2488..dceaa60c3eb3ea 100644
--- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
+++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj
@@ -400,6 +400,8 @@
             Link="Common\System\Security\Cryptography\MLKem.cs" />
     
+    
     
      Interop.Crypto.EvpKemAlgs.MlKem512 is not null;
 
-        private MLKemImplementation(MLKemAlgorithm algorithm, SafeEvpPKeyHandle key) : base(algorithm)
+        private readonly bool _hasSeed;
+        private readonly bool _hasDecapsulationKey;
+
+        private MLKemImplementation(
+            MLKemAlgorithm algorithm,
+            SafeEvpPKeyHandle key,
+            bool hasSeed,
+            bool hasDecapsulationKey) : base(algorithm)
         {
             _key = key;
+            _hasSeed = hasSeed;
+            _hasDecapsulationKey = hasDecapsulationKey;
         }
 
         internal static MLKem GenerateKeyImpl(MLKemAlgorithm algorithm)
@@ -23,7 +32,7 @@ internal static MLKem GenerateKeyImpl(MLKemAlgorithm algorithm)
             Debug.Assert(IsSupported);
             string kemName = MapAlgorithmToName(algorithm);
             SafeEvpPKeyHandle key = Interop.Crypto.EvpKemGeneratePkey(kemName);
-            return new MLKemImplementation(algorithm, key);
+            return new MLKemImplementation(algorithm, key, hasSeed: true, hasDecapsulationKey: true);
         }
 
         internal static MLKem ImportPrivateSeedImpl(MLKemAlgorithm algorithm, ReadOnlySpan source)
@@ -32,7 +41,7 @@ internal static MLKem ImportPrivateSeedImpl(MLKemAlgorithm algorithm, ReadOnlySp
             Debug.Assert(source.Length == algorithm.PrivateSeedSizeInBytes);
             string kemName = MapAlgorithmToName(algorithm);
             SafeEvpPKeyHandle key = Interop.Crypto.EvpKemGeneratePkey(kemName, source);
-            return new MLKemImplementation(algorithm, key);
+            return new MLKemImplementation(algorithm, key, hasSeed: true, hasDecapsulationKey: true);
         }
 
         internal static MLKem ImportDecapsulationKeyImpl(MLKemAlgorithm algorithm, ReadOnlySpan source)
@@ -41,7 +50,7 @@ internal static MLKem ImportDecapsulationKeyImpl(MLKemAlgorithm algorithm, ReadO
             Debug.Assert(source.Length == algorithm.DecapsulationKeySizeInBytes);
             string kemName = MapAlgorithmToName(algorithm);
             SafeEvpPKeyHandle key = Interop.Crypto.EvpPKeyFromData(kemName, source, privateKey: true);
-            return new MLKemImplementation(algorithm, key);
+            return new MLKemImplementation(algorithm, key, hasSeed: false, hasDecapsulationKey: true);
         }
 
         internal static MLKem ImportEncapsulationKeyImpl(MLKemAlgorithm algorithm, ReadOnlySpan source)
@@ -50,7 +59,7 @@ internal static MLKem ImportEncapsulationKeyImpl(MLKemAlgorithm algorithm, ReadO
             Debug.Assert(source.Length == algorithm.EncapsulationKeySizeInBytes);
             string kemName = MapAlgorithmToName(algorithm);
             SafeEvpPKeyHandle key = Interop.Crypto.EvpPKeyFromData(kemName, source, privateKey: false);
-            return new MLKemImplementation(algorithm, key);
+            return new MLKemImplementation(algorithm, key, hasSeed: false, hasDecapsulationKey: false);
         }
 
         protected override void Dispose(bool disposing)
@@ -88,6 +97,16 @@ protected override void ExportEncapsulationKeyCore(Span destination)
             Interop.Crypto.EvpKemExportEncapsulationKey(_key, destination);
         }
 
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            return MLKemPkcs8.TryExportPkcs8PrivateKey(
+                this,
+                _hasSeed,
+                _hasDecapsulationKey,
+                destination,
+                out bytesWritten);
+        }
+
         private static string MapAlgorithmToName(MLKemAlgorithm algorithm)
         {
             string? name = null;
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.NotSupported.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.NotSupported.cs
index 56c8a55405a0ec..cd9fd90cd0e0f8 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.NotSupported.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.NotSupported.cs
@@ -7,14 +7,11 @@ namespace System.Security.Cryptography
 {
     public sealed partial class MLKemOpenSsl : MLKem
     {
-#pragma warning disable CA1822 // Member does not access instance data and can be marked static
-        private partial void Initialize(SafeEvpPKeyHandle upRefHandle)
-#pragma warning restore CA1822
-        {
-            throw new PlatformNotSupportedException();
-        }
-
-        private static partial MLKemAlgorithm AlgorithmFromHandle(SafeEvpPKeyHandle pkeyHandle, out SafeEvpPKeyHandle upRefHandle)
+        private static partial MLKemAlgorithm AlgorithmFromHandle(
+            SafeEvpPKeyHandle pkeyHandle,
+            out SafeEvpPKeyHandle upRefHandle,
+            out bool hasSeed,
+            out bool hasDecapsulationKey)
         {
             throw new PlatformNotSupportedException();
         }
@@ -60,5 +57,11 @@ protected override void ExportEncapsulationKeyCore(Span destination)
             Debug.Fail("Caller should have checked platform availability.");
             throw new PlatformNotSupportedException();
         }
+
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            Debug.Fail("Caller should have checked platform availability.");
+            throw new PlatformNotSupportedException();
+        }
     }
 }
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.OpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.OpenSsl.cs
index 71aee4ead2db7a..034bbb7bafe326 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.OpenSsl.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.OpenSsl.cs
@@ -2,7 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
 using System.Runtime.Versioning;
 using Microsoft.Win32.SafeHandles;
 
@@ -10,25 +9,27 @@ namespace System.Security.Cryptography
 {
     public sealed partial class MLKemOpenSsl
     {
-        private SafeEvpPKeyHandle _key;
-
-        [MemberNotNull(nameof(_key))]
-        private partial void Initialize(SafeEvpPKeyHandle upRefHandle) => _key = upRefHandle;
-
         public partial SafeEvpPKeyHandle DuplicateKeyHandle()
         {
             ThrowIfDisposed();
             return _key.DuplicateHandle();
         }
 
-        private static partial MLKemAlgorithm AlgorithmFromHandle(SafeEvpPKeyHandle pkeyHandle, out SafeEvpPKeyHandle upRefHandle)
+        private static partial MLKemAlgorithm AlgorithmFromHandle(
+            SafeEvpPKeyHandle pkeyHandle,
+            out SafeEvpPKeyHandle upRefHandle,
+            out bool hasSeed,
+            out bool hasDecapsulationKey)
         {
             ArgumentNullException.ThrowIfNull(pkeyHandle);
             upRefHandle = pkeyHandle.DuplicateHandle();
 
             try
             {
-                Interop.Crypto.PalKemAlgorithmId kemId = Interop.Crypto.EvpKemGetKemIdentifier(upRefHandle);
+                Interop.Crypto.PalKemAlgorithmId kemId = Interop.Crypto.EvpKemGetKemIdentifier(
+                    upRefHandle,
+                    out hasSeed,
+                    out hasDecapsulationKey);
 
                 switch (kemId)
                 {
@@ -89,5 +90,16 @@ protected override void ExportEncapsulationKeyCore(Span destination)
         {
             Interop.Crypto.EvpKemExportEncapsulationKey(_key, destination);
         }
+
+        /// 
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            return MLKemPkcs8.TryExportPkcs8PrivateKey(
+                this,
+                _hasSeed,
+                _hasDecapsulationKey,
+                destination,
+                out bytesWritten);
+        }
     }
 }
diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.cs
index a2c4a6e8e588a6..68121d039204b0 100644
--- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.cs
+++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/MLKemOpenSsl.cs
@@ -23,6 +23,10 @@ namespace System.Security.Cryptography
     [Experimental(Experimentals.PostQuantumCryptographyDiagId)]
     public sealed partial class MLKemOpenSsl : MLKem
     {
+        private readonly SafeEvpPKeyHandle _key;
+        private readonly bool _hasSeed;
+        private readonly bool _hasDecapsulationKey;
+
         /// 
         ///   Initializes a new instance of the  class from an existing OpenSSL key
         ///   represented as an EVP_PKEY*.
@@ -47,15 +51,19 @@ public sealed partial class MLKemOpenSsl : MLKem
         [UnsupportedOSPlatform("osx")]
         [UnsupportedOSPlatform("tvos")]
         [UnsupportedOSPlatform("windows")]
-        public MLKemOpenSsl(SafeEvpPKeyHandle pkeyHandle) : base(AlgorithmFromHandle(pkeyHandle, out SafeEvpPKeyHandle upRefHandle))
+        public MLKemOpenSsl(SafeEvpPKeyHandle pkeyHandle) : base(
+            AlgorithmFromHandle(pkeyHandle, out SafeEvpPKeyHandle upRefHandle, out bool hasSeed, out bool hasDecapsulationKey))
         {
-            Initialize(upRefHandle);
+            _key = upRefHandle;
+            _hasSeed = hasSeed;
+            _hasDecapsulationKey = hasDecapsulationKey;
         }
 
-        // This partial can go away if partial constructors are available.
-        // https://github.com/dotnet/csharplang/issues/9058
-        private partial void Initialize(SafeEvpPKeyHandle upRefHandle);
-        private static partial MLKemAlgorithm AlgorithmFromHandle(SafeEvpPKeyHandle pkeyHandle, out SafeEvpPKeyHandle upRefHandle);
+        private static partial MLKemAlgorithm AlgorithmFromHandle(
+            SafeEvpPKeyHandle pkeyHandle,
+            out SafeEvpPKeyHandle upRefHandle,
+            out bool hasSeed,
+            out bool hasDecapsulationKey);
 
         /// 
         /// Creates a duplicate handle.
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.c
index d432d055319e6e..29ec92a988fba6 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.c
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.c
@@ -30,10 +30,10 @@ int32_t CryptoNative_EvpKemAvailable(const char* algorithm)
     return 0;
 }
 
-int32_t CryptoNative_EvpKemGetPalId(const EVP_PKEY* pKey, int32_t* kemId)
+int32_t CryptoNative_EvpKemGetPalId(const EVP_PKEY* pKey, int32_t* kemId, int32_t* hasSeed, int32_t* hasDecapsulationKey)
 {
 #ifdef NEED_OPENSSL_3_0
-    assert(pKey && kemId);
+    assert(pKey && kemId && hasSeed && hasDecapsulationKey);
 
     if (API_EXISTS(EVP_PKEY_is_a))
     {
@@ -54,14 +54,20 @@ int32_t CryptoNative_EvpKemGetPalId(const EVP_PKEY* pKey, int32_t* kemId)
         else
         {
             *kemId = PalKemId_Unknown;
+            *hasSeed = 0;
+            *hasDecapsulationKey = 0;
+            return 1;
         }
 
+        *hasSeed = EvpPKeyHasKeyOctetStringParam(pKey, OSSL_PKEY_PARAM_ML_KEM_SEED);
+        *hasDecapsulationKey = EvpPKeyHasKeyOctetStringParam(pKey, OSSL_PKEY_PARAM_PRIV_KEY);
         return 1;
     }
 #endif
-
     (void)pKey;
     *kemId = PalKemId_Unknown;
+    *hasSeed = 0;
+    *hasDecapsulationKey = 0;
     return 0;
 }
 
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.h b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.h
index 269fccc14ffb92..eda8c054f67fa3 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.h
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_kem.h
@@ -14,7 +14,10 @@ typedef enum
 } PalKemId;
 
 PALEXPORT int32_t CryptoNative_EvpKemAvailable(const char* algorithm);
-PALEXPORT int32_t CryptoNative_EvpKemGetPalId(const EVP_PKEY* pKey, int32_t* kemId);
+PALEXPORT int32_t CryptoNative_EvpKemGetPalId(const EVP_PKEY* pKey,
+                                              int32_t* kemId,
+                                              int32_t* hasSeed,
+                                              int32_t* hasDecapsulationKey);
 PALEXPORT EVP_PKEY* CryptoNative_EvpKemGeneratePkey(const char* kemName, uint8_t* seed, int32_t seedLength);
 PALEXPORT int32_t CryptoNative_EvpKemExportPrivateSeed(const EVP_PKEY* pKey, uint8_t* destination, int32_t destinationLength);
 PALEXPORT int32_t CryptoNative_EvpKemExportDecapsulationKey(const EVP_PKEY* pKey, uint8_t* destination, int32_t destinationLength);
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c
index 25dc5cd42270e7..a7477ff9f72514 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.c
@@ -828,6 +828,35 @@ EVP_PKEY* CryptoNative_EvpPKeyFromData(const char* algorithmName, uint8_t* key,
     return NULL;
 }
 
+int32_t EvpPKeyHasKeyOctetStringParam(const EVP_PKEY* pKey, const char* name)
+{
+    assert(pKey);
+    assert(name);
+
+#ifdef NEED_OPENSSL_3_0
+    if (API_EXISTS(EVP_PKEY_get_octet_string_param))
+    {
+        ERR_clear_error();
+        size_t outLength = 0;
+
+        int ret = EVP_PKEY_get_octet_string_param(pKey, name, NULL, 0, &outLength);
+
+        if (ret == 1)
+        {
+            return outLength > 0 ? 1 : 0;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+#endif
+
+    (void)pKey;
+    (void)name;
+    return 0;
+}
+
 int32_t EvpPKeyGetKeyOctetStringParam(const EVP_PKEY* pKey,
                                       const char* name,
                                       uint8_t* destination,
diff --git a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.h b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.h
index a07f7fb55cd9df..d0aea8c196e3c5 100644
--- a/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.h
+++ b/src/native/libs/System.Security.Cryptography.Native/pal_evp_pkey.h
@@ -147,3 +147,8 @@ int32_t EvpPKeyGetKeyOctetStringParam(const EVP_PKEY* pKey,
     const char* name,
     uint8_t* destination,
     int32_t destinationLength);
+
+/*
+Internal function to determine if an EVP_PKEY has a given octet string property.
+*/
+int32_t EvpPKeyHasKeyOctetStringParam(const EVP_PKEY* pKey, const char* name);