Search code examples
c#x509certificate.net-4.7.2

Change issuer name of X509 certificate


There is same question 7 years ago but solved using external library. Now it is possible to change X509Certificate2 Issuer Name without external library?

public static X509Certificate2 Create(string host)
{
    var ecdsa = ECDsa.Create();
    var request = new CertificateRequest($"CN={host}", ecdsa, HashAlgorithmName.SHA256);
    var validFrom = DateTimeOffset.UtcNow;
    var validUntil = DateTimeOffset.UtcNow.AddYears(5);
    var certificate = request.CreateSelfSigned(validFrom, validUntil);
    var certificateBytes = certificate.Export(X509ContentType.Pfx);
    // ??? set issuer ???
    return new X509Certificate2(certificateBytes);
}

Solution

  • @bartojs answer is correct I have to use CertificateRequest.Create.

    I'm creating self signing certificate for my MITM Proxy Server application, so here working code for generating certificate in .Net Framework 4.7.2 without external .dll library like Bouncy Castle or CERTENROLLIB

    public static X509Certificate2 CreateMyCertificate(string host, bool isCA)
    {
        var validFrom = DateTimeOffset.UtcNow;
        var validUntil = DateTimeOffset.UtcNow.AddYears(8);
        X509Certificate2 certificate;
        RSA rsaKey = Program.rootRSA;
    
        if (isCA)
        {
            // create exportable Root private key
            // will be used later as global variable Program.rootRSA
            CspParameters cspParams = new CspParameters
            {
                KeyContainerName = Guid.NewGuid().ToString()
            };
    
            rsaKey = new RSACryptoServiceProvider(2048, cspParams);
        }
    
        // Request a certificate with the common name as the host using the key pair.
        // Common Name (AKA CN) represents the server name protected by the SSL certificate.
        var request = new CertificateRequest(
            $"CN={host}",
            rsaKey,
            HashAlgorithmName.SHA256,
            RSASignaturePadding.Pkcs1
        );
    
        // Add Certificate Extensions
        request.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection
                {
                    new Oid("1.3.6.1.5.5.7.3.1")
                }, true)
        );
    
        if (isCA)
        {
            request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, true, 1, false));
            request.CertificateExtensions.Add(
                new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
            request.CertificateExtensions.Add(
                new X509KeyUsageExtension(X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, false));
    
            certificate = request.CreateSelfSigned(validFrom, validUntil);
        }
        else
        {
            SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
            sanBuilder.AddDnsName(host.Replace("*.", ""));
            request.CertificateExtensions.Add(sanBuilder.Build());
            request.CertificateExtensions.Add(new X509Extension("2.5.29.35", Program.authorityKeyIdentifer, false));
            request.CertificateExtensions.Add(new X509KeyUsageExtension(
                X509KeyUsageFlags.KeyEncipherment, false));
    
            var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var unixTime = Convert.ToInt64((DateTime.UtcNow - epoch).TotalSeconds);
            var serial = BitConverter.GetBytes(unixTime);
    
            certificate = request.Create(Program.rootCert, validFrom, validUntil.AddDays(-5), serial);
            certificate.PrivateKey = Program.rootCert.PrivateKey;
        }
    
        var certificateBytes = certificate.Export(X509ContentType.Pfx, "");
        // return certificate with persisted private key
        return new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.Exportable);
    }
    

    Then use it like:

    1. create global variable and name it like rootCert and rootRSA
    2. on app start check Computer Certificate, if CA not stored generate using
    rootCert = CreateMyCertificate("__MY_ROOT_CERTIFICATE", true);
    
    1. Export/Store rootCert to trusted Root location
    2. later if there are https connect request we can generate the cert using
    CreateMyCertificate("stackoverflow.com", false);
    
    1. when chaining/creating host certificate make sure rootCert is defined, because it will be used in CreateMyCertificate() method, line below
    if (isCA){...}
    else
    {
        certificate = request.Create(Program.rootCert, validFrom, validUntil.AddDays(-5), serial);
    }
    

    note why you shoud not CERTENROLLIB:

    • Slow because Its operation need to read directly from computer certificate
    • RSA generation is also slower than built-in .net 4.7.2
    • To get private key we need first to install the cert the we can export to PFX by doing alot installation to the computer certificate it can spamming your computer cert.