Skip to content

[NOREVIEW] Test support for certificates with ephemeral keys #114767

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 1 commit 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
Expand Up @@ -18,6 +18,7 @@ internal enum PfxCertStoreFlags : int
PKCS12_ALWAYS_CNG_KSP = 0x00000200,
PKCS12_ALLOW_OVERWRITE_KEY = 0x00004000,
PKCS12_NO_PERSIST_KEY = 0x00008000,
PKCS12_NAMED_NO_PERSIST_KEY = 0x00020000,
PKCS12_INCLUDE_EXTENDED_PROPERTIES = 0x00000010,
None = 0x00000000,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul
}
}
}
catch { };
catch { }
;

try
{
Expand All @@ -95,7 +96,8 @@ public static void CleanupCertificates([CallerMemberName] string? testName = nul
}
}
}
catch { };
catch { }
;
}

internal static X509ExtensionCollection BuildTlsServerCertExtensions(string serverName)
Expand Down Expand Up @@ -160,10 +162,10 @@ public static (X509Certificate2 certificate, X509Certificate2Collection) Generat
responder.Dispose();
root.Dispose();

if (!ephemeralKey && PlatformDetection.IsWindows)
if (PlatformDetection.IsWindows)
{
X509Certificate2 ephemeral = endEntity;
endEntity = X509CertificateLoader.LoadPkcs12(endEntity.Export(X509ContentType.Pfx), (string?)null, X509KeyStorageFlags.Exportable);
endEntity = X509CertificateLoader.LoadPkcs12(endEntity.Export(X509ContentType.Pfx), (string?)null, ephemeralKey ? X509KeyStorageFlags.EphemeralKeySet : X509KeyStorageFlags.Exportable);
ephemeral.Dispose();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,7 @@ public async Task SslStream_ClientCertificateContext_SendsChain(bool useTrust)
TargetHost = "localhost",
};
clientOptions.RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
clientOptions.ClientCertificateContext = SslStreamCertificateContext.Create(clientCertificate, useTrust ? null : clientChain, offline:true, trust);
clientOptions.ClientCertificateContext = SslStreamCertificateContext.Create(clientCertificate, useTrust ? null : clientChain, offline: true, trust);

await SslStream_ClientSendsChain_Core(clientOptions, clientChain);

Expand All @@ -952,10 +952,10 @@ public async Task SslStream_ClientCertificateContext_SendsChain(bool useTrust)

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public async Task SslStream_EphemeralKey_Throws()
public async Task SslStream_EphemeralKey_DoesNotThrow()
{
(X509Certificate2 serverCertificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_EphemeralKey_Throws), ephemeralKey: true);
TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_Throws));
(X509Certificate2 serverCertificate, X509Certificate2Collection chain) = Configuration.Certificates.GenerateCertificates(nameof(SslStream_EphemeralKey_DoesNotThrow), ephemeralKey: true);
TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_DoesNotThrow));

var clientOptions = new SslClientAuthenticationOptions()
{
Expand All @@ -969,21 +969,25 @@ public async Task SslStream_EphemeralKey_Throws()
};

(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
try
{
using (client)
using (server)
{
Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
Task t2 = server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);

Task t1 = client.AuthenticateAsClientAsync(clientOptions, CancellationToken.None);
Task t2 = server.AuthenticateAsServerAsync(serverOptions, CancellationToken.None);

AuthenticationException e = await Assert.ThrowsAsync<AuthenticationException>(() => t2);
Assert.Contains("ephemeral", e.Message);
server.Dispose();
await Assert.ThrowsAsync<IOException>(() => t1);
client.Dispose();

TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_Throws));
serverCertificate.Dispose();
foreach (X509Certificate c in chain)
await TestConfiguration.WhenAllOrAnyFailedWithTimeout(t1, t2);
}
}
finally
{
c.Dispose();
TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_DoesNotThrow));
serverCertificate.Dispose();
foreach (X509Certificate c in chain)
{
c.Dispose();
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ private static Interop.Crypt32.PfxCertStoreFlags MapKeyStorageFlags(X509KeyStora
if ((keyStorageFlags & X509KeyStorageFlags.EphemeralKeySet) == X509KeyStorageFlags.EphemeralKeySet)
{
pfxCertStoreFlags &= ~Interop.Crypt32.PfxCertStoreFlags.PKCS12_PREFER_CNG_KSP;
pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP;
pfxCertStoreFlags |= Interop.Crypt32.PfxCertStoreFlags.PKCS12_NAMED_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_NO_PERSIST_KEY | Interop.Crypt32.PfxCertStoreFlags.PKCS12_ALWAYS_CNG_KSP;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't get clarity from Windows on if it would be bad to do this "generally". It also doesn't solve the problem of loading the key from file and using CopyWithPrivateKey, still requires routing through PFX import/export (or doing something similar for the other path... but I haven't seen how this flag applies for non-PFX)

}

// In .NET Framework loading a PFX then adding the key to the Windows Certificate Store would
Expand Down
Loading