I use BouncyCastle.Crypto 1.8.5.0 to create a SecurityKey for authenticating against Apple's APNS HTTP/2 endpoint. It works just fine when debugging on MacOS Mojave, but when I deploy in a Docker container, I get the following exception:
Interop+Crypto+OpenSslCryptographicException: error:1010207B:elliptic curve routines:ec_key_simple_check_key:invalid private key
at System.Security.Cryptography.ECOpenSsl.ImportParameters(ECParameters parameters)
at System.Security.Cryptography.ECDsa.Create(ECParameters parameters)
After reading this I tried to force using OpenSSL 1.1 by setting env variable CLR_OPENSSL_VERSION_OVERRIDE=1.1 and making sure the latest openssl is installed in the Dockerfile, but no fix.
Code which reads private key file and initializes SecurityKey:
using (var reader = System.IO.File.OpenText (PathToApnsKeyFile)) {
var ecPrivateKeyParameters = (ECPrivateKeyParameters)new PemReader (reader).ReadObject ();
var x = ecPrivateKeyParameters.Parameters.G.AffineXCoord.GetEncoded ();
var y = ecPrivateKeyParameters.Parameters.G.AffineYCoord.GetEncoded ();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned ();
// Convert the BouncyCastle key to a Native Key.
var msEcp = new ECParameters { Curve = ECCurve.NamedCurves.nistP256, Q = { X = x, Y = y }, D = d };
jwtPrivateKey = new ECDsaSecurityKey (ECDsa.Create (msEcp));
}
Dockerfile:
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-bionic AS build-env
WORKDIR /app
# copy csproj and restore as distinct layers
COPY *.csproj ./
RUN dotnet restore
# copy everything else and build
COPY . ./
RUN dotnet publish -c Release -o out -r linux-x64
# build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-bionic AS runtime
RUN apt-get update && apt-get -y install openssl
WORKDIR /app
COPY --from=build-env /app/out ./
ENTRYPOINT ["dotnet", "helloapple.dll"]
UPDATE
My MacOS appears to be using LibreSSL 2.6.5. The openssl version being pulled by apt-get is 1.1.1-1ubuntu2.1~18.04.7.
I finally found the answer. Turns out the problem is in the implementation of the curve parameters, which differs between platforms. The above code works on Windows and Mac, but apparently not on Linux. The correct calculation is:
var q = ecPrivateKeyParameters.Parameters.G.Multiply (ecPrivateKeyParameters.D).Normalize ();
var x = q.AffineXCoord.GetEncoded ();
var y = q.AffineYCoord.GetEncoded ();
var d = ecPrivateKeyParameters.D.ToByteArrayUnsigned ();
// Convert the BouncyCastle key to a Native Key.
var msEcp = new ECParameters { Curve = ECCurve.NamedCurves.nistP256, Q = { X = x, Y = y }, D = d };
Solution was found in the following comment on github:
https://github.com/dotnet/core/issues/2037#issuecomment-436340605