Skip to content

Add PQC certificate support for HTTPS #62866

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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.CodeAnalysis;
using System.Globalization;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
Expand Down Expand Up @@ -95,6 +96,21 @@ private static X509Certificate2 LoadCertificateKey(X509Certificate2 certificate,
const string RSAOid = "1.2.840.113549.1.1.1";
const string DSAOid = "1.2.840.10040.4.1";
const string ECDsaOid = "1.2.840.10045.2.1";
const string MLDSA44Oid = "2.16.840.1.101.3.4.3.17";
const string MLDSA65Oid = "2.16.840.1.101.3.4.3.18";
const string MLDSA87Oid = "2.16.840.1.101.3.4.3.19";
const string SLHDSASHA2128sOid = "2.16.840.1.101.3.4.3.20";
const string SLHDSASHA2128fOid = "2.16.840.1.101.3.4.3.21";
const string SLHDSASHA2192sOid = "2.16.840.1.101.3.4.3.22";
const string SLHDSASHA2192fOid = "2.16.840.1.101.3.4.3.23";
const string SLHDSASHA2256sOid = "2.16.840.1.101.3.4.3.24";
const string SLHDSASHA2256fOid = "2.16.840.1.101.3.4.3.25";
const string SLHDSASHAKE128sOid = "2.16.840.1.101.3.4.3.26";
const string SLHDSASHAKE128fOid = "2.16.840.1.101.3.4.3.27";
const string SLHDSASHAKE192sOid = "2.16.840.1.101.3.4.3.28";
const string SLHDSASHAKE192fOid = "2.16.840.1.101.3.4.3.29";
const string SLHDSASHAKE256sOid = "2.16.840.1.101.3.4.3.30";
const string SLHDSASHAKE256fOid = "2.16.840.1.101.3.4.3.31";

// Duplication is required here because there are separate CopyWithPrivateKey methods for each algorithm.
var keyText = File.ReadAllText(keyPath);
Expand Down Expand Up @@ -142,6 +158,47 @@ private static X509Certificate2 LoadCertificateKey(X509Certificate2 certificate,
throw CreateErrorGettingPrivateKeyException(keyPath, ex);
}
}
case MLDSA44Oid:
case MLDSA65Oid:
case MLDSA87Oid:
{
#pragma warning disable SYSLIB5006 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
using var mlDsa = ImportMLDsaKeyFromFile(keyText, password);

try
{
return certificate.CopyWithPrivateKey(mlDsa);
}
catch (Exception ex)
{
throw CreateErrorGettingPrivateKeyException(keyPath, ex);
}
}
case SLHDSASHA2128sOid:
case SLHDSASHA2128fOid:
case SLHDSASHA2192sOid:
case SLHDSASHA2192fOid:
case SLHDSASHA2256sOid:
case SLHDSASHA2256fOid:
case SLHDSASHAKE128sOid:
case SLHDSASHAKE128fOid:
case SLHDSASHAKE192sOid:
case SLHDSASHAKE192fOid:
case SLHDSASHAKE256sOid:
case SLHDSASHAKE256fOid:
{
using var slhDsa = ImportSlhDsaKeyFromFile(keyText, password);

try
{
return certificate.CopyWithPrivateKey(slhDsa);
}
catch (Exception ex)
{
throw CreateErrorGettingPrivateKeyException(keyPath, ex);
}
}
#pragma warning restore SYSLIB5006 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
default:
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, CoreStrings.UnrecognizedCertificateKeyOid, certificate.PublicKey.Oid.Value));
}
Expand Down Expand Up @@ -174,6 +231,32 @@ private static void ImportKeyFromFile(AsymmetricAlgorithm asymmetricAlgorithm, s
}
}

[Experimental("SYSLIB5006")]
private static MLDsa ImportMLDsaKeyFromFile(string keyText, string? password)
{
if (password == null)
{
return MLDsa.ImportFromPem(keyText);
}
else
{
return MLDsa.ImportFromEncryptedPem(keyText, password);
}
}

[Experimental("SYSLIB5006")]
private static SlhDsa ImportSlhDsaKeyFromFile(string keyText, string? password)
{
if (password == null)
{
return SlhDsa.ImportFromPem(keyText);
}
else
{
return SlhDsa.ImportFromEncryptedPem(keyText, password);
}
}

private static X509Certificate2 LoadFromStoreCert(CertificateConfig certInfo)
{
var subject = certInfo.Subject!;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,66 @@ public void ConfigureEndpoint_ThrowsWhen_The_KeyIsPublic()
[InlineData("https-dsa.pem", "https-dsa-protected.key", "test")]
[InlineData("https-dsa.crt", "https-dsa.key", null)]
[InlineData("https-dsa.crt", "https-dsa-protected.key", "test")]
[InlineData("https-mldsa44.pem", "https-mldsa44.key", null)]
[InlineData("https-mldsa44.pem", "https-mldsa44-protected.key", "aspnetcore")]
[InlineData("https-mldsa44.crt", "https-mldsa44.key", null)]
[InlineData("https-mldsa44.crt", "https-mldsa44-protected.key", "aspnetcore")]
[InlineData("https-mldsa65.pem", "https-mldsa65.key", null)]
[InlineData("https-mldsa65.pem", "https-mldsa65-protected.key", "aspnetcore")]
[InlineData("https-mldsa65.crt", "https-mldsa65.key", null)]
[InlineData("https-mldsa65.crt", "https-mldsa65-protected.key", "aspnetcore")]
[InlineData("https-mldsa87.pem", "https-mldsa87.key", null)]
[InlineData("https-mldsa87.pem", "https-mldsa87-protected.key", "aspnetcore")]
[InlineData("https-mldsa87.crt", "https-mldsa87.key", null)]
[InlineData("https-mldsa87.crt", "https-mldsa87-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-128s.pem", "https-slhdsa-sha2-128s.key", null)]
[InlineData("https-slhdsa-sha2-128s.pem", "https-slhdsa-sha2-128s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-128s.crt", "https-slhdsa-sha2-128s.key", null)]
[InlineData("https-slhdsa-sha2-128s.crt", "https-slhdsa-sha2-128s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-128f.pem", "https-slhdsa-sha2-128f.key", null)]
[InlineData("https-slhdsa-sha2-128f.pem", "https-slhdsa-sha2-128f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-128f.crt", "https-slhdsa-sha2-128f.key", null)]
[InlineData("https-slhdsa-sha2-128f.crt", "https-slhdsa-sha2-128f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-192s.pem", "https-slhdsa-sha2-192s.key", null)]
[InlineData("https-slhdsa-sha2-192s.pem", "https-slhdsa-sha2-192s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-192s.crt", "https-slhdsa-sha2-192s.key", null)]
[InlineData("https-slhdsa-sha2-192s.crt", "https-slhdsa-sha2-192s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-192f.pem", "https-slhdsa-sha2-192f.key", null)]
[InlineData("https-slhdsa-sha2-192f.pem", "https-slhdsa-sha2-192f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-192f.crt", "https-slhdsa-sha2-192f.key", null)]
[InlineData("https-slhdsa-sha2-192f.crt", "https-slhdsa-sha2-192f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-256s.pem", "https-slhdsa-sha2-256s.key", null)]
[InlineData("https-slhdsa-sha2-256s.pem", "https-slhdsa-sha2-256s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-256s.crt", "https-slhdsa-sha2-256s.key", null)]
[InlineData("https-slhdsa-sha2-256s.crt", "https-slhdsa-sha2-256s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-256f.pem", "https-slhdsa-sha2-256f.key", null)]
[InlineData("https-slhdsa-sha2-256f.pem", "https-slhdsa-sha2-256f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-sha2-256f.crt", "https-slhdsa-sha2-256f.key", null)]
[InlineData("https-slhdsa-sha2-256f.crt", "https-slhdsa-sha2-256f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-128s.pem", "https-slhdsa-shake-128s.key", null)]
[InlineData("https-slhdsa-shake-128s.pem", "https-slhdsa-shake-128s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-128s.crt", "https-slhdsa-shake-128s.key", null)]
[InlineData("https-slhdsa-shake-128s.crt", "https-slhdsa-shake-128s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-128f.pem", "https-slhdsa-shake-128f.key", null)]
[InlineData("https-slhdsa-shake-128f.pem", "https-slhdsa-shake-128f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-128f.crt", "https-slhdsa-shake-128f.key", null)]
[InlineData("https-slhdsa-shake-128f.crt", "https-slhdsa-shake-128f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-192s.pem", "https-slhdsa-shake-192s.key", null)]
[InlineData("https-slhdsa-shake-192s.pem", "https-slhdsa-shake-192s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-192s.crt", "https-slhdsa-shake-192s.key", null)]
[InlineData("https-slhdsa-shake-192s.crt", "https-slhdsa-shake-192s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-192f.pem", "https-slhdsa-shake-192f.key", null)]
[InlineData("https-slhdsa-shake-192f.pem", "https-slhdsa-shake-192f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-192f.crt", "https-slhdsa-shake-192f.key", null)]
[InlineData("https-slhdsa-shake-192f.crt", "https-slhdsa-shake-192f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-256s.pem", "https-slhdsa-shake-256s.key", null)]
[InlineData("https-slhdsa-shake-256s.pem", "https-slhdsa-shake-256s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-256s.crt", "https-slhdsa-shake-256s.key", null)]
[InlineData("https-slhdsa-shake-256s.crt", "https-slhdsa-shake-256s-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-256f.pem", "https-slhdsa-shake-256f.key", null)]
[InlineData("https-slhdsa-shake-256f.pem", "https-slhdsa-shake-256f-protected.key", "aspnetcore")]
[InlineData("https-slhdsa-shake-256f.crt", "https-slhdsa-shake-256f.key", null)]
[InlineData("https-slhdsa-shake-256f.crt", "https-slhdsa-shake-256f-protected.key", "aspnetcore")]
public void ConfigureEndpoint_CanLoadPemCertificates(string certificateFile, string certificateKey, string password)
{
var serverOptions = CreateServerOptions();
Expand Down
60 changes: 60 additions & 0 deletions src/Shared/TestCertificates/https-mldsa44-protected.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
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
wOaxVZJTGMRJ
-----END ENCRYPTED PRIVATE KEY-----
Binary file added src/Shared/TestCertificates/https-mldsa44.crt
Binary file not shown.
57 changes: 57 additions & 0 deletions src/Shared/TestCertificates/https-mldsa44.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
-----BEGIN PRIVATE KEY-----
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
-----END PRIVATE KEY-----
Loading
Loading