Создание Self-Signed Certificate при помощь С#

ru-RU | создано: 17.06.2020 | опубликовано: 17.06.2020 | обновлено: 31.07.2020 | просмотров за всё время: 1721

Несколько способов создания self-signed сертификата, в том числе и на C#.

Что такое Self-Signed сертификат

Self-Signed сертификат - это сертификат, который вы подписываете своим собственным закрытым ключом. В отличие от этого, внешний общедоступный центр сертификации интернета (CA) подписывает общедоступный сертификат. Вы также можете иметь свой собственный частный центр сертификации, в котором вы можете выдавать частный сертификат.

Для чего Self-Signed сертификат

На самом деле может быть множество вариантов использования Self-Signed сертификата. Один из, это когда у вас есть локальная сеть (или сеть организации) и в этой сети есть сервер сертификации. Хм.. Согласен, не совсем подходяший пример, потому что если нужно для организации, это будет делать системный администратор вашей организации. Если только для тестовых целей, например для подписания IdentityServer4.

Варианты создания

Приведу пару-тройку ссылок где вы можете выбрать способ создания Self-Signed сертификата.

Powershell (пошаговая инструкция)

Powershell 2 (инструкция)

Powershell (документация)

OpenSSL

Создаем Self-Signed сертификат через C#

Я хочу вам предложить еще один варинт, которым я пользуюсь. Первое что нужно создать - это класс конфигурации для генерации:

public class GenerateCertificateOptions
{
    public GenerateCertificateOptions(string pathToSave, string commonName, string fileName, string password, int years = 1)
    {
        CommonName = commonName;
        if (string.IsNullOrEmpty(CommonName))
        {
            throw new ArgumentNullException(nameof(CommonName));
        }

        PathToSave = pathToSave;
        if (string.IsNullOrEmpty(PathToSave))
        {
            throw new ArgumentNullException(nameof(PathToSave));
        }

        Password = password;
        if (string.IsNullOrEmpty(Password))
        {
            throw new ArgumentNullException(nameof(Password));
        }

        CertificateFileName = fileName;
        if (string.IsNullOrEmpty(CertificateFileName))
        {
            throw new ArgumentNullException(nameof(CertificateFileName));
        }

        Years = years;
        if (Years <= 0)
        {
            Years = 1;
        }
    }

    public string CommonName { get; }
    public string PathToSave { get; }
    public string Password { get; }
    public string CertificateFileName { get; }
    public int Years { get; }
}

Пока можете не образщать на этот класс внимания, будет понятнее позже, или точнее из сода следующего метода:

static void MakeCert(GenerateCertificateOptions options)
{
    var rsa = RSA.Create(4096);
    var req = new CertificateRequest($"cn={options.CommonName}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(options.Years));
    var path = Path.Combine(options.PathToSave, options.CertificateFileName);

    // Create PFX (PKCS #12) with private key
    File.WriteAllBytes($"{path}.pfx", cert.Export(X509ContentType.Pfx, options.Password));

    // Create Base 64 encoded CER (public key only)
    File.WriteAllText($"{path}.cer",
        "-----BEGIN CERTIFICATE-----\r\n"
        + Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
        + "\r\n-----END CERTIFICATE-----");
}

Это статичный метод, его можно "завернуть" в nuget-пакет и устанавливать в те приложения, где вам потребуется. Я же, не так часто использую Self-Signed сертификаты, поэтому я просто себе создал файл в LinqPad (и ниже я приведу его целиком).

Вызов метода для генерации Self-Signed сертификата:

var options = new GenerateCertificateOptions
    (
        pathToSave: "d:\\Temp",
        commonName: "IdentityServer4",
        fileName: "IdentityServer4_certificate",
        password: "P@55w0rd",
        5
    );
    MakeCert(options);

Создаем конфигурацию и отправляем ее в метода MakeCert. Всё просто, не правда ли?

Linqpad файл целиком

// enable nullable
void Main()
{
    var options = new GenerateCertificateOptions
    (
        pathToSave: "d:\\Temp",
        commonName: "IdentityServer4",
        fileName: "IdentityServer4_certificate",
        password: "P@55w0rd",
        5
    );
    MakeCert(options);
}

public class GenerateCertificateOptions
{
    public GenerateCertificateOptions(string pathToSave, string commonName, string fileName, string password, int years = 1)
    {
        CommonName = commonName;
        if (string.IsNullOrEmpty(CommonName))
        {
            throw new ArgumentNullException(nameof(CommonName));
        }

        PathToSave = pathToSave;
        if (string.IsNullOrEmpty(PathToSave))
        {
            throw new ArgumentNullException(nameof(PathToSave));
        }

        Password = password;
        if (string.IsNullOrEmpty(Password))
        {
            throw new ArgumentNullException(nameof(Password));
        }

        CertificateFileName = fileName;
        if (string.IsNullOrEmpty(CertificateFileName))
        {
            throw new ArgumentNullException(nameof(CertificateFileName));
        }

        Years = years;
        if (Years <= 0)
        {
            Years = 1;
        }
    }

    public string CommonName { get; }
    public string PathToSave { get; }
    public string Password { get; }
    public string CertificateFileName { get; }
    public int Years { get; }
}

static void MakeCert(GenerateCertificateOptions options)
{
    var rsa = RSA.Create(4096);
    var req = new CertificateRequest($"cn={options.CommonName}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(options.Years));
    var path = Path.Combine(options.PathToSave, options.CertificateFileName);

    // Create PFX (PKCS #12) with private key
    File.WriteAllBytes($"{path}.pfx", cert.Export(X509ContentType.Pfx, options.Password));

    // Create Base 64 encoded CER (public key only)
    File.WriteAllText($"{path}.cer",
        "-----BEGIN CERTIFICATE-----\r\n"
        + Convert.ToBase64String(cert.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)
        + "\r\n-----END CERTIFICATE-----");
}

Вот и всё. Выбирайте удобный для вас способ и "клепайте" свои сертификаты. Но помните, что эти сертификаты будут сертификатами только для вас. :)