SSL_ERROR_SSL
with .NET 8
and OpenSSL
I'm running a .NET 8
application in a Windows + Visual Studio environment, and everything works fine. However, when I run it inside a Docker container, I get the following error when making an HTTPS request with a client certificate:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
USER root
RUN apt-get update && apt-get install -y ca-certificates
COPY ["InsuranceApi/sosMedecin.crt", "/usr/local/share/ca-certificates/sosMedecin.crt"]
RUN update-ca-certificates
USER $APP_UID
WORKDIR /app
EXPOSE 8080
EXPOSE 8081
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["InsuranceApi/InsuranceApi.csproj", "InsuranceApi/"]
RUN dotnet restore "./InsuranceApi/InsuranceApi.csproj"
COPY . .
WORKDIR "/src/InsuranceApi"
RUN dotnet build "./InsuranceApi.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
RUN dotnet publish "./InsuranceApi.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
COPY ["InsuranceApi/sosMedecin.pfx", "/app/publish/sosMedecin.pfx"]
ENTRYPOINT ["dotnet", "InsuranceApi.dll"]
public class AuthenticationService
{
private readonly CertificateOptions _certificateOptions;
private readonly SsoOptions _ssoOptions;
public AuthenticationService(IOptions<CertificateOptions> certificateOptions, IOptions<SsoOptions> ssoOptions)
{
_certificateOptions = certificateOptions.Value;
_ssoOptions = ssoOptions.Value;
}
public async Task<string?> GetSsoToken()
{
string? ssoToken = null;
HttpClientHandler handler = new HttpClientHandler();
try
{
// Adding client certificate to HttpClientHandler
handler.ClientCertificates.Add(new X509Certificate2(_certificateOptions.CertificatePath, _certificateOptions.CertificatePassword));
// Making the HTTP request to get the SSO token
using (var client = new HttpClient(handler))
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("format", "text"),
new KeyValuePair<string, string>("username", _ssoOptions.Username),
new KeyValuePair<string, string>("password", _ssoOptions.Password),
new KeyValuePair<string, string>("submit", "confirm")
});
HttpResponseMessage response = await client.PostAsync(_ssoOptions.SsoTokenUrl, content);
// If response contains the SSO token cookie
if (response.Headers.Contains("Set-Cookie"))
{
var cookieHeader = response.Headers
.GetValues("Set-Cookie")
.FirstOrDefault(c => c.StartsWith("SSOV2"));
if (!string.IsNullOrEmpty(cookieHeader))
{
ssoToken = cookieHeader.Split(';')[0].Split('=')[1];
}
else
{
throw new ArgumentNullException("Cookie with SSO token was not found.");
}
}
else
{
throw new ArgumentNullException($"SSO token not found in response.\nResponse: {await response.Content.ReadAsStringAsync()}");
}
}
}
catch (Exception ex)
{
// Log any errors and inner exceptions
Console.WriteLine($"Unexpected error: {ex.Message}");
while (ex.InnerException != null)
{
Console.WriteLine($"Inner Exception: {ex.InnerException.Message}");
ex = ex.InnerException;
}
throw;
}
return ssoToken;
}
}
I attempted to add the certificate manually in the Dockerfile using update-ca-certificates, but it did not resolve the issue.
We had the same issue about three weeks ago, but we were using a Windows image. C# certificate validation worked outside of Docker, but failed inside the container with an SSL exception. It is not exactly your problem since you are using a Linux container, but it might be very similar. I will write up our solution and hope that it might point you in the right direction.
We were running the Windows container on systems behind proxies. Our problem was that whatever client the container was using for the certificate revocation check (it is not the C# HTTP client!) was not respecting the proxy settings set to Docker and the C# http client, and we got the SSL connection exception.
The simple solution was to set the certificate revocation mode to Offline
in our certificate validation callback. No more calls where send out of the container for the certificate revocation check, and the certificate verification worked fine inside the container.
Since we did not want to do that, we had to set the proxy settings to the registry of the container, which fixed the issue. This could be done in the Dockerfile using reg add, but since we are using the same container for different customer systems with different proxies, we had to pass the proxy / no proxy settings during the startup of the container via environment variables, f.e. HTTP_PROXY
and NO_PROXY
. The C# app then writes these values into the registry during startup, simplified code:
string path = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
string httpProxy = Environment.GetEnvironmentVariable("HTTP_PROXY");
string noProxy = Environment.GetEnvironmentVariable("NO_PROXY");
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(path, writable: true))
{
key.SetValue("ProxyServer", httpProxy, RegistryValueKind.String);
key.SetValue("ProxyEnable", 1, RegistryValueKind.DWord);
key.SetValue("MigrateProxy", 1, RegistryValueKind.DWord);
key.SetValue("ProxyOverride", noProxy, RegistryValueKind.String);
}
Hope this helps and gives you some ideas on what can go wrong, debugging these certificate issues in a container is not straight forward. What helped me is building the certificate chain in the C# code manually and logging all results, and using tools like openssl
and the Test-Certificate
cmdlet on the command line inside the container to get more results on what exactly goes wrong during the certificate validation.