[YARP Ingress Controller] TLS handshake missing private key: `The server mode SSL must use a certificate with the associated private key`
- Dominant language
- C#
- Stars
- 9.6k
- Forks
- 933
- Avg merge
- 12d 18h
- Merged PRs (30d)
- 2
Description
### Describe the bug
Hi there,
I implemented a HTTPS termination for YARP Ingress Controller this week. During my test, I found that TLS handshake would fail and error was `The server mode SSL must use a certificate with the associated private key`.
I checked the documentation. In summary, a C# `X509Certificate2` instance by default has an ephemeral private key in memory linked by a memory pointer. ASP.NET Kestrel relies on OpenSSL on Linux for TLS handshake. However, the OpenSSL under the hood of Linux doesn't know how to get its private key by pointer so OpenSSL thinks the private key is missing.
> I would like to make a PR fix for you to review and get your feedback if you regard it as a real bug.
#### My fix
> We need to export the private key so that the PFX file itself includes the private key. The code has done it for Windows.
The following code would eliminate the TLS handshake error and my HTTPS termination would work:
```csharp
if (OperatingSystem.IsLinux())
{
var pfx = certificate.Export(X509ContentType.Pkcs12);
certificate = X509CertificateLoader.LoadPkcs12(pfx, null); // a class in .NET9
}
```
I would say the current code would also work with slight modification based on .NET 8, even if I haven't tested:
```csharp
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux())
{
// Cert needs converting. Read https://github.com/dotnet/runtime/issues/23749#issuecomment-388231655
using var convertedCertificate = X509Certificate2.CreateFromPem(certString, privateString);
return new X509Certificate2(convertedCertificate.Export(X509ContentType.Pkcs12));
}
```
### To Reproduce
#### Reproduce
Use a certificate type secret in Kubernetes and use the generated certificate for TLS handshake by [public X509Certificate2 ConvertCertificate(NamespacedName namespacedName, V1Secret secret)](https://github.com/dotnet/yarp/blob/main/src/Kubernetes.Controller/Certificates/CertificateHelper.cs#L26).
#### Exceptions
```
[2026-09-03 16:05:36 DBG] [Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware] Failed to authenticate HTTPS connection.
System.Security.Authentication.AuthenticationException: The server mode SSL must use a certificate with the associated private key.
at System.Net.Security.SslStream.AcquireServerCredentials(Byte[]& thumbPrint)
at System.Net.Security.SslStream.GenerateToken(ReadOnlySpan`1 inputBuffer, Int32& consumed)
at System.Net.Security.SslStream.NextMessage(ReadOnlySpan`1 incomingBuffer, Int32& consumed)
at System.Net.Security.SslStream.ProcessTlsFrame(Int32 frameSize)
at System.Net.Security.SslStream.ForceAuthenticationAsync[TIOAdapter](Boolean receiveFirst, Byte[] reAuthenticationData, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.Kestrel.Https.Internal.HttpsConnectionMiddleware.OnConnectionAsync(ConnectionContext context)
```
### Further technical details
#### Packages
```
```
#### Platform
Docker image `mcr.microsoft.com/dotnet/sdk:10.0-noble` on Ubuntu host machine
Contributor guide
Research direction
Start with ConvertCertificate in src/Kubernetes.Controller/Certificates/CertificateHelper.cs, then reproduce the certificate-secret flow in the Ubuntu-based Docker environment described in the issue. Verify the resulting certificate retains its private key and that the YARP HTTPS termination completes the TLS handshake without the reported authentication exception.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, docker, kubernetes, ubuntu
- Domain
- backend, networking, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100