Create a certificate request
A server needs a hybrid certificate from a certification authority (CA), and its private keys must never leave the server. The server creates a certificate request (PKCS #10), and the CA issues the certificate from it.
Solution overview
| Side | Element | Purpose |
|---|---|---|
| Requester | CertificateRequest.CreateSigningRequest with CryptoMode.Hybrid | request with an RSA key and an ML-DSA key, with both proofs of possession signed |
| CA | X509CertificateBuilder.LoadSigningRequest | reads the request and verifies both signatures |
| CA | X509CertificateBuilder.Create with CryptoMode.Hybrid | issues the hybrid certificate |
| Requester | CopyWithPrivateKey, AttachAlternativePrivateMLDsaKey | connects the certificate with both of its private keys |
Implementation
On the server:
using var serverRsa = RSA.Create(3072); using var serverPq = MLDsaKey.Generate(MLDsaParameterSet.MLDsa65); var request = new CertificateRequest( "CN=www.example.com", serverRsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); byte[] csr = request.CreateSigningRequest(CryptoMode.Hybrid, serverPq); File.WriteAllBytes("www.example.com.csr", csr);
At the CA. Here, ca is a hybrid issuer certificate whose RSA and ML-DSA keys are in the process, for
example after a PFX import:
using var caKey = RSA.Create(3072); var caRequest = new CertificateRequest( "CN=Contoso Hybrid CA", caKey, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); caRequest.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true)); caRequest.CertificateExtensions.Add( new X509KeyUsageExtension(X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true)); using var ca = caRequest.CreateSelfSigned( DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(5), CryptoMode.Hybrid); var incoming = X509CertificateBuilder.LoadSigningRequest( File.ReadAllBytes("www.example.com.csr"), HashAlgorithmName.SHA256, signerSignaturePadding: RSASignaturePadding.Pkcs1, mode: CryptoMode.Hybrid); incoming.CertificateExtensions.Add(new X509KeyUsageExtension( X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true)); using var issued = incoming.Create( ca, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1), RandomNumberGenerator.GetBytes(16), CryptoMode.Hybrid); File.WriteAllText("www.example.com.pem", issued.ExportCertificatePem());
Back on the server:
using var certificate = X509Certificate2.CreateFromPem(File.ReadAllText("www.example.com.pem")); using var server = certificate.CopyWithPrivateKey(serverRsa); server.AttachAlternativePrivateMLDsaKey(serverPq);
Remarks
- Both proofs of possession are verified.
LoadSigningRequestwithCryptoMode.Hybridverifies the request's RSA signature and its ML-DSA signature. If either is invalid, the method throws aCryptographicException. - The ML-DSA key comes from the request. The issued certificate carries exactly the requester's public ML-DSA key. The CA doesn't generate a key of its own.
- Generate the ML-DSA key yourself. Without the
serverPqargument,CreateSigningRequestgenerates a key that you can't reach later. Always pass your own key. - Keep the private ML-DSA key. The server needs
serverPqagain when the certificate arrives. Keep the key until then, for example in a protected file written withExportPkcs8PrivateKey, and afterward move the finished certificate as a PFX file. - Pure ML-DSA request.
CryptoMode.PostQuantumwith a request fromMLDsaX509SignatureGenerator.CreateCertificateRequestproduces a request that contains ML-DSA only. Like every operation inPostQuantummode, it requiresPostQuantumExemptions.PurePostQuantum; see Post-quantum algorithm policy.
Related topics
en/wiki/pqcrypt/lib/how-to/request-certificates.txt · Zuletzt geändert: von Wolfgang van der Stille