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 (security):
If you're using .NET 9 or later, consider bumping this to

```suggestion
            var subjectKeyIdentifier = new X509SubjectKeyIdentifierExtension(new PublicKey(key), X509SubjectKeyIdentifierHashAlgorithm.Sha256, critical: false);
```

rather than the implicit/default SHA-1

It isn't _really_ important, but we're supposed to not use SHA-1 directly or indirectly anywhere these days.

(There's no requirement that any particular formula be used, just that if an Authority Key Identifier(keyId) is used, that it matches exactly the Subject Key Identifier for the candidate issuer cert)

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;
        }
    }