Search code examples
c#.net-corecryptographyx509certificate2ecdsa

Certificate chain X509


Hi I want to generate a certificate chain using c#. Something like this:

certificate chain

I create this code for generation:

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

namespace CC.CertificateCore
{
    public class CertBuilder
    {
        public static CertResult BuildChain()
        {
            CertResult result = new CertResult();

            using ECDsa rootKey = ECDsa.Create(ECCurve.NamedCurves.brainpoolP256t1);

            result.Root = CreateCert(rootKey, "CN=Root CA", "Root");

            using ECDsa aKey = result.Root.GetECDsaPublicKey();

            result.A = CreateCert(aKey, "CN=Root CA", "A");

            using ECDsa bKey = result.A.GetECDsaPublicKey();

            result.B = CreateCert(bKey, "CN=A CA", "B", selfSigned: true);

            return result;
        }

        private static X509Certificate2 CreateCert(ECDsa key, string issuer, string friendlyName, bool selfSigned = false)
        {
            var distinguishedName = new X500DistinguishedName(issuer);

            var request = new CertificateRequest(distinguishedName, key, HashAlgorithmName.MD5);
            request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature, false));

            var certificate = selfSigned 
                ? request.CreateSelfSigned(NotBefore(), NotAfter()) 
                : request.Create(distinguishedName, X509SignatureGenerator.CreateForECDsa(key), NotBefore(), NotAfter(), Serial());
            
            certificate.FriendlyName = friendlyName;
            
            return certificate;
        }

        public static byte[] Serial()
        {
            byte[] serial = new byte[12];

            using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
            {
                rng.GetBytes(serial);
            }

            return serial;
        }

        public static DateTimeOffset NotBefore()
        {
            return new DateTimeOffset(DateTime.UtcNow.AddDays(-1));
        }

        public static DateTimeOffset NotAfter()
        {
            return new DateTimeOffset(DateTime.UtcNow.AddDays(3650));
        }
    }

    public class CertResult
    {
        public X509Certificate2 Root { get; set; }
        public X509Certificate2 A { get; set; }
        public X509Certificate2 B { get; set; }
    }
}

And I getting this error (WindowsCryptographicException: 'Key does not exist.'): error

What Im doing wrong? Is this chain even possible?. The chain is a requirement and I'm implementing a probe of concept to validate if it can be done. The project is a console project netcore 3.1

Thanks in advance,

Regards


Solution

  • using ECDsa aKey = result.Root.GetECDsaPublicKey();
    
    result.A = CreateCert(aKey, "CN=Root CA", "A");
    
    ...
    
     : request.Create(distinguishedName, X509SignatureGenerator.CreateForECDsa(key), NotBefore(), NotAfter(), Serial());
    

    You're trying to sign with a public key. Public keys can't sign. The exception is saying that the private portion of the key is missing.

    Since your code ultimately is using the same key as the subject public key and the signing key, you're trying to create all of your certificates as self-signed. This is reinforced by you using the same distinguished name for the issuer and the subject in each certificate. So there's no chaining happening at all.