<data name="net_auth_alert" xml:space="preserve">
<value>Authentication failed on the remote side (the stream might still be available for additional authentication attempts).</value>
</data>
+ <data name="net_auth_ephemeral" xml:space="preserve">
+ <value>Authentication failed because the platform does not support ephemeral keys.</value>
+ </data>
<data name="net_auth_message_not_encrypted" xml:space="preserve">
<value>Protocol error: A received message contains a valid signature but it was not encrypted as required by the effective Protection Level.</value>
</data>
Link="Common\Interop\Windows\Crypt32\Interop.CertEnumCertificatesInStore.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.MsgEncodingType.cs"
Link="Common\Interop\Windows\Crypt32\Interop.Interop.MsgEncodingType.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CertContextPropId.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CertContextPropId.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CertDuplicateCertificateContext.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CertDuplicateCertificateContex.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CertGetCertificateContextProperty_NO_NULLABLE.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CertGetCertificateContextProperty_NO_NULLABLE.cs" />
+ <Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeCrypt32Handle.cs"
+ Link="Common\Microsoft\Win32\SafeHandles\SafeCrypt32Handle.cs" />
+ <Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeHandleCache.cs"
+ Link="Common\Microsoft\Win32\SafeHandles\SafeHandleCache.cs" />
+ <Compile Include="$(CommonPath)Microsoft\Win32\SafeHandles\SafeCertContextHandle.cs"
+ Link="Common\Microsoft\Win32\SafeHandles\SafeCertContextHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CloseHandle.cs"
Link="Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
<Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.Alerts.cs"
public static SafeFreeCredentials AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions, bool newCredentialsRequested)
{
+ SslStreamCertificateContext? certificateContext = sslAuthenticationOptions.CertificateContext;
+
try
{
EncryptionPolicy policy = sslAuthenticationOptions.EncryptionPolicy;
AcquireCredentialsHandleSchCredentials(sslAuthenticationOptions);
#pragma warning restore SYSLIB0040
- SslStreamCertificateContext? certificateContext = sslAuthenticationOptions.CertificateContext;
-
if (certificateContext != null && certificateContext.Trust != null && certificateContext.Trust._sendTrustInHandshake)
{
AttachCertificateStore(cred, certificateContext.Trust._store!);
return cred;
}
+ catch (Win32Exception e) when (e.NativeErrorCode == (int)Interop.SECURITY_STATUS.NoCredentials && certificateContext != null)
+ {
+ Debug.Assert(certificateContext.Certificate.HasPrivateKey);
+ using SafeCertContextHandle safeCertContextHandle = Interop.Crypt32.CertDuplicateCertificateContext(certificateContext.Certificate.Handle);
+ // on Windows we do not support ephemeral keys.
+ throw new AuthenticationException(safeCertContextHandle.HasEphemeralPrivateKey ? SR.net_auth_ephemeral : SR.net_auth_SSPI, e);
+ }
catch (Win32Exception e)
{
throw new AuthenticationException(SR.net_auth_SSPI, e);
}
}
+ [Fact]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public async Task SslStream_EphemeralKey_Throws()
+ {
+ (X509Certificate2 serverCertificate, X509Certificate2Collection chain) = TestHelper.GenerateCertificates(nameof(SslStream_EphemeralKey_Throws), ephemeralKey: true);
+ TestHelper.CleanupCertificates(nameof(SslStream_EphemeralKey_Throws));
+
+ var clientOptions = new SslClientAuthenticationOptions()
+ {
+ TargetHost = "localhost",
+ RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true
+ };
+
+ var serverOptions = new SslServerAuthenticationOptions()
+ {
+ ServerCertificate = serverCertificate
+ };
+
+ (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
+
+ 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)
+ {
+ c.Dispose();
+ }
+ }
+
[Theory]
[InlineData(16384 * 100, 4096, 1024, false)]
[InlineData(16384 * 100, 4096, 1024, true)]
return extensions;
}
- internal static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(string targetName, [CallerMemberName] string? testName = null, bool longChain = false, bool serverCertificate = true)
+ internal static (X509Certificate2 certificate, X509Certificate2Collection) GenerateCertificates(
+ string targetName,
+ [CallerMemberName] string? testName = null,
+ bool longChain = false,
+ bool serverCertificate = true,
+ bool ephemeralKey = false)
{
const int keySize = 2048;
if (PlatformDetection.IsWindows && testName != null)
responder.Dispose();
root.Dispose();
- if (PlatformDetection.IsWindows)
+ if (!ephemeralKey && PlatformDetection.IsWindows)
{
X509Certificate2 ephemeral = endEntity;
endEntity = new X509Certificate2(endEntity.Export(X509ContentType.Pfx), (string?)null, X509KeyStorageFlags.Exportable);