2

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);
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
uingtea
  • 6,002
  • 2
  • 26
  • 40

2 Answers2

4

For a self-signed certificate changing the issuer name would just make for a non-verifiable certificate (unless you share a private key with the issuer, which is a bad idea).

For a chain-signed certificate you can use one of the CertificateRequest.Create methods to either provide the issuer certificate (its subject name will be the new certificate's issuer name) or you can supply the issuer name and a signature generator (e.g. X509SignatureGenerator.CreateForECDsa(key)) for full control (including making the aforementioned non-verifiable certificate).

bartonjs
  • 30,352
  • 2
  • 71
  • 111
2

@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.
uingtea
  • 6,002
  • 2
  • 26
  • 40