Repository: dotnet/aspnetcore
File: src/Shared/CertificateGeneration/CertificateManager.cs (C#)

Code before:

    internal abstract CheckCertificateStateResult CheckCertificateState(X509Certificate2 candidate);

    internal abstract void CorrectCertificateState(X509Certificate2 candidate);

    internal static X509Certificate2 CreateSelfSignedCertificate(
        X500DistinguishedName subject,
        IEnumerable<X509Extension> extensions,
        DateTimeOffset notBefore,
        DateTimeOffset notAfter)
    {
        using var key = CreateKeyMaterial(RSAMinimumKeySizeInBits);

        var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        foreach (var extension in extensions)
        {
            request.CertificateExtensions.Add(extension);
        }

        // Only add the SKI and AKI extensions if neither is already present.
        // OpenSSL needs these to correctly identify the trust chain for a private key. If multiple certificates don't have a subject key identifier and share the same subject,
        // the wrong certificate can be chosen for the trust chain, leading to validation errors.
        if (!request.CertificateExtensions.OfType<X509SubjectKeyIdentifierExtension>().Any() && !request.CertificateExtensions.OfType<X509AuthorityKeyIdentifierExtension>().Any())
        {
            // RFC 5280 section 4.2.1.2
            var subjectKeyIdentifier = new X509SubjectKeyIdentifierExtension(new PublicKey(key), critical: false);
            // RFC 5280 section 4.2.1.1
            var authorityKeyIdentifier = X509AuthorityKeyIdentifierExtension.CreateFromSubjectKeyIdentifier(subjectKeyIdentifier);

            request.CertificateExtensions.Add(subjectKeyIdentifier);
            request.CertificateExtensions.Add(authorityKeyIdentifier);
        }

        var result = request.CreateSelfSigned(notBefore, notAfter);
        return result;

        static RSA CreateKeyMaterial(int minimumKeySize)
        {
            var rsa = RSA.Create(minimumKeySize);
            if (rsa.KeySize < minimumKeySize)
            {
                throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits");
            }

            return rsa;
        }
    }

    internal static void DisposeCertificates(IEnumerable<X509Certificate2> disposables)
    {
        foreach (var disposable in disposables)

Review comment (suggestion):
Oh, and you can just get the public key from `request.PublicKey`, you don't need to do `new PublicKey(key)`.  (IIRC, Subject Key Identifier was my main driver for exposing that :smile:)

Code after:
                throw new InvalidOperationException("Invalid location.");
        }
    }

    internal abstract CheckCertificateStateResult CheckCertificateState(X509Certificate2 candidate);

    internal abstract void CorrectCertificateState(X509Certificate2 candidate);

    internal static X509Certificate2 CreateSelfSignedCertificate(
        X500DistinguishedName subject,
        IEnumerable<X509Extension> extensions,
        DateTimeOffset notBefore,
        DateTimeOffset notAfter)
    {
        using var key = CreateKeyMaterial(RSAMinimumKeySizeInBits);

        var request = new CertificateRequest(subject, key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        foreach (var extension in extensions)
        {
            request.CertificateExtensions.Add(extension);
        }

        // Only add the SKI and AKI extensions if neither is already present.
        // OpenSSL needs these to correctly identify the trust chain for a private key. If multiple certificates don't have a subject key identifier and share the same subject,
        // the wrong certificate can be chosen for the trust chain, leading to validation errors.
        if (!request.CertificateExtensions.Any(ext => ext.Oid?.Value is SubjectKeyIdentifierOid or AuthorityKeyIdentifierOid))
        {
            // RFC 5280 section 4.2.1.2
            var subjectKeyIdentifier = new X509SubjectKeyIdentifierExtension(request.PublicKey, X509SubjectKeyIdentifierHashAlgorithm.Sha256, critical: false);
            // RFC 5280 section 4.2.1.1
            var authorityKeyIdentifier = X509AuthorityKeyIdentifierExtension.CreateFromSubjectKeyIdentifier(subjectKeyIdentifier);

            request.CertificateExtensions.Add(subjectKeyIdentifier);
            request.CertificateExtensions.Add(authorityKeyIdentifier);
        }

        var result = request.CreateSelfSigned(notBefore, notAfter);
        return result;

        static RSA CreateKeyMaterial(int minimumKeySize)
        {
            var rsa = RSA.Create(minimumKeySize);
            if (rsa.KeySize < minimumKeySize)
            {
                throw new InvalidOperationException($"Failed to create a key with a size of {minimumKeySize} bits");
            }

            return rsa;
        }
    }