diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsa.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsa.cs
index ace8f116be1a51..0e23b6c4663b33 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsa.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsa.cs
@@ -89,8 +89,15 @@ protected CompositeMLDsa(CompositeMLDsaAlgorithm algorithm)
         /// 
         ///    if the algorithm is supported; otherwise, .
         /// 
-        public static bool IsAlgorithmSupported(CompositeMLDsaAlgorithm algorithm) =>
-            CompositeMLDsaImplementation.IsAlgorithmSupportedImpl(algorithm);
+        /// 
+        ///    is .
+        /// 
+        public static bool IsAlgorithmSupported(CompositeMLDsaAlgorithm algorithm)
+        {
+            ArgumentNullException.ThrowIfNull(algorithm);
+
+            return CompositeMLDsaImplementation.IsAlgorithmSupportedImpl(algorithm);
+        }
 
         /// 
         ///   Signs the specified data.
@@ -136,23 +143,32 @@ public byte[] SignData(byte[] data, byte[]? context = default)
 
             ThrowIfDisposed();
 
-            // TODO If we know exact size of signature, then we can allocate instead of renting and copying.
-            byte[] rented = CryptoPool.Rent(Algorithm.MaxSignatureSizeInBytes);
-
-            try
+            if (Algorithm.SignatureSize.IsExact)
             {
-                if (!TrySignDataCore(new ReadOnlySpan(data), new ReadOnlySpan(context), rented, out int written))
+                byte[] signature = new byte[Algorithm.MaxSignatureSizeInBytes];
+                int bytesWritten = SignDataCore(new ReadOnlySpan(data), new ReadOnlySpan(context), signature);
+
+                if (signature.Length != bytesWritten)
                 {
-                    Debug.Fail($"Signature exceeds {nameof(Algorithm.MaxSignatureSizeInBytes)} ({Algorithm.MaxSignatureSizeInBytes}).");
                     throw new CryptographicException();
                 }
 
-                return rented.AsSpan(0, written).ToArray();
+                return signature;
             }
-            finally
+
+            using (CryptoPoolLease lease = CryptoPoolLease.Rent(Algorithm.MaxSignatureSizeInBytes, skipClear: true))
             {
-                // Signature does not contain sensitive information.
-                CryptoPool.Return(rented, clearSize: 0);
+                int bytesWritten = SignDataCore(
+                    new ReadOnlySpan(data),
+                    new ReadOnlySpan(context),
+                    lease.Span);
+
+                if (!Algorithm.SignatureSize.IsValidSize(bytesWritten))
+                {
+                    throw new CryptographicException();
+                }
+
+                return lease.Span.Slice(0, bytesWritten).ToArray();
             }
         }
 
@@ -163,20 +179,18 @@ public byte[] SignData(byte[] data, byte[]? context = default)
         ///   The data to sign.
         /// 
         /// 
-        ///   The buffer to receive the signature.
+        ///   The buffer to receive the signature. Its length must be at least .
         /// 
         /// 
         ///   An optional context-specific value to limit the scope of the signature.
         ///   The default value is an empty buffer.
         /// 
-        /// 
-        ///   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, .
+        ///   The number of bytes written to the  buffer.
         /// 
+        /// 
+        ///    is less than  in length.
+        /// 
         /// 
         ///    has a  in excess of
         ///   255 bytes.
@@ -189,10 +203,7 @@ public byte[] SignData(byte[] data, byte[]? context = default)
         ///   -or-
         ///   An error occurred while signing the data.
         /// 
-        /// 
-        ///   The signature will be at most  in length.
-        /// 
-        public bool TrySignData(ReadOnlySpan data, Span destination, out int bytesWritten, ReadOnlySpan context = default)
+        public int SignData(ReadOnlySpan data, Span destination, ReadOnlySpan context = default)
         {
             if (context.Length > MaxContextLength)
             {
@@ -202,15 +213,24 @@ public bool TrySignData(ReadOnlySpan data, Span destination, out int
                     SR.Argument_SignatureContextTooLong255);
             }
 
+            if (destination.Length < Algorithm.MaxSignatureSizeInBytes)
+            {
+                throw new ArgumentException(SR.Argument_DestinationTooShort, nameof(destination));
+            }
+
             ThrowIfDisposed();
 
-            if (destination.Length < Algorithm.MLDsaAlgorithm.SignatureSizeInBytes)
+            int bytesWritten = SignDataCore(data, context, destination.Slice(0, Algorithm.MaxSignatureSizeInBytes));
+
+            // Make sure minimum size is also satisfied.
+            if (!Algorithm.SignatureSize.IsValidSize(bytesWritten))
             {
-                bytesWritten = 0;
-                return false;
+                CryptographicOperations.ZeroMemory(destination);
+
+                throw new CryptographicException();
             }
 
-            return TrySignDataCore(data, context, destination, out bytesWritten);
+            return bytesWritten;
         }
 
         /// 
@@ -224,20 +244,15 @@ public bool TrySignData(ReadOnlySpan data, Span destination, out int
         ///   The signature context.
         /// 
         /// 
-        ///   The buffer to receive the signature, whose length will be at least the ML-DSA component's signature size.
-        /// 
-        /// 
-        ///   When this method returns, contains the number of bytes written to the  buffer.
-        ///   This parameter is treated as uninitialized.
+        ///   The buffer to receive the signature, whose length will be exactly .
         /// 
         /// 
-        ///    if  was large enough to hold the result;
-        ///   otherwise, .
+        ///   The number of bytes written to the  buffer.
         /// 
         /// 
         ///   An error occurred while signing the data.
         /// 
-        protected abstract bool TrySignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination, out int bytesWritten);
+        protected abstract int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination);
 
         /// 
         ///   Verifies that the specified signature is valid for this key and the provided data.
@@ -316,7 +331,7 @@ public bool VerifyData(ReadOnlySpan data, ReadOnlySpan signature, Re
 
             ThrowIfDisposed();
 
-            if (signature.Length < Algorithm.MLDsaAlgorithm.SignatureSizeInBytes)
+            if (!Algorithm.SignatureSize.IsValidSize(signature.Length))
             {
                 return false;
             }
@@ -660,9 +675,9 @@ static void SubjectPublicKeyReader(ReadOnlyMemory key, in AlgorithmIdentif
             {
                 CompositeMLDsaAlgorithm algorithm = GetAlgorithmIdentifier(in identifier);
 
-                if (key.Length < algorithm.MLDsaAlgorithm.PublicKeySizeInBytes)
+                if (!algorithm.PublicKeySize.IsValidSize(key.Length))
                 {
-                    throw new CryptographicException(SR.Argument_PublicKeyTooShortForAlgorithm);
+                    throw new CryptographicException(SR.Argument_PublicKeyWrongSizeForAlgorithm);
                 }
 
                 dsa = CompositeMLDsaImplementation.ImportCompositeMLDsaPublicKeyImpl(algorithm, key.Span);
@@ -859,15 +874,27 @@ static void PrivateKeyReader(
                     throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
                 }
 
-                if (key.Length < algorithm.MLDsaAlgorithm.PrivateSeedSizeInBytes)
+                if (!algorithm.PrivateKeySize.IsValidSize(key.Length))
                 {
-                    throw new CryptographicException(SR.Argument_PrivateKeyTooShortForAlgorithm);
+                    throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm);
                 }
 
                 dsa = CompositeMLDsaImplementation.ImportCompositeMLDsaPrivateKeyImpl(algorithm, key);
             }
         }
 
+        /// 
+        /// 
+        ///    or  is .
+        /// 
+        public static CompositeMLDsa ImportCompositeMLDsaPublicKey(CompositeMLDsaAlgorithm algorithm, byte[] source)
+        {
+            ArgumentNullException.ThrowIfNull(algorithm);
+            ArgumentNullException.ThrowIfNull(source);
+
+            return ImportCompositeMLDsaPublicKey(algorithm, new ReadOnlySpan(source));
+        }
+
         /// 
         ///   Imports a Composite ML-DSA public key.
         /// 
@@ -895,15 +922,29 @@ static void PrivateKeyReader(
         /// 
         public static CompositeMLDsa ImportCompositeMLDsaPublicKey(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
         {
+            ArgumentNullException.ThrowIfNull(algorithm);
             ThrowIfNotSupported(algorithm);
 
-            if (source.Length < algorithm.MLDsaAlgorithm.PublicKeySizeInBytes)
+            if (!algorithm.PublicKeySize.IsValidSize(source.Length))
             {
-                throw new CryptographicException(SR.Argument_PublicKeyTooShortForAlgorithm);
+                throw new CryptographicException(SR.Argument_PublicKeyWrongSizeForAlgorithm);
             }
 
             return CompositeMLDsaImplementation.ImportCompositeMLDsaPublicKeyImpl(algorithm, source);
         }
+
+        /// 
+        /// 
+        ///    or  is .
+        /// 
+        public static CompositeMLDsa ImportCompositeMLDsaPrivateKey(CompositeMLDsaAlgorithm algorithm, byte[] source)
+        {
+            ArgumentNullException.ThrowIfNull(algorithm);
+            ArgumentNullException.ThrowIfNull(source);
+
+            return ImportCompositeMLDsaPrivateKey(algorithm, new ReadOnlySpan(source));
+        }
+
         /// 
         ///   Imports a Composite ML-DSA private key.
         /// 
@@ -931,11 +972,12 @@ public static CompositeMLDsa ImportCompositeMLDsaPublicKey(CompositeMLDsaAlgorit
         /// 
         public static CompositeMLDsa ImportCompositeMLDsaPrivateKey(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
         {
+            ArgumentNullException.ThrowIfNull(algorithm);
             ThrowIfNotSupported(algorithm);
 
-            if (source.Length < algorithm.MLDsaAlgorithm.PrivateSeedSizeInBytes)
+            if (!algorithm.PrivateKeySize.IsValidSize(source.Length))
             {
-                throw new CryptographicException(SR.Argument_PrivateKeyTooShortForAlgorithm);
+                throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm);
             }
 
             return CompositeMLDsaImplementation.ImportCompositeMLDsaPrivateKeyImpl(algorithm, source);
@@ -1346,7 +1388,7 @@ public bool TryExportPkcs8PrivateKey(Span destination, out int bytesWritte
 
             // The bound can be tightened but private key length of some traditional algorithms,
             // can vary and aren't worth the complex calculation.
-            int minimumPossiblePkcs8Key = Algorithm.MLDsaAlgorithm.PrivateSeedSizeInBytes;
+            int minimumPossiblePkcs8Key = Algorithm.PrivateKeySize.MinimumSizeInBytes;
 
             if (destination.Length < minimumPossiblePkcs8Key)
             {
@@ -1465,6 +1507,13 @@ public byte[] ExportCompositeMLDsaPublicKey()
         {
             ThrowIfDisposed();
 
+            if (Algorithm.PublicKeySize.IsExact)
+            {
+                return ExportExactSize(
+                    Algorithm.PublicKeySize.MinimumSizeInBytes,
+                    static (key, dest, out written) => key.TryExportCompositeMLDsaPublicKey(dest, out written));
+            }
+
             return ExportPublicKeyCallback(static publicKey => publicKey.ToArray());
         }
 
@@ -1492,9 +1541,28 @@ public bool TryExportCompositeMLDsaPublicKey(Span destination, out int byt
         {
             ThrowIfDisposed();
 
-            // TODO short-circuit based on known required length lower bounds
+            if (Algorithm.PublicKeySize.IsAlwaysLargerThan(destination.Length))
+            {
+                bytesWritten = 0;
+                return false;
+            }
+
+            if (TryExportCompositeMLDsaPublicKeyCore(destination, out int written))
+            {
+                if (!Algorithm.PublicKeySize.IsValidSize(written))
+                {
+                    CryptographicOperations.ZeroMemory(destination);
 
-            return TryExportCompositeMLDsaPublicKeyCore(destination, out bytesWritten);
+                    bytesWritten = 0;
+                    throw new CryptographicException();
+                }
+
+                bytesWritten = written;
+                return true;
+            }
+
+            bytesWritten = 0;
+            return false;
         }
 
         /// 
@@ -1513,12 +1581,16 @@ public byte[] ExportCompositeMLDsaPrivateKey()
         {
             ThrowIfDisposed();
 
-            // TODO The private key has a max size so add it as CompositeMLDsaAlgorithm.MaxPrivateKeySize and use it here.
-            int initalSize = 1;
+            if (Algorithm.PrivateKeySize.IsExact)
+            {
+                return ExportExactSize(
+                    Algorithm.PrivateKeySize.MinimumSizeInBytes,
+                    static (key, dest, out written) => key.TryExportCompositeMLDsaPrivateKey(dest, out written));
+            }
 
             return ExportWithCallback(
-                initalSize,
-                static (key, dest, out written) => key.TryExportCompositeMLDsaPrivateKeyCore(dest, out written),
+                Algorithm.PrivateKeySize.InitialExportBufferSizeInBytes,
+                static (key, dest, out written) => key.TryExportCompositeMLDsaPrivateKey(dest, out written),
                 static privateKey => privateKey.ToArray());
         }
 
@@ -1546,9 +1618,28 @@ public bool TryExportCompositeMLDsaPrivateKey(Span destination, out int by
         {
             ThrowIfDisposed();
 
-            // TODO short-circuit based on known required length lower bounds
+            if (Algorithm.PrivateKeySize.IsAlwaysLargerThan(destination.Length))
+            {
+                bytesWritten = 0;
+                return false;
+            }
+
+            if (TryExportCompositeMLDsaPrivateKeyCore(destination, out int written))
+            {
+                if (!Algorithm.PrivateKeySize.IsValidSize(written))
+                {
+                    CryptographicOperations.ZeroMemory(destination);
+
+                    bytesWritten = 0;
+                    throw new CryptographicException();
+                }
+
+                bytesWritten = written;
+                return true;
+            }
 
-            return TryExportCompositeMLDsaPrivateKeyCore(destination, out bytesWritten);
+            bytesWritten = 0;
+            return false;
         }
 
         /// 
@@ -1570,7 +1661,7 @@ public bool TryExportCompositeMLDsaPrivateKey(Span destination, out int by
         /// 
         ///   An error occurred while exporting the key.
         /// 
-        protected abstract bool TryExportCompositeMLDsaPublicKeyCore(ReadOnlySpan destination, out int bytesWritten);
+        protected abstract bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten);
 
         /// 
         ///   When overridden in a derived class, attempts to export the private key portion of the current key.
@@ -1591,7 +1682,7 @@ public bool TryExportCompositeMLDsaPrivateKey(Span destination, out int by
         /// 
         ///   An error occurred while exporting the key.
         /// 
-        protected abstract bool TryExportCompositeMLDsaPrivateKeyCore(ReadOnlySpan destination, out int bytesWritten);
+        protected abstract bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten);
 
         /// 
         ///   Releases all resources used by the  class.
@@ -1670,8 +1761,7 @@ private AsnWriter WritePkcs8ToAsnWriter()
 
         private TResult ExportPkcs8PrivateKeyCallback(ProcessExportedContent func)
         {
-            // TODO Pick a good estimate for the initial size of the buffer.
-            int initialSize = 1;
+            int initialSize = Algorithm.PrivateKeySize.InitialExportBufferSizeInBytes;
 
             return ExportWithCallback(
                 initialSize,
@@ -1708,13 +1798,9 @@ private AsnWriter WriteSubjectPublicKeyToAsnWriter()
 
         private TResult ExportPublicKeyCallback(ProcessExportedContent func)
         {
-            // TODO RSA is the only algo without a strict max size. The exponent can be arbitrarily large,
-            // but in practice it is always 65537. Add an internal CompositeMLDsaAlgorithm.EstimatedMaxPublicKeySizeInBytes and use that here.
-            int initialSize = 1;
-
             return ExportWithCallback(
-                initialSize,
-                static (key, dest, out written) => key.TryExportCompositeMLDsaPublicKeyCore(dest, out written),
+                Algorithm.PublicKeySize.InitialExportBufferSizeInBytes,
+                static (key, dest, out written) => key.TryExportCompositeMLDsaPublicKey(dest, out written),
                 func);
         }
 
@@ -1756,6 +1842,20 @@ private TResult ExportWithCallback(
             }
         }
 
+        private byte[] ExportExactSize(int exactSize, TryExportFunc tryExportFunc)
+        {
+            byte[] ret = new byte[exactSize];
+
+            if (!tryExportFunc(this, ret, out int written) || written != exactSize)
+            {
+                CryptographicOperations.ZeroMemory(ret);
+
+                throw new CryptographicException();
+            }
+
+            return ret;
+        }
+
         private static CompositeMLDsaAlgorithm GetAlgorithmIdentifier(ref readonly AlgorithmIdentifierAsn identifier)
         {
             CompositeMLDsaAlgorithm? algorithm = CompositeMLDsaAlgorithm.GetAlgorithmFromOid(identifier.Algorithm);
@@ -1771,7 +1871,7 @@ private static CompositeMLDsaAlgorithm GetAlgorithmIdentifier(ref readonly Algor
             return algorithm;
         }
 
-        internal static void ThrowIfNotSupported()
+        private static void ThrowIfNotSupported()
         {
             if (!IsSupported)
             {
@@ -1779,7 +1879,7 @@ internal static void ThrowIfNotSupported()
             }
         }
 
-        internal static void ThrowIfNotSupported(CompositeMLDsaAlgorithm algorithm)
+        private static void ThrowIfNotSupported(CompositeMLDsaAlgorithm algorithm)
         {
             if (!IsSupported || !IsAlgorithmSupported(algorithm))
             {
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaAlgorithm.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaAlgorithm.cs
index 60caef2db61fcb..f7b7239204b124 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaAlgorithm.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaAlgorithm.cs
@@ -2,6 +2,7 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Security.Cryptography;
 using Internal.Cryptography;
@@ -14,6 +15,8 @@ namespace System.Security.Cryptography
     [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
     public sealed class CompositeMLDsaAlgorithm : IEquatable
     {
+        internal const int RandomizerSizeInBytes = 32;
+
         /// 
         ///   Gets the name of the algorithm.
         /// 
@@ -28,22 +31,28 @@ public sealed class CompositeMLDsaAlgorithm : IEquatable
         ///   The maximum signature size in bytes for the composite algorithm.
         /// 
-        public int MaxSignatureSizeInBytes { get; }
+        public int MaxSignatureSizeInBytes => SignatureSize.MaximumSizeInBytes!.Value;
 
-        internal MLDsaAlgorithm MLDsaAlgorithm { get; }
+        internal SizeRange SignatureSize { get; }
+        internal SizeRange PrivateKeySize { get; }
+        internal SizeRange PublicKeySize { get; }
 
         internal string Oid { get; }
 
         private CompositeMLDsaAlgorithm(
             string name,
-            MLDsaAlgorithm mlDsaAlgorithm,
-            int maxTraditionalSignatureSize,
+            SizeRange signatureSize,
+            SizeRange privateKeySize,
+            SizeRange publicKeySize,
             string oid)
         {
+            Debug.Assert(signatureSize.MaximumSizeInBytes is not null);
+
             Name = name;
-            MLDsaAlgorithm = mlDsaAlgorithm;
-            MaxSignatureSizeInBytes = MLDsaAlgorithm.SignatureSizeInBytes + maxTraditionalSignatureSize;
             Oid = oid;
+            SignatureSize = signatureSize;
+            PrivateKeySize = privateKeySize;
+            PublicKeySize = publicKeySize;
         }
 
         /// 
@@ -53,7 +62,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-44 and 2048-bit RSASSA-PSS with SHA256 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa44WithRSA2048Pss { get; } =
-            new("MLDSA44-RSA2048-PSS-SHA256", MLDsaAlgorithm.MLDsa44, 2048 / 8, Oids.MLDsa44WithRSA2048PssPreHashSha256);
+            CreateRsa(
+                "MLDSA44-RSA2048-PSS-SHA256",
+                MLDsaAlgorithm.MLDsa44,
+                2048,
+                Oids.MLDsa44WithRSA2048PssPreHashSha256);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-44 and 2048-bit RSASSA-PKCS1-v1_5 with SHA256 algorithm.
@@ -62,7 +75,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-44 and 2048-bit RSASSA-PKCS1-v1_5 with SHA256 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa44WithRSA2048Pkcs15 { get; } =
-            new("MLDSA44-RSA2048-PKCS15-SHA256", MLDsaAlgorithm.MLDsa44, 2048 / 8, Oids.MLDsa44WithRSA2048Pkcs15PreHashSha256);
+            CreateRsa(
+                "MLDSA44-RSA2048-PKCS15-SHA256",
+                MLDsaAlgorithm.MLDsa44,
+                2048,
+                Oids.MLDsa44WithRSA2048Pkcs15PreHashSha256);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-44 and Ed25519 algorithm.
@@ -71,7 +88,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-44 and Ed25519 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa44WithEd25519 { get; } =
-            new("MLDSA44-Ed25519-SHA512", MLDsaAlgorithm.MLDsa44, 64, Oids.MLDsa44WithEd25519PreHashSha512);
+            CreateEdDsa(
+                "MLDSA44-Ed25519-SHA512",
+                MLDsaAlgorithm.MLDsa44,
+                32 * 8,
+                Oids.MLDsa44WithEd25519PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-44 and ECDSA P-256 with SHA256 algorithm.
@@ -80,7 +101,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-44 and ECDSA P-256 with SHA256 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa44WithECDsaP256 { get; } =
-            new("MLDSA44-ECDSA-P256-SHA256", MLDsaAlgorithm.MLDsa44, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(256), Oids.MLDsa44WithECDsaP256PreHashSha256);
+            CreateECDsa(
+                "MLDSA44-ECDSA-P256-SHA256",
+                MLDsaAlgorithm.MLDsa44,
+                256,
+                Oids.MLDsa44WithECDsaP256PreHashSha256);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and 3072-bit RSASSA-PSS with SHA512 algorithm.
@@ -89,7 +114,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and 3072-bit RSASSA-PSS with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithRSA3072Pss { get; } =
-            new("MLDSA65-RSA3072-PSS-SHA512", MLDsaAlgorithm.MLDsa65, 3072 / 8, Oids.MLDsa65WithRSA3072PssPreHashSha512);
+            CreateRsa(
+                "MLDSA65-RSA3072-PSS-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                3072,
+                Oids.MLDsa65WithRSA3072PssPreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and 3072-bit RSASSA-PKCS1-v1_5 with SHA512 algorithm.
@@ -98,7 +127,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and 3072-bit RSASSA-PKCS1-v1_5 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithRSA3072Pkcs15 { get; } =
-            new("MLDSA65-RSA3072-PKCS15-SHA512", MLDsaAlgorithm.MLDsa65, 3072 / 8, Oids.MLDsa65WithRSA3072Pkcs15PreHashSha512);
+            CreateRsa(
+                "MLDSA65-RSA3072-PKCS15-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                3072,
+                Oids.MLDsa65WithRSA3072Pkcs15PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and 4096-bit RSASSA-PSS with SHA512 algorithm.
@@ -107,7 +140,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and 4096-bit RSASSA-PSS with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithRSA4096Pss { get; } =
-            new("MLDSA65-RSA4096-PSS-SHA512", MLDsaAlgorithm.MLDsa65, 4096 / 8, Oids.MLDsa65WithRSA4096PssPreHashSha512);
+            CreateRsa(
+                "MLDSA65-RSA4096-PSS-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                4096,
+                Oids.MLDsa65WithRSA4096PssPreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and 4096-bit RSASSA-PKCS1-v1_5 with SHA512 algorithm.
@@ -116,7 +153,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and 4096-bit RSASSA-PKCS1-v1_5 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithRSA4096Pkcs15 { get; } =
-            new("MLDSA65-RSA4096-PKCS15-SHA512", MLDsaAlgorithm.MLDsa65, 4096 / 8, Oids.MLDsa65WithRSA4096Pkcs15PreHashSha512);
+            CreateRsa(
+                "MLDSA65-RSA4096-PKCS15-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                4096,
+                Oids.MLDsa65WithRSA4096Pkcs15PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA P-256 with SHA512 algorithm.
@@ -125,7 +166,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA P-256 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithECDsaP256 { get; } =
-            new("MLDSA65-ECDSA-P256-SHA512", MLDsaAlgorithm.MLDsa65, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(256), Oids.MLDsa65WithECDsaP256PreHashSha512);
+            CreateECDsa(
+                "MLDSA65-ECDSA-P256-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                256,
+                Oids.MLDsa65WithECDsaP256PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA P-384 with SHA512 algorithm.
@@ -134,7 +179,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA P-384 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithECDsaP384 { get; } =
-            new("MLDSA65-ECDSA-P384-SHA512", MLDsaAlgorithm.MLDsa65, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(384), Oids.MLDsa65WithECDsaP384PreHashSha512);
+            CreateECDsa(
+                "MLDSA65-ECDSA-P384-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                384,
+                Oids.MLDsa65WithECDsaP384PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA BrainpoolP256r1 with SHA512 algorithm.
@@ -143,7 +192,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and ECDSA BrainpoolP256r1 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithECDsaBrainpoolP256r1 { get; } =
-            new("MLDSA65-ECDSA-brainpoolP256r1-SHA512", MLDsaAlgorithm.MLDsa65, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(256), Oids.MLDsa65WithECDsaBrainpoolP256r1PreHashSha512);
+            CreateECDsa(
+                "MLDSA65-ECDSA-brainpoolP256r1-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                256,
+                Oids.MLDsa65WithECDsaBrainpoolP256r1PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-65 and Ed25519 algorithm.
@@ -152,7 +205,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-65 and Ed25519 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa65WithEd25519 { get; } =
-            new("MLDSA65-Ed25519-SHA512", MLDsaAlgorithm.MLDsa65, 64, Oids.MLDsa65WithEd25519PreHashSha512);
+            CreateEdDsa(
+                "MLDSA65-Ed25519-SHA512",
+                MLDsaAlgorithm.MLDsa65,
+                32 * 8,
+                Oids.MLDsa65WithEd25519PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA P-384 with SHA512 algorithm.
@@ -161,7 +218,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA P-384 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithECDsaP384 { get; } =
-            new("MLDSA87-ECDSA-P384-SHA512", MLDsaAlgorithm.MLDsa87, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(384), Oids.MLDsa87WithECDsaP384PreHashSha512);
+            CreateECDsa(
+                "MLDSA87-ECDSA-P384-SHA512",
+                MLDsaAlgorithm.MLDsa87,
+                384,
+                Oids.MLDsa87WithECDsaP384PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA BrainpoolP384r1 with SHA512 algorithm.
@@ -170,7 +231,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA BrainpoolP384r1 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithECDsaBrainpoolP384r1 { get; } =
-            new("MLDSA87-ECDSA-brainpoolP384r1-SHA512", MLDsaAlgorithm.MLDsa87, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(384), Oids.MLDsa87WithECDsaBrainpoolP384r1PreHashSha512);
+            CreateECDsa(
+                "MLDSA87-ECDSA-brainpoolP384r1-SHA512",
+                MLDsaAlgorithm.MLDsa87,
+                384,
+                Oids.MLDsa87WithECDsaBrainpoolP384r1PreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and Ed448 algorithm.
@@ -179,7 +244,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and Ed448 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithEd448 { get; } =
-            new("MLDSA87-Ed448-SHAKE256", MLDsaAlgorithm.MLDsa87, 114, Oids.MLDsa87WithEd448PreHashShake256_512);
+            CreateEdDsa(
+                "MLDSA87-Ed448-SHAKE256",
+                MLDsaAlgorithm.MLDsa87,
+                57 * 8,
+                Oids.MLDsa87WithEd448PreHashShake256_512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and 3072-bit RSASSA-PSS with SHA512 algorithm.
@@ -188,7 +257,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and 3072-bit RSASSA-PSS with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithRSA3072Pss { get; } =
-            new("MLDSA87-RSA3072-PSS-SHA512", MLDsaAlgorithm.MLDsa87, 3072 / 8, Oids.MLDsa87WithRSA3072PssPreHashSha512);
+            CreateRsa(
+                "MLDSA87-RSA3072-PSS-SHA512",
+                MLDsaAlgorithm.MLDsa87,
+                3072,
+                Oids.MLDsa87WithRSA3072PssPreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and 4096-bit RSASSA-PSS with SHA512 algorithm.
@@ -197,7 +270,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and 4096-bit RSASSA-PSS with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithRSA4096Pss { get; } =
-            new("MLDSA87-RSA4096-PSS-SHA512", MLDsaAlgorithm.MLDsa87, 4096 / 8, Oids.MLDsa87WithRSA4096PssPreHashSha512);
+            CreateRsa(
+                "MLDSA87-RSA4096-PSS-SHA512",
+                MLDsaAlgorithm.MLDsa87,
+                4096,
+                Oids.MLDsa87WithRSA4096PssPreHashSha512);
 
         /// 
         ///   Gets a Composite ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA P-521 with SHA512 algorithm.
@@ -206,7 +283,11 @@ private CompositeMLDsaAlgorithm(
         ///   An ML-DSA algorithm identifier for the ML-DSA-87 and ECDSA P-521 with SHA512 algorithm.
         /// 
         public static CompositeMLDsaAlgorithm MLDsa87WithECDsaP521 { get; } =
-            new("MLDSA87-ECDSA-P521-SHA512", MLDsaAlgorithm.MLDsa87, AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(521), Oids.MLDsa87WithECDsaP521PreHashSha512);
+            CreateECDsa(
+                "MLDSA87-ECDSA-P521-SHA512",
+                MLDsaAlgorithm.MLDsa87,
+                521,
+                Oids.MLDsa87WithECDsaP521PreHashSha512);
 
         /// 
         ///   Compares two  objects.
@@ -289,5 +370,141 @@ private CompositeMLDsaAlgorithm(
                 _ => null,
             };
         }
+
+        private static CompositeMLDsaAlgorithm CreateRsa(
+            string name,
+            MLDsaAlgorithm algorithm,
+            int keySizeInBits,
+            string oid)
+        {
+            Debug.Assert(keySizeInBits % 8 == 0);
+            int keySizeInBytes = keySizeInBits / 8;
+
+            SizeRange signatureSize = SizeRange.CreateExact(RandomizerSizeInBytes + algorithm.SignatureSizeInBytes + keySizeInBytes);
+
+            SizeRange privateKeySize = SizeRange.CreateUnbounded(
+                // n must be modulus length, but other parameters can vary. This is a weak lower bound.
+                minimumSize: algorithm.PrivateSeedSizeInBytes + keySizeInBytes,
+                // n and d are about modulus length, p, q, dP, dQ, qInv are about half modulus length.
+                // Estimate that version and e are usually small (65537 = 3 bytes) and 64 bytes for ASN.1 overhead.
+                initialExportBufferSize: algorithm.PrivateSeedSizeInBytes + keySizeInBytes * 2 + (keySizeInBytes / 2) * 5 + 64);
+
+            SizeRange publicKeySize = SizeRange.CreateUnbounded(
+                // n must be modulus length, but other parameters can vary. This is a weak lower bound.
+                minimumSize: algorithm.PublicKeySizeInBytes + keySizeInBytes,
+                // Estimated that e is usually small (65537 = 3 bytes) and 16 bytes for ASN.1 overhead.
+                initialExportBufferSize: algorithm.PublicKeySizeInBytes + keySizeInBytes + 16);
+
+            return new CompositeMLDsaAlgorithm(name, signatureSize, privateKeySize, publicKeySize, oid);
+        }
+
+        private static CompositeMLDsaAlgorithm CreateECDsa(
+            string name,
+            MLDsaAlgorithm algorithm,
+            int keySizeInBits,
+            string oid)
+        {
+            int keySizeInBytes = (keySizeInBits + 7) / 8;
+
+            SizeRange signatureSize = SizeRange.CreateBounded(
+                RandomizerSizeInBytes + algorithm.SignatureSizeInBytes,
+                RandomizerSizeInBytes + algorithm.SignatureSizeInBytes + AsymmetricAlgorithmHelpers.GetMaxDerSignatureSize(keySizeInBits));
+
+            SizeRange privateKeySize = SizeRange.CreateUnbounded(
+                minimumSize: algorithm.PrivateSeedSizeInBytes + 1 + keySizeInBytes,
+                // Add optional uncompressed public key and estimate 32 bytes for version, optional ECParameters and ASN.1 overhead.
+                initialExportBufferSize: algorithm.PrivateSeedSizeInBytes + 1 + keySizeInBytes + 1 + 2 * keySizeInBytes + 32);
+
+            SizeRange publicKeySize = SizeRange.CreateExact(algorithm.PublicKeySizeInBytes + 1 + 2 * keySizeInBytes);
+
+            return new CompositeMLDsaAlgorithm(name, signatureSize, privateKeySize, publicKeySize, oid);
+        }
+
+        private static CompositeMLDsaAlgorithm CreateEdDsa(
+            string name,
+            MLDsaAlgorithm algorithm,
+            int keySizeInBits,
+            string oid)
+        {
+            Debug.Assert(keySizeInBits % 8 == 0);
+            int keySizeInBytes = keySizeInBits / 8;
+
+            SizeRange signatureSize = SizeRange.CreateExact(RandomizerSizeInBytes + algorithm.SignatureSizeInBytes + 2 * keySizeInBytes);
+            SizeRange privateKeySize = SizeRange.CreateExact(algorithm.PrivateSeedSizeInBytes + keySizeInBytes);
+            SizeRange publicKeySize = SizeRange.CreateExact(algorithm.PublicKeySizeInBytes + keySizeInBytes);
+
+            return new CompositeMLDsaAlgorithm(name, signatureSize, privateKeySize, publicKeySize, oid);
+        }
+
+        internal abstract class SizeRange
+        {
+            internal abstract bool IsExact { get; }
+            internal abstract int MinimumSizeInBytes { get; }
+            internal abstract int? MaximumSizeInBytes { get; }
+            internal abstract int InitialExportBufferSizeInBytes { get; }
+
+            internal static SizeRange CreateExact(int size)
+            {
+                Debug.Assert(size >= 0);
+
+                return new ExactSize(size);
+            }
+
+            internal static SizeRange CreateBounded(int minimumSize, int maximumSize)
+            {
+                Debug.Assert(minimumSize >= 0);
+                Debug.Assert(maximumSize >= minimumSize);
+
+                return minimumSize == maximumSize ? new ExactSize(minimumSize) : new VariableSize(minimumSize, maximumSize, maximumSize);
+            }
+
+            internal static SizeRange CreateUnbounded(int minimumSize, int initialExportBufferSize)
+            {
+                Debug.Assert(minimumSize >= 0);
+                Debug.Assert(initialExportBufferSize >= minimumSize);
+
+                return new VariableSize(minimumSize, null, initialExportBufferSize);
+            }
+
+            internal bool IsValidSize(int size)
+            {
+                return size >= MinimumSizeInBytes && size <= MaximumSizeInBytes.GetValueOrDefault(int.MaxValue);
+            }
+
+            internal bool IsAlwaysLargerThan(int size)
+            {
+                return size < MinimumSizeInBytes;
+            }
+
+            private sealed class ExactSize : SizeRange
+            {
+                private readonly int _size;
+
+                internal ExactSize(int size)
+                {
+                    _size = size;
+                }
+
+                internal override bool IsExact => true;
+                internal override int MinimumSizeInBytes => _size;
+                internal override int? MaximumSizeInBytes => _size;
+                internal override int InitialExportBufferSizeInBytes => _size;
+            }
+
+            private sealed class VariableSize : SizeRange
+            {
+                internal VariableSize(int minimumSize, int? maximumSize, int initialExportBufferSize)
+                {
+                    MinimumSizeInBytes = minimumSize;
+                    MaximumSizeInBytes = maximumSize;
+                    InitialExportBufferSizeInBytes = initialExportBufferSize;
+                }
+
+                internal override bool IsExact => false;
+                internal override int MinimumSizeInBytes { get; }
+                internal override int? MaximumSizeInBytes { get; }
+                internal override int InitialExportBufferSizeInBytes { get; }
+            }
+        }
     }
 }
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.NotSupported.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.NotSupported.cs
index 7cc6ae81d277d0..7c7210100aa1f2 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.NotSupported.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.NotSupported.cs
@@ -15,20 +15,20 @@ public CompositeMLDsaImplementation(CompositeMLDsaAlgorithm algorithm)
             throw new PlatformNotSupportedException();
         }
 
-        internal static bool SupportsAny() => false;
+        internal static partial bool SupportsAny() => false;
 
-        internal static bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm) => false;
+        internal static partial bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm) => false;
 
-        internal static CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm) =>
+        internal static partial CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm) =>
             throw new PlatformNotSupportedException();
 
-        internal static CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source) =>
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source) =>
             throw new PlatformNotSupportedException();
 
-        internal static CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source) =>
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source) =>
             throw new PlatformNotSupportedException();
 
-        protected override bool TrySignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination, out int bytesWritten) =>
+        protected override int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination) =>
             throw new PlatformNotSupportedException();
 
         protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan context, ReadOnlySpan signature) =>
@@ -37,10 +37,10 @@ protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan destination, out int bytesWritten) =>
             throw new PlatformNotSupportedException();
 
-        protected override bool TryExportCompositeMLDsaPublicKeyCore(ReadOnlySpan destination, out int bytesWritten) =>
+        protected override bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten) =>
             throw new PlatformNotSupportedException();
 
-        protected override bool TryExportCompositeMLDsaPrivateKeyCore(ReadOnlySpan destination, out int bytesWritten) =>
+        protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten) =>
             throw new PlatformNotSupportedException();
     }
 }
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.Windows.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.Windows.cs
new file mode 100644
index 00000000000000..a425f7366b6212
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.Windows.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Runtime.InteropServices;
+
+namespace System.Security.Cryptography
+{
+    internal sealed partial class CompositeMLDsaImplementation : CompositeMLDsa
+    {
+        private CompositeMLDsaImplementation(CompositeMLDsaAlgorithm algorithm)
+            : base(algorithm)
+        {
+            throw new PlatformNotSupportedException();
+        }
+
+        internal static partial bool SupportsAny()
+        {
+#if !NETFRAMEWORK
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return false;
+            }
+#endif
+
+            return CompositeMLDsaManaged.SupportsAny();
+        }
+
+        internal static partial bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm)
+        {
+#if !NETFRAMEWORK
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                return false;
+            }
+#endif
+
+            return CompositeMLDsaManaged.IsAlgorithmSupportedImpl(algorithm);
+        }
+
+        internal static partial CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm) =>
+            throw new PlatformNotSupportedException();
+
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
+        {
+#if !NETFRAMEWORK
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                throw new PlatformNotSupportedException();
+            }
+#endif
+
+            return CompositeMLDsaManaged.ImportCompositeMLDsaPublicKeyImpl(algorithm, source);
+        }
+
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
+        {
+#if !NETFRAMEWORK
+            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+            {
+                throw new PlatformNotSupportedException();
+            }
+#endif
+
+            return CompositeMLDsaManaged.ImportCompositeMLDsaPrivateKeyImpl(algorithm, source);
+        }
+
+        protected override int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination) =>
+            throw new PlatformNotSupportedException();
+
+        protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan context, ReadOnlySpan signature) =>
+            throw new PlatformNotSupportedException();
+
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) =>
+            throw new PlatformNotSupportedException();
+
+        protected override bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten) =>
+            throw new PlatformNotSupportedException();
+
+        protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten) =>
+            throw new PlatformNotSupportedException();
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.cs
new file mode 100644
index 00000000000000..c565db17ef39dd
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaImplementation.cs
@@ -0,0 +1,18 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Security.Cryptography
+{
+    internal sealed partial class CompositeMLDsaImplementation : CompositeMLDsa
+    {
+        internal static partial bool SupportsAny();
+
+        internal static partial bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm);
+
+        internal static partial CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm);
+
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source);
+
+        internal static partial CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source);
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.RSA.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.RSA.cs
new file mode 100644
index 00000000000000..ad5b058229009a
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.RSA.cs
@@ -0,0 +1,316 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Formats.Asn1;
+
+namespace System.Security.Cryptography
+{
+    internal sealed partial class CompositeMLDsaManaged
+    {
+        private sealed class RsaComponent : ComponentAlgorithm
+#if DESIGNTIMEINTERFACES
+#pragma warning disable SA1001 // Commas should be spaced correctly
+            , IComponentAlgorithmFactory
+#pragma warning restore SA1001 // Commas should be spaced correctly
+#endif
+        {
+            private readonly HashAlgorithmName _hashAlgorithmName;
+            private readonly RSASignaturePadding _padding;
+
+            private RSA _rsa;
+
+            private RsaComponent(RSA rsa, HashAlgorithmName hashAlgorithmName, RSASignaturePadding padding)
+            {
+                Debug.Assert(rsa is not null);
+                Debug.Assert(padding is not null);
+
+                _rsa = rsa;
+                _hashAlgorithmName = hashAlgorithmName;
+                _padding = padding;
+            }
+
+            public static bool IsAlgorithmSupported(RsaAlgorithm _) => true;
+
+#if NETFRAMEWORK
+            // RSA-PSS requires RSACng on .NET Framework
+            private static RSACng CreateRSA() => new RSACng();
+#else
+            private static RSA CreateRSA() => RSA.Create();
+#endif
+
+            internal override int SignData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                Span destination)
+            {
+#if NET
+                return _rsa.SignData(data, destination, _hashAlgorithmName, _padding);
+#else
+                // Composite ML-DSA virtual methods only accept ROS so we need to allocate for signature
+                byte[] signature = _rsa.SignData(data, _hashAlgorithmName, _padding);
+
+                if (signature.AsSpan().TryCopyTo(destination))
+                {
+                    return signature.Length;
+                }
+
+                CryptographicOperations.ZeroMemory(destination);
+
+                throw new CryptographicException();
+#endif
+            }
+
+            internal override bool VerifyData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                ReadOnlySpan signature)
+            {
+#if NET
+                return _rsa.VerifyData(data, signature, _hashAlgorithmName, _padding);
+#else
+                // Composite ML-DSA virtual methods only accept ROS so we need to use ToArray() for signature
+                return _rsa.VerifyData(data, signature.ToArray(), _hashAlgorithmName, _padding);
+#endif
+            }
+
+            public static RsaComponent GenerateKey(RsaAlgorithm algorithm) =>
+                throw new NotImplementedException();
+
+            public static RsaComponent ImportPrivateKey(RsaAlgorithm algorithm, ReadOnlySpan source)
+            {
+                Debug.Assert(IsAlgorithmSupported(algorithm));
+
+                RSA? rsa = null;
+
+                try
+                {
+                    rsa = CreateRSA();
+
+#if NET
+                    rsa.ImportRSAPrivateKey(source, out int bytesRead);
+
+                    if (bytesRead != source.Length)
+                    {
+                        throw new CryptographicException(SR.Argument_PrivateKeyWrongSizeForAlgorithm);
+                    }
+#else
+                    ConvertRSAPrivateKeyToParameters(algorithm, source, (in parameters) =>
+                    {
+                        rsa.ImportParameters(parameters);
+                    });
+#endif
+                }
+                catch (CryptographicException)
+                {
+                    rsa?.Dispose();
+                    throw;
+                }
+
+                return new RsaComponent(rsa, algorithm.HashAlgorithmName, algorithm.Padding);
+            }
+
+            public static RsaComponent ImportPublicKey(RsaAlgorithm algorithm, ReadOnlySpan source)
+            {
+                Debug.Assert(IsAlgorithmSupported(algorithm));
+
+                RSA? rsa = null;
+
+                try
+                {
+                    rsa = CreateRSA();
+
+#if NET
+                    rsa.ImportRSAPublicKey(source, out int bytesRead);
+
+                    if (bytesRead != source.Length)
+                    {
+                        throw new CryptographicException(SR.Argument_PublicKeyWrongSizeForAlgorithm);
+                    }
+#else
+                    ConvertRSAPublicKeyToParameters(algorithm, source, (in parameters) =>
+                    {
+                        rsa.ImportParameters(parameters);
+                    });
+#endif
+                }
+                catch (CryptographicException)
+                {
+                    rsa?.Dispose();
+                    throw;
+                }
+
+                return new RsaComponent(rsa, algorithm.HashAlgorithmName, algorithm.Padding);
+            }
+
+            internal override bool TryExportPublicKey(Span destination, out int bytesWritten)
+            {
+#if NET
+                return _rsa.TryExportRSAPublicKey(destination, out bytesWritten);
+#else
+                RSAParameters parameters = _rsa.ExportParameters(includePrivateParameters: false);
+                AsnWriter writer = RSAKeyFormatHelper.WritePkcs1PublicKey(in parameters);
+                return writer.TryEncode(destination, out bytesWritten);
+#endif
+            }
+
+            internal override bool TryExportPrivateKey(Span destination, out int bytesWritten)
+            {
+#if NET
+                return _rsa.TryExportRSAPrivateKey(destination, out bytesWritten);
+#else
+                RSAParameters parameters = _rsa.ExportParameters(includePrivateParameters: true);
+
+                using (PinAndClear.Track(parameters.D))
+                using (PinAndClear.Track(parameters.P))
+                using (PinAndClear.Track(parameters.Q))
+                using (PinAndClear.Track(parameters.DP))
+                using (PinAndClear.Track(parameters.DQ))
+                using (PinAndClear.Track(parameters.InverseQ))
+                {
+                    AsnWriter? writer = null;
+
+                    try
+                    {
+                        writer = RSAKeyFormatHelper.WritePkcs1PrivateKey(in parameters);
+                        return writer.TryEncode(destination, out bytesWritten);
+                    }
+                    finally
+                    {
+                        writer?.Reset();
+                    }
+                }
+#endif
+            }
+
+            protected override void Dispose(bool disposing)
+            {
+                if (disposing)
+                {
+                    _rsa?.Dispose();
+                    _rsa = null!;
+                }
+
+                base.Dispose(disposing);
+            }
+
+#if !NET
+            private delegate void ConvertRSAKeyToParametersCallback(in RSAParameters source);
+
+            private static unsafe void ConvertRSAPublicKeyToParameters(
+                RsaAlgorithm algorithm,
+                ReadOnlySpan key,
+                ConvertRSAKeyToParametersCallback callback)
+            {
+                Debug.Assert(algorithm.KeySizeInBits % 8 == 0);
+                int modulusLength = algorithm.KeySizeInBits / 8;
+                RSAParameters parameters = default;
+
+                try
+                {
+                    AsnValueReader reader = new AsnValueReader(key, AsnEncodingRules.BER);
+                    AsnValueReader sequenceReader = reader.ReadSequence(Asn1Tag.Sequence);
+
+                    parameters.Modulus = sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes();
+
+                    if (parameters.Modulus.Length != modulusLength)
+                    {
+                        throw new CryptographicException(SR.Cryptography_NotValidPrivateKey);
+                    }
+
+                    parameters.Exponent = sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes();
+
+                    sequenceReader.ThrowIfNotEmpty();
+                    reader.ThrowIfNotEmpty();
+                }
+                catch (AsnContentException e)
+                {
+                    throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
+                }
+
+                callback(in parameters);
+            }
+
+            private static unsafe void ConvertRSAPrivateKeyToParameters(
+                RsaAlgorithm algorithm,
+                ReadOnlySpan key,
+                ConvertRSAKeyToParametersCallback callback)
+            {
+                int modulusLength = algorithm.KeySizeInBits / 8;
+                int halfModulusLength = modulusLength / 2;
+
+                RSAParameters parameters = new()
+                {
+                    D = new byte[modulusLength],
+                    P = new byte[halfModulusLength],
+                    Q = new byte[halfModulusLength],
+                    DP = new byte[halfModulusLength],
+                    DQ = new byte[halfModulusLength],
+                    InverseQ = new byte[halfModulusLength],
+                };
+
+                using (PinAndClear.Track(parameters.D))
+                using (PinAndClear.Track(parameters.P))
+                using (PinAndClear.Track(parameters.Q))
+                using (PinAndClear.Track(parameters.DP))
+                using (PinAndClear.Track(parameters.DQ))
+                using (PinAndClear.Track(parameters.InverseQ))
+                {
+                    try
+                    {
+                        AsnValueReader reader = new AsnValueReader(key, AsnEncodingRules.BER);
+                        AsnValueReader sequenceReader = reader.ReadSequence(Asn1Tag.Sequence);
+
+                        if (!sequenceReader.TryReadInt32(out int version))
+                        {
+                            sequenceReader.ThrowIfNotEmpty();
+                        }
+
+                        const int MaxSupportedVersion = 0;
+
+                        if (version > MaxSupportedVersion)
+                        {
+                            throw new CryptographicException(
+                                SR.Format(
+                                    SR.Cryptography_RSAPrivateKey_VersionTooNew,
+                                    version,
+                                    MaxSupportedVersion));
+                        }
+
+                        parameters.Modulus = sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes();
+
+                        if (parameters.Modulus.Length != modulusLength)
+                        {
+                            throw new CryptographicException(SR.Cryptography_NotValidPrivateKey);
+                        }
+
+                        parameters.Exponent = sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes();
+
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.D);
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.P);
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.Q);
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.DP);
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.DQ);
+                        sequenceReader.ReadIntegerBytes().ToUnsignedIntegerBytes(parameters.InverseQ);
+
+                        sequenceReader.ThrowIfNotEmpty();
+                        reader.ThrowIfNotEmpty();
+                    }
+                    catch (AsnContentException e)
+                    {
+                        throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding, e);
+                    }
+
+                    callback(in parameters);
+                }
+            }
+#endif
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.cs
new file mode 100644
index 00000000000000..2484a31c5f3186
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaManaged.cs
@@ -0,0 +1,692 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+using Internal.Cryptography;
+
+namespace System.Security.Cryptography
+{
+#if !SYSTEM_SECURITY_CRYPTOGRAPHY
+    // System.Security.Cryptography excludes browser at build time, but we need to rely on UnsupportedOSPlatform for Microsoft.Bcl.Cryptography.
+    [UnsupportedOSPlatform("browser")]
+#endif
+    [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
+    internal sealed partial class CompositeMLDsaManaged : CompositeMLDsa
+    {
+        private static readonly Dictionary s_algorithmMetadata = CreateAlgorithmMetadata();
+        private static readonly ConcurrentDictionary s_algorithmSupport = new();
+
+        private static ReadOnlySpan MessageRepresentativePrefix => "CompositeAlgorithmSignatures2025"u8;
+
+        private MLDsa _mldsa;
+        private ComponentAlgorithm _componentAlgorithm;
+
+        private AlgorithmMetadata AlgorithmDetails => field ??= s_algorithmMetadata[Algorithm];
+
+        private CompositeMLDsaManaged(CompositeMLDsaAlgorithm algorithm, MLDsa mldsa, ComponentAlgorithm componentAlgorithm)
+            : base(algorithm)
+        {
+            _mldsa = mldsa;
+            _componentAlgorithm = componentAlgorithm;
+        }
+
+        internal static bool SupportsAny() => MLDsaImplementation.SupportsAny();
+
+        internal static bool IsAlgorithmSupportedImpl(CompositeMLDsaAlgorithm algorithm)
+        {
+            AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];
+
+            return s_algorithmSupport.GetOrAdd(
+                algorithm,
+                alg => MLDsaImplementation.IsAlgorithmSupported(metadata.MLDsaAlgorithm) && metadata.TraditionalAlgorithm switch
+                {
+                    RsaAlgorithm rsaAlgorithm => RsaComponent.IsAlgorithmSupported(rsaAlgorithm),
+                    ECDsaAlgorithm ecdsaAlgorithm => ECDsaComponent.IsAlgorithmSupported(ecdsaAlgorithm),
+                    _ => false,
+                });
+        }
+
+        internal static CompositeMLDsa GenerateKeyImpl(CompositeMLDsaAlgorithm algorithm) =>
+            throw new PlatformNotSupportedException();
+
+        internal static CompositeMLDsa ImportCompositeMLDsaPublicKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
+        {
+            Debug.Assert(IsAlgorithmSupportedImpl(algorithm));
+
+            AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];
+
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 5.1
+            //  1.  Parse each constituent encoded public key.
+            //        The length of the mldsaKey is known based on the size of
+            //        the ML-DSA component key length specified by the Object ID.
+            //
+            //      switch ML-DSA do
+            //          case ML-DSA-44:
+            //              mldsaPK = bytes[:1312]
+            //              tradPK = bytes[1312:]
+            //          case ML-DSA-65:
+            //              mldsaPK = bytes[:1952]
+            //              tradPK = bytes[1952:]
+            //          case ML-DSA-87:
+            //              mldsaPK = bytes[:2592]
+            //              tradPK = bytes[2592:]
+            //
+            //      Note that while ML-DSA has fixed-length keys, RSA and ECDSA
+            //      may not, depending on encoding, so rigorous length - checking
+            //      of the overall composite key is not always possible.
+            //
+            //  2.  Output the component public keys
+            //
+            //      output(mldsaPK, tradPK)
+
+            ReadOnlySpan mldsaKey = source.Slice(0, metadata.MLDsaAlgorithm.PublicKeySizeInBytes);
+            ReadOnlySpan tradKey = source.Slice(metadata.MLDsaAlgorithm.PublicKeySizeInBytes);
+
+            MLDsaImplementation mldsa = MLDsaImplementation.ImportPublicKey(metadata.MLDsaAlgorithm, mldsaKey);
+            ComponentAlgorithm componentAlgorithm = metadata.TraditionalAlgorithm switch
+            {
+                RsaAlgorithm rsaAlgorithm => RsaComponent.ImportPublicKey(rsaAlgorithm, tradKey),
+                ECDsaAlgorithm ecdsaAlgorithm => ECDsaComponent.ImportPublicKey(ecdsaAlgorithm, tradKey),
+                _ => throw FailAndGetException(),
+            };
+
+            static CryptographicException FailAndGetException()
+            {
+                Debug.Fail("Only supported algorithms should reach here.");
+                return new CryptographicException();
+            }
+
+            return new CompositeMLDsaManaged(algorithm, mldsa, componentAlgorithm);
+        }
+
+        internal static CompositeMLDsa ImportCompositeMLDsaPrivateKeyImpl(CompositeMLDsaAlgorithm algorithm, ReadOnlySpan source)
+        {
+            Debug.Assert(IsAlgorithmSupportedImpl(algorithm));
+
+            AlgorithmMetadata metadata = s_algorithmMetadata[algorithm];
+
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 5.2
+            //  1.  Parse each constituent encoded key.
+            //      The length of an ML-DSA private key is always a 32 byte seed
+            //      for all parameter sets.
+            //
+            //      mldsaSeed = bytes[:32]
+            //      tradSK  = bytes[32:]
+            //
+            //      Note that while ML-DSA has fixed-length keys, RSA and ECDSA
+            //      may not, depending on encoding, so rigorous length-checking
+            //      of the overall composite key is not always possible.
+            //
+            //  2.  Output the component private keys
+            //
+            //      output (mldsaSeed, tradSK)
+
+            ReadOnlySpan mldsaKey = source.Slice(0, metadata.MLDsaAlgorithm.PrivateSeedSizeInBytes);
+            ReadOnlySpan tradKey = source.Slice(metadata.MLDsaAlgorithm.PrivateSeedSizeInBytes);
+
+            MLDsaImplementation mldsa = MLDsaImplementation.ImportSeed(metadata.MLDsaAlgorithm, mldsaKey);
+            ComponentAlgorithm componentAlgorithm = metadata.TraditionalAlgorithm switch
+            {
+                RsaAlgorithm rsaAlgorithm => RsaComponent.ImportPrivateKey(rsaAlgorithm, tradKey),
+                ECDsaAlgorithm ecdsaAlgorithm => ECDsaComponent.ImportPrivateKey(ecdsaAlgorithm, tradKey),
+                _ => throw FailAndGetException(),
+            };
+
+            static CryptographicException FailAndGetException()
+            {
+                Debug.Fail("Only supported algorithms should reach here.");
+                return new CryptographicException();
+            }
+
+            return new CompositeMLDsaManaged(algorithm, mldsa, componentAlgorithm);
+        }
+
+        protected override int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination)
+        {
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 4.2
+            //  1.  If len(ctx) > 255:
+            //      return error
+
+            Debug.Assert(context.Length <= 255, $"Caller should have checked context.Length, got {context.Length}");
+
+            //  2.  Compute the Message representative M'.
+            //      As in FIPS 204, len(ctx) is encoded as a single unsigned byte.
+            //      Randomize the message representative
+            //
+            //          r = Random(32)
+            //          M' :=  Prefix || Domain || len(ctx) || ctx || r
+            //                                              || PH( M )
+
+            Span r = stackalloc byte[CompositeMLDsaAlgorithm.RandomizerSizeInBytes];
+            RandomNumberGenerator.Fill(r);
+
+            byte[] M_prime = GetMessageRepresentative(AlgorithmDetails, context, r, data);
+
+            //  3.  Separate the private key into component keys
+            //      and re-generate the ML-DSA key from seed.
+            //
+            //          (mldsaSeed, tradSK) = DeserializePrivateKey(sk)
+            //          (_, mldsaSK) = ML-DSA.KeyGen(mldsaSeed)
+
+            /* no-op */
+
+            //  4.  Generate the two component signatures independently by calculating
+            //      the signature over M' according to their algorithm specifications.
+            //
+            //          mldsaSig = ML-DSA.Sign( mldsaSK, M', ctx=Domain )
+            //          tradSig = Trad.Sign( tradSK, M' )
+
+            //  Note that in step 4 above, both component signature processes are
+            //  invoked, and no indication is given about which one failed.This
+            //  SHOULD be done in a timing-invariant way to prevent side-channel
+            //  attackers from learning which component algorithm failed.
+
+            Span randomizer = destination.Slice(0, CompositeMLDsaAlgorithm.RandomizerSizeInBytes);
+            Span mldsaSig = destination.Slice(CompositeMLDsaAlgorithm.RandomizerSizeInBytes, AlgorithmDetails.MLDsaAlgorithm.SignatureSizeInBytes);
+            Span tradSig = destination.Slice(CompositeMLDsaAlgorithm.RandomizerSizeInBytes + AlgorithmDetails.MLDsaAlgorithm.SignatureSizeInBytes);
+
+            bool mldsaSigned = false;
+            bool tradSigned = false;
+
+            try
+            {
+                _mldsa.SignData(M_prime, mldsaSig, AlgorithmDetails.DomainSeparator);
+                mldsaSigned = true;
+            }
+            catch (CryptographicException)
+            {
+            }
+
+            int tradBytesWritten = 0;
+
+            try
+            {
+                tradBytesWritten = _componentAlgorithm.SignData(M_prime, tradSig);
+                tradSigned = true;
+            }
+            catch (CryptographicException)
+            {
+            }
+
+            //  5.  If either ML-DSA.Sign() or Trad.Sign() return an error, then this
+            //      process MUST return an error.
+            //
+            //          if NOT mldsaSig or NOT tradSig:
+            //              output "Signature generation error"
+
+            [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+            static bool Or(bool x, bool y) => x | y;
+
+            if (Or(!mldsaSigned, !tradSigned))
+            {
+                CryptographicOperations.ZeroMemory(destination);
+                throw new CryptographicException(SR.Cryptography_CompositeSignDataError);
+            }
+
+            //  6.  Output the encoded composite signature value.
+            //
+            //          s = SerializeSignatureValue(r, mldsaSig, tradSig)
+            //          return s
+
+            r.CopyTo(randomizer);
+            return randomizer.Length + mldsaSig.Length + tradBytesWritten;
+        }
+
+        protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan context, ReadOnlySpan signature)
+        {
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 4.3
+            //  1.  If len(ctx) > 255
+            //          return error
+
+            Debug.Assert(context.Length <= 255, $"Caller should have checked context.Length, got {context.Length}");
+
+            //  2.  Separate the keys and signatures
+            //
+            //          (mldsaPK, tradPK)       = DeserializePublicKey(pk)
+            //          (r, mldsaSig, tradSig)  = DeserializeSignatureValue(s)
+            //
+            //      If Error during deserialization, or if any of the component
+            //      keys or signature values are not of the correct type or
+            //      length for the given component algorithm then output
+            //      "Invalid signature" and stop.
+
+            ReadOnlySpan r = signature.Slice(0, CompositeMLDsaAlgorithm.RandomizerSizeInBytes);
+            ReadOnlySpan mldsaSig = signature.Slice(CompositeMLDsaAlgorithm.RandomizerSizeInBytes, AlgorithmDetails.MLDsaAlgorithm.SignatureSizeInBytes);
+            ReadOnlySpan tradSig = signature.Slice(CompositeMLDsaAlgorithm.RandomizerSizeInBytes + AlgorithmDetails.MLDsaAlgorithm.SignatureSizeInBytes);
+
+            //  3.  Compute a Hash of the Message.
+            //      As in FIPS 204, len(ctx) is encoded as a single unsigned byte.
+            //
+            //          M' = Prefix || Domain || len(ctx) || ctx || r
+            //                                                   || PH( M )
+
+            byte[] M_prime = GetMessageRepresentative(AlgorithmDetails, context, r, data);
+
+            //  4.  Check each component signature individually, according to its
+            //      algorithm specification.
+            //      If any fail, then the entire signature validation fails.
+            //
+            //      if not ML-DSA.Verify( mldsaPK, M', mldsaSig, ctx=Domain ) then
+            //          output "Invalid signature"
+            //
+            //      if not Trad.Verify( tradPK, M', tradSig ) then
+            //          output "Invalid signature"
+            //
+            //      if all succeeded, then
+            //          output "Valid signature"
+
+            // We don't short circuit here because we want to avoid revealing which component signature failed.
+            // This is not required in the spec, but it is a good practice to avoid timing attacks.
+
+            [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+            static bool And(bool x, bool y) => x & y;
+
+            return And(_mldsa.VerifyData(M_prime, mldsaSig, AlgorithmDetails.DomainSeparator), _componentAlgorithm.VerifyData(M_prime, tradSig));
+        }
+
+        protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) =>
+            throw new PlatformNotSupportedException();
+
+        protected override bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten)
+        {
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 5.1
+            //  1.  Combine and output the encoded public key
+            //
+            //      output mldsaPK || tradPK
+
+            _mldsa.ExportMLDsaPublicKey(destination.Slice(0, AlgorithmDetails.MLDsaAlgorithm.PublicKeySizeInBytes));
+
+            if (_componentAlgorithm.TryExportPublicKey(destination.Slice(AlgorithmDetails.MLDsaAlgorithm.PublicKeySizeInBytes), out int componentBytesWritten))
+            {
+                bytesWritten = AlgorithmDetails.MLDsaAlgorithm.PublicKeySizeInBytes + componentBytesWritten;
+                return true;
+            }
+
+            bytesWritten = 0;
+            return false;
+        }
+
+        protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten)
+        {
+            // draft-ietf-lamps-pq-composite-sigs-latest (June 20, 2025), 5.2
+            //  1.  Combine and output the encoded private key
+            //
+            //      output mldsaSeed || tradSK
+
+            try
+            {
+                _mldsa.ExportMLDsaPrivateSeed(destination.Slice(0, AlgorithmDetails.MLDsaAlgorithm.PrivateSeedSizeInBytes));
+
+                if (_componentAlgorithm.TryExportPrivateKey(destination.Slice(AlgorithmDetails.MLDsaAlgorithm.PrivateSeedSizeInBytes), out int componentBytesWritten))
+                {
+                    bytesWritten = AlgorithmDetails.MLDsaAlgorithm.PrivateSeedSizeInBytes + componentBytesWritten;
+                    return true;
+                }
+
+                bytesWritten = 0;
+                return false;
+            }
+            catch (CryptographicException)
+            {
+                CryptographicOperations.ZeroMemory(destination);
+                throw;
+            }
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                _mldsa?.Dispose();
+                _mldsa = null!;
+
+                _componentAlgorithm?.Dispose();
+                _componentAlgorithm = null!;
+            }
+
+            base.Dispose(disposing);
+        }
+
+        private static byte[] GetMessageRepresentative(
+            AlgorithmMetadata metadata,
+            ReadOnlySpan context,
+            ReadOnlySpan r,
+            ReadOnlySpan message)
+        {
+            checked
+            {
+                Debug.Assert(r.Length is CompositeMLDsaAlgorithm.RandomizerSizeInBytes);
+
+                // M' = Prefix || Domain || len(ctx) || ctx || r || PH( M )
+
+                using (IncrementalHash hash = IncrementalHash.CreateHash(metadata.HashAlgorithmName))
+                {
+#if NET
+                    int hashLength = hash.HashLengthInBytes;
+#else
+                    int hashLength = hash.GetHashLengthInBytes();
+#endif
+
+                    int length =
+                        MessageRepresentativePrefix.Length +    // Prefix
+                        metadata.DomainSeparator.Length +       // Domain
+                        1 +                                     // len(ctx)
+                        context.Length +                        // ctx
+                        r.Length +                              // r
+                        hashLength;                             // PH( M )
+
+                    // The representative message will often be < 256 bytes so we can stackalloc with a callback.
+                    // That gets a little messy on .NET Framework where by-ref generics aren't supported, so we just allocate.
+                    byte[] M_prime = new byte[length];
+
+                    int offset = 0;
+
+                    // Prefix
+                    MessageRepresentativePrefix.CopyTo(M_prime.AsSpan(offset, MessageRepresentativePrefix.Length));
+                    offset += MessageRepresentativePrefix.Length;
+
+                    // Domain
+                    metadata.DomainSeparator.AsSpan().CopyTo(M_prime.AsSpan(offset, metadata.DomainSeparator.Length));
+                    offset += metadata.DomainSeparator.Length;
+
+                    // len(ctx)
+                    M_prime[offset] = (byte)context.Length;
+                    offset++;
+
+                    // ctx
+                    context.CopyTo(M_prime.AsSpan(offset, context.Length));
+                    offset += context.Length;
+
+                    // r
+                    r.CopyTo(M_prime.AsSpan(offset, r.Length));
+                    offset += r.Length;
+
+                    // PH( M )
+                    hash.AppendData(message);
+#if NET
+                    hash.GetHashAndReset(M_prime.AsSpan(offset, hashLength));
+#else
+                    byte[] hashBytes = hash.GetHashAndReset();
+                    hashBytes.CopyTo(M_prime.AsSpan(offset, hashLength));
+#endif
+                    offset += hashLength;
+
+                    Debug.Assert(offset == M_prime.Length);
+
+                    return M_prime;
+                }
+            }
+        }
+
+#if DESIGNTIMEINTERFACES
+        private interface IComponentAlgorithmFactory
+            where TComponentAlgorithm : ComponentAlgorithm, IComponentAlgorithmFactory
+        {
+            internal static abstract bool IsAlgorithmSupported(TAlgorithmDescriptor algorithm);
+            internal static abstract TComponentAlgorithm GenerateKey(TAlgorithmDescriptor algorithm);
+            internal static abstract TComponentAlgorithm ImportPrivateKey(TAlgorithmDescriptor algorithm, ReadOnlySpan source);
+            internal static abstract TComponentAlgorithm ImportPublicKey(TAlgorithmDescriptor algorithm, ReadOnlySpan source);
+        }
+#endif
+
+        private abstract class ComponentAlgorithm : IDisposable
+        {
+            private bool _disposed;
+
+            internal abstract bool TryExportPublicKey(Span destination, out int bytesWritten);
+            internal abstract bool TryExportPrivateKey(Span destination, out int bytesWritten);
+
+            internal abstract int SignData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                Span destination);
+
+            internal abstract bool VerifyData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                ReadOnlySpan signature);
+
+            public void Dispose()
+            {
+                if (!_disposed)
+                {
+                    _disposed = true;
+                    Dispose(true);
+                    GC.SuppressFinalize(this);
+                }
+            }
+
+            protected virtual void Dispose(bool disposing)
+            {
+            }
+        }
+
+        private sealed class ECDsaComponent : ComponentAlgorithm
+#if DESIGNTIMEINTERFACES
+#pragma warning disable SA1001 // Commas should be spaced correctly
+            , IComponentAlgorithmFactory
+#pragma warning restore SA1001 // Commas should be spaced correctly
+#endif
+        {
+            public static bool IsAlgorithmSupported(ECDsaAlgorithm _) => false;
+            public static ECDsaComponent GenerateKey(ECDsaAlgorithm algorithm) => throw new NotImplementedException();
+            public static ECDsaComponent ImportPrivateKey(ECDsaAlgorithm algorithm, ReadOnlySpan source) => throw new NotImplementedException();
+            public static ECDsaComponent ImportPublicKey(ECDsaAlgorithm algorithm, ReadOnlySpan source) => throw new NotImplementedException();
+
+            internal override bool TryExportPrivateKey(Span destination, out int bytesWritten) => throw new NotImplementedException();
+            internal override bool TryExportPublicKey(Span destination, out int bytesWritten) => throw new NotImplementedException();
+
+            internal override bool VerifyData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                ReadOnlySpan signature) => throw new NotImplementedException();
+
+            internal override int SignData(
+#if NET
+                ReadOnlySpan data,
+#else
+                byte[] data,
+#endif
+                Span destination) => throw new NotImplementedException();
+        }
+
+        private static Dictionary CreateAlgorithmMetadata()
+        {
+            const int count = 18;
+
+            Dictionary algorithmMetadata = new(count)
+            {
+                {
+                    CompositeMLDsaAlgorithm.MLDsa44WithRSA2048Pss,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa44,
+                        new RsaAlgorithm(2048, HashAlgorithmName.SHA256, RSASignaturePadding.Pss),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x00],
+                        HashAlgorithmName.SHA256)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa44WithRSA2048Pkcs15,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa44,
+                        new RsaAlgorithm(2048, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x01],
+                        HashAlgorithmName.SHA256)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa44WithEd25519,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa44,
+                        new EdDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x02],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa44WithECDsaP256,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa44,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x03],
+                        HashAlgorithmName.SHA256)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithRSA3072Pss,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new RsaAlgorithm(3072, HashAlgorithmName.SHA512, RSASignaturePadding.Pss),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x04],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithRSA3072Pkcs15,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new RsaAlgorithm(3072, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x05],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pss,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new RsaAlgorithm(4096, HashAlgorithmName.SHA512, RSASignaturePadding.Pss),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x06],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithRSA4096Pkcs15,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new RsaAlgorithm(4096, HashAlgorithmName.SHA384, RSASignaturePadding.Pkcs1),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x07],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithECDsaP256,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x08],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithECDsaP384,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x09],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithECDsaBrainpoolP256r1,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0A],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa65WithEd25519,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa65,
+                        new EdDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0B],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithECDsaP384,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0C],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithECDsaBrainpoolP384r1,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0D],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithEd448,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new EdDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0E],
+                        new HashAlgorithmName("SHAKE256"))
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithRSA3072Pss,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new RsaAlgorithm(3072, HashAlgorithmName.SHA512, RSASignaturePadding.Pss),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x0F],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithRSA4096Pss,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new RsaAlgorithm(4096, HashAlgorithmName.SHA512, RSASignaturePadding.Pss),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x10],
+                        HashAlgorithmName.SHA512)
+                },
+                {
+                    CompositeMLDsaAlgorithm.MLDsa87WithECDsaP521,
+                    new AlgorithmMetadata(
+                        MLDsaAlgorithm.MLDsa87,
+                        new ECDsaAlgorithm(),
+                        [0x06, 0x0B, 0x60, 0x86, 0x48, 0x01, 0x86, 0xFA, 0x6B, 0x50, 0x09, 0x01, 0x11],
+                        HashAlgorithmName.SHA512)
+                }
+            };
+
+            Debug.Assert(count == algorithmMetadata.Count);
+
+            return algorithmMetadata;
+        }
+
+        private sealed class AlgorithmMetadata(
+            MLDsaAlgorithm mldsaAlgorithm,
+            object traditionalAlgorithm,
+            byte[] domainSeparator,
+            HashAlgorithmName hashAlgorithmName)
+        {
+            internal MLDsaAlgorithm MLDsaAlgorithm { get; } = mldsaAlgorithm;
+            internal object TraditionalAlgorithm { get; } = traditionalAlgorithm;
+            internal byte[] DomainSeparator { get; } = domainSeparator;
+            internal HashAlgorithmName HashAlgorithmName { get; } = hashAlgorithmName;
+        }
+
+        private sealed class RsaAlgorithm(int keySizeInBits, HashAlgorithmName hashAlgorithmName, RSASignaturePadding padding)
+        {
+            internal int KeySizeInBits { get; } = keySizeInBits;
+            internal HashAlgorithmName HashAlgorithmName { get; } = hashAlgorithmName;
+            internal RSASignaturePadding Padding { get; } = padding;
+        }
+
+        private sealed class ECDsaAlgorithm
+        {
+        }
+
+        private sealed class EdDsaAlgorithm
+        {
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs b/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs
index 40d07a69e24999..a761d7264225c9 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/KeyBlobHelpers.cs
@@ -7,39 +7,10 @@
 
 namespace System.Security.Cryptography
 {
-    internal static class KeyBlobHelpers
+    internal static partial class KeyBlobHelpers
     {
-        internal static byte[] ToUnsignedIntegerBytes(this ReadOnlyMemory memory, int length)
+        internal static byte[] ToUnsignedIntegerBytes(this ReadOnlySpan span)
         {
-            if (memory.Length == length)
-            {
-                return memory.ToArray();
-            }
-
-            ReadOnlySpan span = memory.Span;
-
-            if (memory.Length == length + 1)
-            {
-                if (span[0] == 0)
-                {
-                    return span.Slice(1).ToArray();
-                }
-            }
-
-            if (span.Length > length)
-            {
-                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
-            }
-
-            byte[] target = new byte[length];
-            span.CopyTo(target.AsSpan(length - span.Length));
-            return target;
-        }
-
-        internal static byte[] ToUnsignedIntegerBytes(this ReadOnlyMemory memory)
-        {
-            ReadOnlySpan span = memory.Span;
-
             if (span.Length > 1 && span[0] == 0)
             {
                 return span.Slice(1).ToArray();
@@ -48,22 +19,32 @@ internal static byte[] ToUnsignedIntegerBytes(this ReadOnlyMemory memory)
             return span.ToArray();
         }
 
-        internal static byte[] ExportKeyParameter(this BigInteger value, int length)
+        internal static void ToUnsignedIntegerBytes(this ReadOnlySpan span, Span destination)
         {
-            byte[] target = new byte[length];
+            int length = destination.Length;
 
-            if (value.TryWriteBytes(target, out int bytesWritten, isUnsigned: true, isBigEndian: true))
+            if (span.Length == length)
             {
-                if (bytesWritten < length)
+                span.CopyTo(destination);
+                return;
+            }
+
+            if (span.Length == length + 1)
+            {
+                if (span[0] == 0)
                 {
-                    Buffer.BlockCopy(target, 0, target, length - bytesWritten, bytesWritten);
-                    target.AsSpan(0, length - bytesWritten).Clear();
+                    span.Slice(1).CopyTo(destination);
+                    return;
                 }
+            }
 
-                return target;
+            if (span.Length > length)
+            {
+                throw new CryptographicException(SR.Cryptography_Der_Invalid_Encoding);
             }
 
-            throw new CryptographicException(SR.Cryptography_NotValidPublicOrPrivateKey);
+            destination.Slice(0, destination.Length - span.Length).Clear();
+            span.CopyTo(destination.Slice(length - span.Length));
         }
 
         internal static void WriteKeyParameterInteger(this AsnWriter writer, ReadOnlySpan integer)
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.NotSupported.cs b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.NotSupported.cs
index f42e3221690baa..c79071595dd271 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.NotSupported.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.NotSupported.cs
@@ -16,6 +16,8 @@ private MLDsaImplementation(MLDsaAlgorithm algorithm)
 
         internal static partial bool SupportsAny() => false;
 
+        internal static partial bool IsAlgorithmSupported(MLDsaAlgorithm algorithm) => false;
+
         // The instance override methods are unreachable, as the constructor will always throw.
         protected override void SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination) =>
             throw new PlatformNotSupportedException();
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
index 5b62f4a6e4048e..5999f2fd736379 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.Windows.cs
@@ -34,6 +34,9 @@ private MLDsaImplementation(
         [MemberNotNullWhen(true, nameof(s_algHandle))]
         internal static partial bool SupportsAny() => s_algHandle is not null;
 
+        [MemberNotNullWhen(true, nameof(s_algHandle))]
+        internal static partial bool IsAlgorithmSupported(MLDsaAlgorithm algorithm) => SupportsAny();
+
         protected override void SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination)
         {
             if (!_hasSecretKey)
diff --git a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
index b3ca249c345ea9..63c4ee70dafad2 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/MLDsaImplementation.cs
@@ -10,6 +10,7 @@ namespace System.Security.Cryptography
     internal sealed partial class MLDsaImplementation : MLDsa
     {
         internal static partial bool SupportsAny();
+        internal static partial bool IsAlgorithmSupported(MLDsaAlgorithm algorithm);
 
         internal static partial MLDsaImplementation GenerateKeyImpl(MLDsaAlgorithm algorithm);
         internal static partial MLDsaImplementation ImportPublicKey(MLDsaAlgorithm algorithm, ReadOnlySpan source);
diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs
new file mode 100644
index 00000000000000..d8a6783f15e572
--- /dev/null
+++ b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.Pkcs1.cs
@@ -0,0 +1,65 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.Formats.Asn1;
+using System.Security.Cryptography.Asn1;
+
+namespace System.Security.Cryptography
+{
+    internal static partial class RSAKeyFormatHelper
+    {
+        internal static AsnWriter WritePkcs1PublicKey(in RSAParameters rsaParameters)
+        {
+            if (rsaParameters.Modulus == null || rsaParameters.Exponent == null)
+            {
+                throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
+            }
+
+            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
+            writer.PushSequence();
+            writer.WriteKeyParameterInteger(rsaParameters.Modulus);
+            writer.WriteKeyParameterInteger(rsaParameters.Exponent);
+            writer.PopSequence();
+
+            return writer;
+        }
+
+        internal static AsnWriter WritePkcs1PrivateKey(in RSAParameters rsaParameters)
+        {
+            if (rsaParameters.Modulus == null || rsaParameters.Exponent == null)
+            {
+                throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
+            }
+
+            if (rsaParameters.D == null ||
+                rsaParameters.P == null ||
+                rsaParameters.Q == null ||
+                rsaParameters.DP == null ||
+                rsaParameters.DQ == null ||
+                rsaParameters.InverseQ == null)
+            {
+                throw new CryptographicException(SR.Cryptography_NotValidPrivateKey);
+            }
+
+            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
+
+            writer.PushSequence();
+
+            // Format version 0
+            writer.WriteInteger(0);
+            writer.WriteKeyParameterInteger(rsaParameters.Modulus);
+            writer.WriteKeyParameterInteger(rsaParameters.Exponent);
+            writer.WriteKeyParameterInteger(rsaParameters.D);
+            writer.WriteKeyParameterInteger(rsaParameters.P);
+            writer.WriteKeyParameterInteger(rsaParameters.Q);
+            writer.WriteKeyParameterInteger(rsaParameters.DP);
+            writer.WriteKeyParameterInteger(rsaParameters.DQ);
+            writer.WriteKeyParameterInteger(rsaParameters.InverseQ);
+
+            writer.PopSequence();
+            return writer;
+        }
+    }
+}
diff --git a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs
index c77944c204c658..c3732580830a6a 100644
--- a/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs
+++ b/src/libraries/Common/src/System/Security/Cryptography/RSAKeyFormatHelper.cs
@@ -15,6 +15,10 @@ internal static partial class RSAKeyFormatHelper
             Oids.Rsa,
         };
 
+        // TODO Currently reading PKCS#1 keys uses BigInteger which is not optimal and uses APIs that are not
+        // available downlevel. These methods should eventually be replaced with a more efficient implementation
+        // and they should be moved into the RSAKeyFormatHelper.Pkcs1 (which is shared between S.S.C. and M.B.C.).
+
         internal static void FromPkcs1PrivateKey(
             ReadOnlyMemory keyData,
             in AlgorithmIdentifierAsn algId,
@@ -254,57 +258,5 @@ private static void WriteAlgorithmIdentifier(AsnWriter writer)
 
             writer.PopSequence();
         }
-
-        internal static AsnWriter WritePkcs1PublicKey(in RSAParameters rsaParameters)
-        {
-            if (rsaParameters.Modulus == null || rsaParameters.Exponent == null)
-            {
-                throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
-            }
-
-            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
-            writer.PushSequence();
-            writer.WriteKeyParameterInteger(rsaParameters.Modulus);
-            writer.WriteKeyParameterInteger(rsaParameters.Exponent);
-            writer.PopSequence();
-
-            return writer;
-        }
-
-        internal static AsnWriter WritePkcs1PrivateKey(in RSAParameters rsaParameters)
-        {
-            if (rsaParameters.Modulus == null || rsaParameters.Exponent == null)
-            {
-                throw new CryptographicException(SR.Cryptography_InvalidRsaParameters);
-            }
-
-            if (rsaParameters.D == null ||
-                rsaParameters.P == null ||
-                rsaParameters.Q == null ||
-                rsaParameters.DP == null ||
-                rsaParameters.DQ == null ||
-                rsaParameters.InverseQ == null)
-            {
-                throw new CryptographicException(SR.Cryptography_NotValidPrivateKey);
-            }
-
-            AsnWriter writer = new AsnWriter(AsnEncodingRules.DER);
-
-            writer.PushSequence();
-
-            // Format version 0
-            writer.WriteInteger(0);
-            writer.WriteKeyParameterInteger(rsaParameters.Modulus);
-            writer.WriteKeyParameterInteger(rsaParameters.Exponent);
-            writer.WriteKeyParameterInteger(rsaParameters.D);
-            writer.WriteKeyParameterInteger(rsaParameters.P);
-            writer.WriteKeyParameterInteger(rsaParameters.Q);
-            writer.WriteKeyParameterInteger(rsaParameters.DP);
-            writer.WriteKeyParameterInteger(rsaParameters.DQ);
-            writer.WriteKeyParameterInteger(rsaParameters.InverseQ);
-
-            writer.PopSequence();
-            return writer;
-        }
     }
 }
diff --git a/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaContractTests.cs b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaContractTests.cs
new file mode 100644
index 00000000000000..c4c0f754db9aba
--- /dev/null
+++ b/src/libraries/Common/tests/System/Security/Cryptography/AlgorithmImplementations/CompositeMLDsa/CompositeMLDsaContractTests.cs
@@ -0,0 +1,633 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Linq;
+using Xunit;
+using Xunit.Sdk;
+
+using CompositeMLDsaTestVector = System.Security.Cryptography.Tests.CompositeMLDsaTestData.CompositeMLDsaTestVector;
+
+namespace System.Security.Cryptography.Tests
+{
+    public static class CompositeMLDsaContractTests
+    {
+        public static IEnumerable