75

I need to create a self-signed certificate (for local encryption - it's not used to secure communications), using C#.

I've seen some implementations that use P/Invoke with Crypt32.dll, but they are complicated and it's hard to update the parameters - and I would also like to avoid P/Invoke if at all possible.

I don't need something that is cross platform - running only on Windows is good enough for me.

Ideally, the result would be an X509Certificate2 object that I can use to insert into the Windows certificate store or export to a PFX file.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Guss
  • 24,799
  • 13
  • 87
  • 109
  • For future readers, I have posted my BouncyCastle code at : https://granadacoder.wordpress.com/2016/11/04/service-bus-and-custom-self-signed-certificates-with-a-high-availabilitymultiple-computing-nodes-in-the-farm/ This will create 2 certs. One to be the "Trusted Root" certificate and the second one which is "signed" by the (first) Trusted Root certificate. – granadaCoder Nov 04 '16 at 17:33
  • It's possible to do this without using COM or external dependencies now, see https://stackoverflow.com/questions/48196350. – bartonjs Mar 20 '19 at 15:36
  • In VS2019: Project Properties->Signing->ClickOnce->Create Test Certificate? – Andrew Aug 15 '20 at 23:02
  • 2
    @Andrew the question is how to create this programmatically. There are plenty of ways to create a one-use self-signed certificate, such as using CertUtil or openssl, but the context of the question is building software that generates these certs automatically on a user machine. – Guss Aug 16 '20 at 09:22

7 Answers7

77

This implementation uses the CX509CertificateRequestCertificate COM object (and friends - MSDN doc) from certenroll.dll to create a self signed certificate request and sign it.

The example below is pretty straight forward (if you ignore the bits of COM stuff that goes on here) and there are a few parts of the code that are really optional (such as EKU) which are none-the-less useful and easy to adapt to your use.

public static X509Certificate2 CreateSelfSignedCertificate(string subjectName)
{
    // create DN for subject and issuer
    var dn = new CX500DistinguishedName();
    dn.Encode("CN=" + subjectName, X500NameFlags.XCN_CERT_NAME_STR_NONE);

    // create a new private key for the certificate
    CX509PrivateKey privateKey = new CX509PrivateKey();
    privateKey.ProviderName = "Microsoft Base Cryptographic Provider v1.0";
    privateKey.MachineContext = true;
    privateKey.Length = 2048;
    privateKey.KeySpec = X509KeySpec.XCN_AT_SIGNATURE; // use is not limited
    privateKey.ExportPolicy = X509PrivateKeyExportFlags.XCN_NCRYPT_ALLOW_PLAINTEXT_EXPORT_FLAG;
    privateKey.Create();

    // Use the stronger SHA512 hashing algorithm
    var hashobj = new CObjectId();
    hashobj.InitializeFromAlgorithmName(ObjectIdGroupId.XCN_CRYPT_HASH_ALG_OID_GROUP_ID,
        ObjectIdPublicKeyFlags.XCN_CRYPT_OID_INFO_PUBKEY_ANY, 
        AlgorithmFlags.AlgorithmFlagsNone, "SHA512");

    // add extended key usage if you want - look at MSDN for a list of possible OIDs
    var oid = new CObjectId();
    oid.InitializeFromValue("1.3.6.1.5.5.7.3.1"); // SSL server
    var oidlist = new CObjectIds();
    oidlist.Add(oid);
    var eku = new CX509ExtensionEnhancedKeyUsage();
    eku.InitializeEncode(oidlist); 

    // Create the self signing request
    var cert = new CX509CertificateRequestCertificate();
    cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextMachine, privateKey, "");
    cert.Subject = dn;
    cert.Issuer = dn; // the issuer and the subject are the same
    cert.NotBefore = DateTime.Now;
    // this cert expires immediately. Change to whatever makes sense for you
    cert.NotAfter = DateTime.Now; 
    cert.X509Extensions.Add((CX509Extension)eku); // add the EKU
    cert.HashAlgorithm = hashobj; // Specify the hashing algorithm
    cert.Encode(); // encode the certificate

    // Do the final enrollment process
    var enroll = new CX509Enrollment();
    enroll.InitializeFromRequest(cert); // load the certificate
    enroll.CertificateFriendlyName = subjectName; // Optional: add a friendly name
    string csr = enroll.CreateRequest(); // Output the request in base64
    // and install it back as the response
    enroll.InstallResponse(InstallResponseRestrictionFlags.AllowUntrustedCertificate,
        csr, EncodingType.XCN_CRYPT_STRING_BASE64, ""); // no password
    // output a base64 encoded PKCS#12 so we can import it back to the .Net security classes
    var base64encoded = enroll.CreatePFX("", // no password, this is for internal consumption
        PFXExportOptions.PFXExportChainWithRoot);

    // instantiate the target class with the PKCS#12 data (and the empty password)
    return new System.Security.Cryptography.X509Certificates.X509Certificate2(
        System.Convert.FromBase64String(base64encoded), "", 
        // mark the private key as exportable (this is usually what you want to do)
        System.Security.Cryptography.X509Certificates.X509KeyStorageFlags.Exportable
    );
}

The result can be added to a certificate store using X509Store or exported using the X509Certificate2 methods.

For a fully managed and not tied to Microsoft's platform, and if you're OK with Mono's licensing, then you can look at X509CertificateBuilder from Mono.Security. Mono.Security is standalone from Mono, in that it doesn't need the rest of Mono to run and can be used in any compliant .Net environment (e.g. Microsoft's implementation).

Guss
  • 24,799
  • 13
  • 87
  • 109
  • 3
    You might also point it out that Mono has a fully managed implementation of makecert (powered by Mono.Security as your describe), https://github.com/mono/mono/blob/master/mcs/tools/security/makecert.cs That serves as a better example if someone wants to explore Mono. – Lex Li Apr 19 '13 at 08:51
  • Where can I get the libraries/assemblies for this? – CompanyDroneFromSector7G Oct 23 '13 at 10:39
  • @bukko: Please peruse the links in the answer. – Guss Oct 23 '13 at 11:08
  • @Guss: sorry I'm obviously a bit slow. I can't use Mono, but the Lightswitch link doesn't describe where to get the library(s) unless I missed it, which is very possible... – CompanyDroneFromSector7G Oct 23 '13 at 13:13
  • ...or is CertEnroll.dll included in Windows...? – CompanyDroneFromSector7G Oct 23 '13 at 13:15
  • 5
    If you want to use the code sample I posted above, use it in conjunction with `certenroll.dll` which should be available in your operating system (I think Windows 6.0 and above). – Guss Oct 23 '13 at 13:15
  • 4
    I can't get this to work on Windows 8 :( on the line "enroll.CreateRequest" I get a System.UnauthorizedAccessException ... – Mirek Apr 10 '14 at 14:08
  • I've never used win8, and I'm not familiar with its rather different authorization model (nor do I ever plan to), so I can't help you much in that. Though I'd might check if the cryptographic provider name is OK, or it needs changing for win8. – Guss Apr 10 '14 at 19:10
  • @Motig, you need to run as administrator. – Lex Li Oct 05 '14 at 05:25
  • Meanwhile, SHA512 can be problematic as some services might not support it. Should use SHA1 for better compatibility. – Lex Li Oct 05 '14 at 05:26
  • 2
    Please note that most people consider SHA1 to be a security problem and deprecated. Specifically Google stated that they will rank down web sites that are still using SHA1, with complete exclusion in the near future. Do not use SHA1 unless you have specific backward compatibility concerns that cannot be resolved otherwise. – Guss Oct 05 '14 at 13:12
  • Is it possible to get X509Certificate2 with private key without enrollment proccess? – a.farkas2508 Nov 26 '14 at 12:06
  • @a.farkas2508, I'm not sure what you mean by that - "enrollment" is what Microsoft calls the actual process of creating a certificae by signing a request. This is normally done by a CA service, but the above code does it internally with the private key it just generated and immediately drops the enrollment context - so you can say that this is done "without an enrollment process" because it is done completely internally. – Guss Nov 26 '14 at 17:57
  • this code actually seems to immediately install the certificate into the LocalMachine\My store. Is that normal? How can I have more control over that? – Blub Mar 20 '15 at 14:54
  • @blub: why do you think the certificate is installed in the "my" store? The cert must be installed, because .net doesn't actually holds the private bits in memory (it's not secure) but uses the Windows crypto API, which holds the secret stuff in a certificate store (which is the Right Thing (TM)), but it should be an anonymous store and definetly not the "my" store. – Guss Mar 20 '15 at 14:59
  • Guss, I call your code and then click refresh on my mmc.exe snap in "Certificates (Local Computer) / Personal / Certificates" and it shows up there. That's the MY store! – Blub Mar 20 '15 at 15:03
  • 1
    I put up a version using mono.security at http://www.freekpaans.nl/2015/04/creating-self-signed-x-509-certificates-using-mono-security/ – Freek Apr 08 '15 at 05:57
  • 3
    mono makecert.cs is not secure. It generates only with MD5 and SHA1 .. do not use as-is. Modify the code to use SHA512 before generating a certificate. – Max May 31 '15 at 17:42
  • @John: correct for Mono.Security's as mentioned by Lex Li - but Freek's implementation uses SHA2. – Guss Jun 01 '15 at 09:42
  • Noticed that the answer was edited to correct a mispelled class name `CX500DistinguishedName`, when in fact it is supposed to be CX500 and not X500 in order for the encode method to work and for the object to be assigned to the subject and issuer properties of the CX509CertificateRequestCertificate object. – Hive Nov 22 '16 at 20:25
  • You are correct @Hive, I haven't noticed that change. fixed back. – Guss Nov 23 '16 at 20:42
  • I'm trying to adopt this to be able to store a self signed certificate that is non-exportable and stored in the user store. afterwards.. So all I did was change X509CertificateEnrollmentContext.ContextMachine --> ContextUser and set X509PrivateKeyExportFlags to NONE. I get a "CertEnroll::CX509CertificateRequestCertificate::InitializeFromPrivateKey: The parameter is incorrect. " message if I do. Any help? – Spyral Jan 16 '17 at 16:45
  • 1
    If you get the error: error CS1503: Argument 2: cannot convert from 'CERTENROLLLib.IX509PrivateKey' to 'CERTENROLLLib.CX509PrivateKey' Check out the solution here: http://stackoverflow.com/a/35365099/1100379 – Asher Garland Apr 27 '17 at 17:24
  • how to sign a certificate using bouncy castle, or make CA signed certificates using bouncy castle – crypt Jun 22 '17 at 11:20
  • When running above code getting this exception:System.ArithmeticException HResult=0x80070216 Message=CertEnroll::CX509CertificateRequestCertificate::InitializeFromTemplateName: Arithmetic result exceeded 32 bits. 0x80070216 (WIN32: 534 ERROR_ARITHMETIC_OVERFLOW) Source=Microsoft.Hpc.Scheduler.Store StackTrace: ........ – Deepak Mar 20 '19 at 10:14
  • Hey, I get "This certificate is not valid for code signing" when trying to sign manifest via ClickOnce in VS. It is valid for Assembly signing though. Any reason for it? What should I change to be able to sign manifests? – Justas G Apr 04 '19 at 12:16
  • You probably forgot to add the code signing EKU to your certificate. It is `1.3.6.1.5.5.7.3.3`. See the sample code in the answer as to how to add an EKU. – Guss Apr 04 '19 at 12:20
  • If you get AccessDenied errors, it's because the sample here tries to make a machine certificate, which requires running as Admin. Swap out the line `cn.InitiatlizeFromPrivateKey` to this and it will work `cert.InitializeFromPrivateKey(X509CertificateEnrollmentContext.ContextUser, privateKey, "");` – FoxDeploy Apr 19 '19 at 14:26
77

Since .NET 4.7.2 you can create self-signed certs using System.Security.Cryptography.X509Certificates.CertificateRequest.

For example:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

public class CertificateUtil
{
    static void MakeCert()
    {
        var ecdsa = ECDsa.Create(); // generate asymmetric key pair
        var req = new CertificateRequest("cn=foobar", ecdsa, HashAlgorithmName.SHA256);
        var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));

        // Create PFX (PKCS #12) with private key
        File.WriteAllBytes("c:\\temp\\mycert.pfx", cert.Export(X509ContentType.Pfx, "P@55w0rd"));

        // Create Base 64 encoded CER (public key only)
        File.WriteAllText("c:\\temp\\mycert.cer",
            "-----BEGIN CERTIFICATE-----\r\n"
            + Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
            + "\r\n-----END CERTIFICATE-----");
    }
}
Duncan Smart
  • 27,805
  • 8
  • 60
  • 69
  • 4
    The cert doesn't seem to have a private key associated with it, is there a way to generated? I need to import the cert into the cert store for IIS. – Viertaxa Jan 21 '19 at 21:29
  • 1
    @Viertaxa The PFX contains the private key - import that, not the CER file – Duncan Smart Jan 22 '19 at 17:19
  • 3
    I'm actually getting a null reference exception on cert.PrivateKey.Get(), as if the .CreateSelfSigned is not generating it. Interestingly, the .HasPrivateKey property returns true. – Viertaxa Jan 22 '19 at 18:15
  • 10
    Update: It seems the .Export is critical. Once exporting to a byte array and re-importing to a new X509Certificate2, I was able to use the private key. – Viertaxa Jan 22 '19 at 18:23
  • Unfortunately, this is not available in .Net Standard 2.0. – Greg Vogel Jul 11 '19 at 00:14
  • 1
    @Viertaxa how did exporting an re-importing work for you? For me its throwing, systemnotsupported exception on accessing private key.. {var ecdsa = System.Security.Cryptography.ECDsa.Create(); var req = new CertificateRequest("cn=test", ecdsa, System.Security.Cryptography.HashAlgorithmName.SHA256); X509Certificate2 cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5)); X509Certificate2 cert2 = new X509Certificate2(cert.Export(X509ContentType.Pfx, "P@55w0rd"), "P@55w0rd", X509KeyStorageFlags.PersistKeySet);} – Vivek Baranwal Jan 06 '20 at 18:47
  • @GregVogel CreateSelfSigned function is supported in .Net Standard 2.1. Looks like a good answer – MikeMurko Jun 14 '20 at 18:09
  • 3
    @Viertaxa Can you please post your code to get the private key? – Daniel Jul 10 '20 at 08:50
  • @Viertaxa You can call `cert2.GetECDsaPrivateKey()` to get the private key. – Psddp Sep 09 '20 at 14:42
20

Another option is to use the CLR Security extensions library from CodePlex, which implements a helper function to generate self-signed X.509 certificates:

X509Certificate2 cert = CngKey.CreateSelfSignedCertificate(subjectName);

You can also look at the implementation of that function (in CngKeyExtensionMethods.cs) to see how to create the self-signed certificate explicitly in managed code.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
dthorpe
  • 33,791
  • 3
  • 72
  • 118
  • The CLR security work looks interesting - do you know what is the relationship between this project and Microsoft corporation? The project page seem to assert that it is written by the same team that wrote the standard Security.Cryptography classes, but I didn't see any reference to it other than a couple of blogs. Being a security software package, its important to know if it is subject to the same security review as the official .net release. What also doesn't help my gut feeling is that the last release is for .net 3.5 but the provided functionality is still missing from .net 4.x... – Guss Dec 12 '12 at 19:33
  • 2
    It was written by people who were on the CLR security team at Microsoft. I think the intent was to fold the extensions into a Microsoft .NET release at some point, but I don't think that has happened. You might ping shawnfa via the codeplex site to see where its at. He was my go-to guy with x509 questions when I was at Microsoft. He's really good with this crypto stuff. – dthorpe Dec 12 '12 at 19:52
  • What SHA is this using? – rolls Aug 10 '17 at 05:31
  • How can access to the private key?, when I try access I got an exception. – Fernando Aguilar Nov 05 '19 at 08:00
10

You can use the free PluralSight.Crypto library to simplify programmatic creation of self-signed X.509 certificates:

    using (CryptContext ctx = new CryptContext())
    {
        ctx.Open();

        X509Certificate2 cert = ctx.CreateSelfSignedCertificate(
            new SelfSignedCertProperties
            {
                IsPrivateKeyExportable = true,
                KeyBitLength = 4096,
                Name = new X500DistinguishedName("cn=localhost"),
                ValidFrom = DateTime.Today.AddDays(-1),
                ValidTo = DateTime.Today.AddYears(1),
            });

        X509Certificate2UI.DisplayCertificate(cert);
    }

PluralSight.Crypto requires .NET 3.5 or later.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
dthorpe
  • 33,791
  • 3
  • 72
  • 118
  • 8
    I up voter because its a solution, but please note that using PluralSight is a problem for several reasons: (1) defining it is "free" is problematic - can I use it in my open source project and release its source code under the General Public License? Can I use it in my commercial product and sell software that includes it? For those unfamiliar with software licensing, the answer to these questions is **no**. (2) Internally PluralSight uses P/Invoke, so if you have problems with using P/Invoke (other then not wanting to write it yourself), then you still have a problem. – Guss Dec 11 '12 at 06:16
  • 3
    I've generated certificate using this tool and chrome is complaining that for signature it is using SHA1, which is considered insecure. – Giedrius Feb 03 '16 at 07:46
  • @Guss Every file in the sources has a comment o top: `// This code was written by Keith Brown, and may be freely used.` – Andrzej Gis May 11 '18 at 13:58
  • @gisek - I was referring to a page on PluralSight that offered the library for download (not as part of a sample project like the link in the answer) and the license there was problematic (I don't remember details). I can't find that page now (6 years later), nor can I find any official site for the library, so at this time I can only comment that "may be freely used" is a horrible license due to much of the same reasons that "public domain" doesn't work (see this article for details: https://creativecommons.org/share-your-work/public-domain/cc0/) but its probably good enough for most people. – Guss May 11 '18 at 15:23
4

If it helps anyone else, I needed to generate a test certificate in PEM format (so needed crt and key files), using the answer from Duncan Smart, I produced the following...

        public static void MakeCert(string certFilename, string keyFilename)
        {
            const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
            const string CRT_FOOTER = "\n-----END CERTIFICATE-----";

            const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----\n";
            const string KEY_FOOTER = "\n-----END RSA PRIVATE KEY-----";

            using var rsa = RSA.Create();
            var certRequest = new CertificateRequest("cn=test", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

            // We're just going to create a temporary certificate, that won't be valid for long
            var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));

            // export the private key
            var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);

            File.WriteAllText(keyFilename, KEY_HEADER + privateKey + KEY_FOOTER);

            // Export the certificate
            var exportData = certificate.Export(X509ContentType.Cert);

            var crt = Convert.ToBase64String(exportData, Base64FormattingOptions.InsertLineBreaks);
            File.WriteAllText(certFilename, CRT_HEADER + crt + CRT_FOOTER);
        }
0909EM
  • 3,721
  • 3
  • 20
  • 30
0

Extending 0909EMs answer with SubjectAlternativeNames based on code found here: Understanding self-signed certificates in c#

        public static void MakeCert(string certFilename, string keyFilename)
        {
            const string CRT_HEADER = "-----BEGIN CERTIFICATE-----\n";
            const string CRT_FOOTER = "\n-----END CERTIFICATE-----";

            const string KEY_HEADER = "-----BEGIN RSA PRIVATE KEY-----\n";
            const string KEY_FOOTER = "\n-----END RSA PRIVATE KEY-----";

            using var rsa = RSA.Create();
            var certRequest = new CertificateRequest("cn=test", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

            // Adding SubjectAlternativeNames (SAN)
            var subjectAlternativeNames = new SubjectAlternativeNameBuilder();
            subjectAlternativeNames .AddDnsName("test");
            certRequest.CertificateExtensions.Add(subjectAlternativeNames.Build());

            // We're just going to create a temporary certificate, that won't be valid for long
            var certificate = certRequest.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddDays(1));

            // export the private key
            var privateKey = Convert.ToBase64String(rsa.ExportRSAPrivateKey(), Base64FormattingOptions.InsertLineBreaks);

            File.WriteAllText(keyFilename, KEY_HEADER + privateKey + KEY_FOOTER);

            // Export the certificate
            var exportData = certificate.Export(X509ContentType.Cert);

            var crt = Convert.ToBase64String(exportData, Base64FormattingOptions.InsertLineBreaks);
            File.WriteAllText(certFilename, CRT_HEADER + crt + CRT_FOOTER);
        }

And for definition of the usage of a key using X509KeyUsageExtension look here https://stackoverflow.com/a/48210587/226278

sc911
  • 750
  • 6
  • 17
-2

This is the Powershell version on how to create a certificate. You can use it by executing the command. Check https://technet.microsoft.com/itpro/powershell/windows/pkiclient/new-selfsignedcertificate

Edit: forgot to say that after you create the certificate, you can use the Windows "manage computer certificates" program, to export the certificate to .CER or other type.

Roger Deep
  • 142
  • 1
  • 6