38

What I currently do is that I use OpenSSL to generate PFX file. This is causing an unwanted dependency, especially for Windows users. So I found some examples on how to create your own certificate using BouncyCastle, but this library is not .NET Core compatible (or I failed to find the compatible package).

So, is it possible to create your own self signed X509 certificate using just .NET core to avoid dependency on OpenSSL (or any other certificate generating external tool)?

EDIT: It was suggested by someone (editor?) that this SO question How to create a self-signed certificate using C#? provides an answer. Sadly again, this has nothing to do with .NET Core. The accepted answer there starts with This implementation uses the CX509CertificateRequestCertificate COM object (and friends - MSDN doc) from certenroll.dll to create a self signed certificate request and sign it, which is obviously NOT .NET Core ... In fact, none of the answers there is .NET Core compatible.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Wapac
  • 4,058
  • 2
  • 20
  • 33
  • Possible duplicate of [How to create a self-signed certificate using C#?](http://stackoverflow.com/questions/13806299/how-to-create-a-self-signed-certificate-using-c) – AStopher Mar 14 '17 at 13:45
  • Technology isn't there yet - "This mechanism is not yet available on `.NET Core.`" - https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-encryption-at-rest – J. Doe Mar 14 '17 at 14:11

2 Answers2

56

I found this other SO question that put me on the right track. Certificates API was added to .Net Core on 2.0 version. I have a function like the next one to create self signed certificates that I later import into My store to use them on IIS.

    private X509Certificate2 buildSelfSignedServerCertificate()
    {
        SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
        sanBuilder.AddIpAddress(IPAddress.Loopback);
        sanBuilder.AddIpAddress(IPAddress.IPv6Loopback);
        sanBuilder.AddDnsName("localhost");
        sanBuilder.AddDnsName(Environment.MachineName);

        X500DistinguishedName distinguishedName = new X500DistinguishedName($"CN={CertificateName}");

        using (RSA rsa = RSA.Create(2048))
        {
            var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256,RSASignaturePadding.Pkcs1);

            request.CertificateExtensions.Add(
                new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature , false));


            request.CertificateExtensions.Add(
               new X509EnhancedKeyUsageExtension(
                   new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));

            request.CertificateExtensions.Add(sanBuilder.Build());

            var certificate= request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(3650)));
            certificate.FriendlyName = CertificateName;

            return new X509Certificate2(certificate.Export(X509ContentType.Pfx, "WeNeedASaf3rPassword"), "WeNeedASaf3rPassword", X509KeyStorageFlags.MachineKeySet);
        }
    }

If you want the pfx, the Export function on X509Certificate2 should do the trick. It returns a byte array with the raw pfx data.

Monticola Explorator
  • 1,188
  • 13
  • 20
  • 3
    Was looking for this code for quite a while. There are many incorrect versions out there. You can simplify the syntax by replacing `request.CertificateExtensions.Add` with `new Request { CertificateExtensions = { new X509KeyUsageExtension(...), new X509EnhancedKeyUsageExtension(...), sanBuilder.Build() } }` – Adassko Aug 13 '20 at 04:35
  • 1
    To generate PFX file one will write: `var cert = buildSelfSignedServerCertificate(); byte[] certBytes = cert.Export(X509ContentType.Pfx, "PASSWORD"); File.WriteAllBytes("file.pfx", certBytes);` and get `CryptoThrowHelper+WindowsCryptographicException: Key not valid for use in specified state.` To solve this change `X509KeyStorageFlags.MachineKeySet` to `X509KeyStorageFlags.Exportable`. (.net 6) – Remigijus Pankevičius Sep 21 '22 at 18:22
  • This bit changes the length of the trusted chain from 1 to 2 `X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false)`. Any idea why? – matohak Jan 31 '23 at 00:08
-9

The Microsoft way is doing this with makecert and pvk2pfx (from the windows SDK), and not in the .net code itself. Now Im not very familiar with .net core, but since the full blown .net doesn't have native support, it would surprise me very much if the core version does have the function.

here's a description of how to do that: https://msdn.microsoft.com/en-us/library/ff699202.aspx

Edit: Let me rephrase it: Without external dependancies it is not available in .net (core).. There are ways in full blown .net but you need external dll's for that.

g6nuk
  • 1
  • 2
  • 1
    Sorry, but again, this is not very useful. I am able to generate PFX certificates even cross platform using OpenSSL. The question is about how to avoid external dependency - i.e. how to programatically create such a certificate. – Wapac Mar 14 '17 at 14:17
  • See edit; I just gave you a link to the " Microsoft best practise " to show that it wasn't just my hunch, but that it is not the way they designed it. If it was a native function, one would expect that to be their answer and not some SDK tool. Sorry for not being specific enough. – g6nuk Mar 15 '17 at 10:26
  • 1
    That article is probably 5-7 years old (it references Visual Studio 2010 as the latest). In that year, there was no .NET Core. How do you run pvk2pfx on Linux? This is just not the right thing. Even using OpenSSL is much more better than this. – Wapac Mar 15 '17 at 15:59