0

I need to build a self-signed x509 certificate that is recognized valid on my computer through C# code.

If required, this can be ran with admin privileges.

My current code is the following

public static X509Certificate2 GenerateCertificate(string name)
{
    string subjectName = $"CN={name}";
    using (RSA rsa = RSA.Create(2048))
    {
        CertificateRequest req = new CertificateRequest(
            subjectName,
            rsa,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pkcs1);

        req.CertificateExtensions.Add(
            new X509BasicConstraintsExtension(false, false, 0, false));

        req.CertificateExtensions.Add(
            new X509KeyUsageExtension(
                X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.NonRepudiation,
                false));

        req.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection
                {
                    new Oid("1.3.6.1.5.5.7.3.8")
                },
                true));

        req.CertificateExtensions.Add(
            new X509SubjectKeyIdentifierExtension(req.PublicKey, false));

        return req.CreateSelfSigned(
            DateTimeOffset.UtcNow.AddDays(-1),
            DateTimeOffset.UtcNow.AddYears(50));
    }
}

public static X509Certificate2 GetOrCreateCertificate(string serverName)
{
    using (X509Store store = new X509Store(StoreLocation.LocalMachine))
    {
        X509Certificate2 certificate;
        store.Open(OpenFlags.ReadWrite);
        X509Certificate2Collection certificateCollection = store.Certificates.Find(X509FindType.FindBySubjectName, serverName, true);//With true, my certificates are not returned
        if (certificateCollection.Count > 0)
        {
            certificate = certificateCollection[0];
            return certificate;
        }

        certificate = GenerateCertificate(serverName);
        store.Add(certificate);

        return certificate;
    }
}

Currently, if I go in the windows MMC, certificate snap-in, I see the certificate, but it is considered as invalid.

What did I miss?

EDIT

  • This is based on the question How to create a self-signed certificate using C#? which generate a certificate that is considered as invalid.
  • I want to use .Net classes available in .Net 4.7.2, not bouncyCastle, not com objects or external third party library.
J4N
  • 19,480
  • 39
  • 187
  • 340
  • did you try the below link https://stackoverflow.com/questions/22230745/generate-self-signed-certificate-on-the-fly – Saravanan Feb 07 '19 at 10:18
  • and this one https://stackoverflow.com/questions/13806299/how-to-create-a-self-signed-certificate-using-c – Syntax Error Feb 07 '19 at 10:19
  • Possible duplicate of [How to create a self-signed certificate using C#?](https://stackoverflow.com/questions/13806299/how-to-create-a-self-signed-certificate-using-c) – Ebbelink Feb 07 '19 at 11:15
  • @Madailei I tried, in fact this is the base of my question, but once the certificate is created, it's considered as invalid – J4N Feb 07 '19 at 11:40
  • @SyntaxError Sorry, I want to avoid COM usage – J4N Feb 07 '19 at 11:40
  • @Saravanan I would like to use .Net 4.7.2 which allow me to do this, not bouncycastle. – J4N Feb 07 '19 at 11:41
  • I think you are trying to export a key that is marked as non exportable – Ebbelink Feb 07 '19 at 13:28
  • "Invalid", or "untrusted"? What is it actually saying? – bartonjs Feb 07 '19 at 14:10
  • @bartonjs : `store.Certificates.Find(X509FindType.FindBySubjectName, serverName, true); ` returns nothing while asking it with "false" does – J4N Feb 08 '19 at 07:44

1 Answers1

1

The problem that you're having seems to be that the system doesn't trust the new certificate.

In order to be trusted, the root of a certificate chain must be represented in one of the following stores:

  • LocalMachine\Root
  • LocalMachine\ThirdPartyRoot
  • CurrentUser\Root

(there are also some other stores involved, for domain-managed root authorities)

So after you do

certificate = GenerateCertificate(serverName);
store.Add(certificate);

You will also want to do

using (X509Store rootStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
using (X509Certificate2 withoutPrivateKey = new X509Certificate2(certificate.RawData))
{
    rootStore.Open(OpenFlags.ReadWrite);
    rootStore.Add(withoutPrivateKey);
}

Now the system will be able to verify the (single-node) chain up to a trusted certificate, and the validOnly: true constraint on Find will consider the certificate to be "valid" (which, for that method, means chain-trusted and not expired).

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Any reason why adding this certificate **ONLY** to LocalMachine/Root won't work? – J4N Feb 11 '19 at 07:11
  • @J4N IIS and MMC and probably lots of other software expect that if you have a certificate with a private key and it's in a store it's in either LocalMachine\My or CurrentUser\My; but if you control all the software it should work. So it's just non-standard – bartonjs Feb 11 '19 at 14:25