dockerasp.net-coresslssl-certificatekestrel-http-server

SSL certificate error when running ASP.NET core app on docker


On Mac, I generated an SSL certificate using Keychain access and exported it as a .cer file which worked well locally. However, when ran on docker, I received this: Unhandled exception. System.NotSupportedException: The server mode SSL must use a certificate with the associated private key.

So I exported a .p12 file for the same cert and extracted the .key from it using this command

Nabils-MBP:certs nabilhaffar$ openssl pkcs12 -legacy -nocerts -in mycert.p12 -out mykey.key 
 

Then used the original .cer file with mykey.key to create a .pfx using:

openssl pkcs12 -export -out mycert.pfx -inkey mykey.key -in mycert.cer

So now I have this error:

Unhandled exception. System.Security.Cryptography.CryptographicException: The certificate data cannot be read with the provided password, the password may be incorrect.

I've been stuck here for a couple days. I need this certificate to work on docker locally, and the AWS EC2 instance it's running on. Any help would be appreciated.

.NET 7 certificate loading in program.cs :

var certFilePath = builder.Configuration["Kestrel:Endpoints:Https:Certificate:Path"];
var certPassword = builder.Configuration["Cert:Password"];
Console.WriteLine($"Certificate Path: {certFilePath}");
Console.WriteLine($"Certificate Password: {certPassword}");
if (string.IsNullOrEmpty(certFilePath) || string.IsNullOrEmpty(certPassword))
{
    throw new Exception("Certificate file path or password not configured.");
}


try
{
    var certificate = new X509Certificate2(certFilePath, certPassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet);
    builder.WebHost.ConfigureKestrel((context, options) =>
    {
        options.ConfigureHttpsDefaults(httpsOptions =>
        {
            httpsOptions.ServerCertificate = certificate;
        });
        options.Listen(IPAddress.Any, 80);   // HTTP port
        options.Listen(IPAddress.Any, 5001, listenOptions =>
        {
            listenOptions.UseHttps();        // HTTPS port with a certificate
        });
    });


    // Use certificate (e.g., add to services for HTTPS)
    builder.Services.AddSingleton(certificate);
}
catch (CryptographicException ex)
{
    Console.WriteLine($"CryptographicException: {ex.Message}");
    throw;
}

DockerFile:

# Use the official .NET 7 SDK image for building the app
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /app

# Copy and restore dependencies
COPY *.sln ./
COPY AssetTracker/*.csproj ./AssetTracker/
RUN dotnet restore AssetTracker/AssetTracker.csproj

# Copy everything else and build the application
COPY AssetTracker/. ./AssetTracker/
WORKDIR /app/AssetTracker
RUN dotnet publish -c Release -o /app/publish

# Use the .NET runtime image to run the app
FROM mcr.microsoft.com/dotnet/aspnet:7.0
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 80
EXPOSE 443
EXPOSE 5001
CMD ["dotnet", "AssetTracker.dll"]

RUN apt update && apt install -y curl

docker-compose.dev.yml

services:
  assettracker:
    image: assettracker
    build: .
    ports:
      - "8080:80"
      - "5001:443"
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - Redis__Host=redis
      - Redis__Port=6379
    volumes:
      - /Users/nabilhaffar/.microsoft/usersecrets:/root/.microsoft/usersecrets:ro
      - /Users/nabilhaffar/.aspnet/DataProtection-Keys:/root/.aspnet/DataProtection-Keys
      - ./Assettracker/certs:/root/certs  # Mount certs to /root/certs inside container

    env_file:
      - .env
    depends_on:
      - redis

  redis:
    image: redis:latest
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

volumes:
  redis-data:
    driver: local

appsettings.Development.json

{
  "Kestrel": {
    "Endpoints": {
      "Https": {
        "Url": "https://0.0.0.0:443",
        "Certificate": {
          "Path": "/root/certs/mycert.pfx"
          //"Path": "certs/mycert.cer" // Use the relative path locally
        },
        "CertificateKey": {
          //"Path": "/root/certs/mykey.key",
          //"Path": "certs/mykey-pkcs8.key" // Use the relative path locally

        }
      }
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },

  "Redis": {
    "Host": "127.0.0.1",
    "Port": "6379"
  }
}

Solution

  • The issue I was having was due to having the Kestrel configuration in appsettings.json as well as program.cs. For some reason, it was adding another Kestrel (instance?) to the one already configured in the program.cs code. The Kestrel config and https cert loaded in program.cs had already been working with the same Dockerfile and compose.yml. I only noticed it when the same error got triggered at App.run in program.cs while previously it triggered at the certificate loading code also in program.cs