6

I am developing a web application using ASP.NET Core 3.0 preview 8. What I want to achieve is to create a X509Certificate2 directly from .crt and .key (PKCS#1) files to use it with Kestrel using the new .NET Core 3.0 built in Api which was introduced in this link.

What's new in .NET Core 3.0 (Preview 8)

Currently I am using this command to convert the certificate to .pfx file

openssl pkcs12 -export -in $CERTIFICATE_FILE -inkey $KEY_FILE -out $OUTPUT_CERTIFICATE_FILE -passout pass:$OUTPUT_CERTIFICATE_PASSWORD

Then I create the X509Certificate2 and use it with Kestrel:

webBuilder.ConfigureKestrel((context, options) =>
{
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
        if (!context.HostingEnvironment.IsProduction())
            return;

            string outputCertificateFile = context.Configuration["OUTPUT_CERTIFICATE_FILE"];
            string outputCertificatePassword = context.Configuration["OUTPUT_CERTIFICATE_PASSWORD"];

            var tlsCertificate = new X509Certificate2(outputCertificateFile, outputCertificatePassword);
            httpsOptions.ServerCertificate = tlsCertificate;
    });
});

Edit:-

I have implemented the new Api based on the example on the link above:

using (var privateKey = RSA.Create())
{
    byte[] keyBytes = File.ReadAllBytes(context.Configuration["KEY_FILE"]);

    privateKey.ImportRSAPrivateKey(keyBytes, out int bytesRead);

    X509Certificate2 certificateFile = new X509Certificate2(context.Configuration["CERTIFICATE_FILE"]);

    X509Certificate2 tlsCertificate = certificateFile.CopyWithPrivateKey(privateKey);

    httpsOptions.ServerCertificate = tlsCertificate;
}

But the new code throw this exception:

Unhandled exception. System.Security.Cryptography.CryptographicException: ASN1 corrupted data.
at System.Security.Cryptography.Asn1.AsnReader.CheckExpectedTag(Asn1Tag tag, Asn1Tag expectedTag, UniversalTagNumber tagNumber)
at System.Security.Cryptography.Asn1.AsnReader.ReadSequence(Asn1Tag expectedTag)
at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(AsnReader reader, Asn1Tag expectedTag, RSAPrivateKeyAsn& decoded)
at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(Asn1Tag expectedTag, ReadOnlyMemory`1 encoded, AsnEncodingRules ruleSet)
at System.Security.Cryptography.Asn1.RSAPrivateKeyAsn.Decode(ReadOnlyMemory`1 encoded, AsnEncodingRules ruleSet)
at System.Security.Cryptography.RSAKeyFormatHelper.FromPkcs1PrivateKey(ReadOnlyMemory`1 keyData, AlgorithmIdentifierAsn& algId, RSAParameters& ret)
at System.Security.Cryptography.RSA.ImportRSAPrivateKey(ReadOnlySpan`1 source, Int32& bytesRead)
at Example.Program.<>c__DisplayClass1_0.<CreateWebHostBuilder>b__3(HttpsConnectionAdapterOptions httpsOptions) in /src/Example/Program.cs:line 47
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions.ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configureOptions)
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(IServerAddressesFeature addresses, KestrelServerOptions serverOptions, ILogger logger, Func`2 createBinding)
at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServer.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
at Example.Program.Main(String[] args) in /src/Example/Program.cs:line 16
Waxren
  • 2,002
  • 4
  • 30
  • 43
  • .NET Core 3.0 should support .crt and .key directly, https://stackoverflow.com/questions/48077748/how-to-use-pem-certificate-in-kestrel-directly – Lex Li Aug 31 '19 at 16:11
  • Could you provide an example? As the provided link doesn't contain any code using the new built-in API – Waxren Aug 31 '19 at 16:32
  • https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-core-3-0#cryptographic-key-importexport – Lex Li Aug 31 '19 at 17:10
  • 1
    I have implemented the example in the link and unfortunately it didn't work. I have updated the question with the exception details @LexLi – Waxren Aug 31 '19 at 17:20
  • 1
    @Waxren Any updates? We also have this issue. – Pavel Voronin Sep 20 '19 at 09:39
  • Someone could solve this problem? I get the same exception. – Álvaro García Dec 20 '19 at 17:05
  • Check your key file to see it is '-----BEGIN ENCRYPTED PRIVATE KEY-----' or '-----BEGIN RSA PRIVATE KEY-----'. If it is encrypted private key, run 'openssl rsa -in encrypted.key -out rsa.key'. – Jason Li Aug 14 '20 at 02:18

2 Answers2

3

to do it in .net 6+:

    static X509Certificate2 CreateCertFromPemFile(string certPath, string keyPath)
    {
        if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return X509Certificate2.CreateFromPemFile(certPath, keyPath);

        //workaround for windows issue https://github.com/dotnet/runtime/issues/23749#issuecomment-388231655
        using var cert = X509Certificate2.CreateFromPemFile(certPath, keyPath);
        return new X509Certificate2(cert.Export(X509ContentType.Pkcs12));
    }
eglasius
  • 35,831
  • 5
  • 65
  • 110
1

The following should load PEM-encoded certificate and key files into Kestral:

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(serverOptions =>
            {
                serverOptions.ConfigureHttpsDefaults(listenOptions =>
                {
                    using var privateKey = RSA.Create();
                    privateKey.ImportRSAPrivateKey(
                        PemBytes(pemKeyFile), out var bytesRead);
                    X509Certificate2 certificate = 
                        new X509Certificate2(PemBytes(pemCertFile));
                    listenOptions.ServerCertificate = 
                        certificate.CopyWithPrivateKey(privateKey);
                });
            });
            webBuilder.UseStartup<Startup>();
        });
}

public static byte[] PemBytes(string fileName) => 
    Convert.FromBase64String(
        File.ReadAllLines(fileName)
            .Where(l => !l.Contains('-'))
            .Aggregate("", (current, next) => current + next));
mheyman
  • 4,211
  • 37
  • 34