C# Как выполнить S/Mime в Asp.net 5 с надувным замком вместо x509certificate2

#c# #asp.net-core #x509certificate #mailkit #mimekit

Вопрос:

Я успешно использовал приведенный ниже код для отправки электронной почты S/Mime на последнем сервере Windows, но на сервере Windows 2012 это не удалось. Я думаю, что 2012 год не поддерживает более новое шифрование AES.

Как бы я переписал следующий код для замены System.Security.Cryptography.X509Certificates.X509Certificate2() на Надувной замок, также используя MimeKit и Mailkit? Я попытался использовать более старый DES3 для 2012 года, но он не работает с CryptoThrowHelper WindowsCryptographicException: The specified network password is not correct. точно таким же кодом, который отлично работает в Windows 10 на моей машине разработчика. В конечном счете, я хочу, чтобы это работало в контейнере Linux, плюс я думаю, что «Надувной замок» гораздо интереснее печатать и говорить. 🙂

 var message = new MimeMessage(); // Using MimeKit, MailKit 
message.To.Add( new MailboxAddress("John Doe","jdoe@somewhere.com"));
message.From.Add( new MailboxAddress("Jane Doe","jndoe@somewhere.com"));
message.Headers.Add( "AS3-From", "PILM");
message.Headers.Add( "AS3-To", "SARS");
message.Date = DateTimeOffset.Now;
message.MessageId = "42";
message.Subject = "This is a subject";
message.Body = new TextPart("html") { Text = "This is a body" };

using (var context = new TemporarySecureMimeContext())
{   // TODO: Replace Microsoft Cryptography with BouncyCastle
    var cert = new System.Security.Cryptography
        .X509Certificates.X509Certificate2(
        @"c:securitysmime.p12", "VeryCoolPassword",
        System.Security.Cryptography.X509Certificates
            .X509KeyStorageFlags.EphemeralKeySet);

    var recip = new CmsRecipient(cert) {
        EncryptionAlgorithms = new EncryptionAlgorithm[] { 
            EncryptionAlgorithm.TripleDes }
    };
    var recips = new CmsRecipientCollection();
    recips.Add(recip);

    message.Body = ApplicationPkcs7Mime.Encrypt(
        context, recips, message.Body );
};

var client = new SmtpClient();
client.Connect("MySuperEmailServer", 465, true);
client.Authenticate("MySuperUserName", "VeryCoolPassword");
client.Send(message);
client.Disconnect(true);
client.Dispose();
 

Комментарии:

1. I think 2012 does not support newer AES encryption — AES поддерживается с Windows Server 2008. Ваше предположение неверно. Кроме того, ваш фрагмент кода полезен только тогда, когда вы отправляете электронное письмо самому себе.

2. @Crypt32 Два вопроса. Если да, то почему сертификат не загружается в 2012 году, когда он загружается в Win 10? Я протестировал командную строку как администратор с теми же результатами, так что, похоже, это не проблема с разрешением или местоположением. Кроме того, предполагая, что мы добавим базу данных сертификатов для поиска физических лиц, является ли это причиной того, что этот код полезен только для меня?

3. 1) Это просто PFX, который несовместим с Windows Server 2012. Шифрование AES в PFX было добавлено в Windows Server 2012 R2. И это не имеет никакого отношения к S/MIME. AES в S/MIME поддерживается с Windows Server 2008. 2) Я только что указал, что вы загружаете сертификат из PFX, что предполагает, что у вас есть закрытый ключ получателя, что является довольно странным предположением. Вам нужна только общедоступная часть сертификата получателя, чтобы зашифровать электронное письмо.

4. @Crypt32 Спасибо за ваше руководство. Я определенно ищу руководство по этому вопросу, так как знаю только основы S/Mime и шифрования. Я смог получить приведенный выше код только для успешного шифрования на сервере и расшифровки в Microsoft 365 Outlook, но не уверен, правильно ли это. Я предполагаю, что мне нужно сделать эту работу без X509Certificate2 из-за Linux тоже. Я сгенерировал X509 CA и личные файлы, файлы crt и ключей, а также личные файлы csr и p12, где файл p12 был единственным сертификатом, успешно загруженным. Куда идти дальше?

Ответ №1:

По-видимому, в этом вопросе есть несколько частей.

Во-первых, вы хотите знать, как загрузить сертификат S/MIME с помощью BouncyCastle. Поскольку, как указал Crypt32, вам вряд ли понадобится загружать файлы *.pfx (он же *.p12), поскольку у вас не будет их закрытого ключа, вам, скорее всего, потребуется загрузить файл *.cer (или *.crt) :

 using (var stream = File.OpenRead ("smime-recipient-certificate.cer")) {
    var parser = new X509CertificateParser ();

    var certificate = parser.ReadCertificate (stream);
}
 

Класс CmsRecipient MimeKit сделает это за вас, если вы вызовете CmsRecipient .ctor, который принимает аргумент имени файла.

Тем не менее, никогда не помешает знать, как загрузить его вручную, если у вас, возможно, нет его где-то на диске (например, может быть, поток памяти или что-то в этом роде).

CmsRecipient .ctor, который принимает X509Certificate2 (который вы используете в настоящее время), также подходит для использования. MimeKit просто преобразует сертификат X509Certificate2 в внутренний сертификат BouncyCastle.

Тем не менее, основываясь на вашем разговоре с Crypt32, похоже, что проблема, с которой вы столкнулись, связана с загрузкой X509Certificate2 из файла .pfx в Windows Server 2012? В этом случае загрузка сертификата с помощью BouncyCastle, вероятно, является правильным решением.

Если вам нужно или вы хотите знать, как загрузить файл .pfx с помощью BouncyCastle, вы можете сделать это, используя следующую логику:

 // Note: BouncyCastle's X509Certificate class is named X509Certificate, not to be
// confused with System.Security.Cryptography.X509Certificates.X509Certificate.
X509Certificate LoadPfx (Stream stream, string password, out AsymmetricKeyParameter privateKey)
{
    var pkcs12 = new Pkcs12Store (stream, password.ToCharArray ());

    foreach (string alias in pkcs12.Aliases) {
        if (!pkcs12.IsKeyEntry (alias))
            continue;

        var chain = pkcs12.GetCertificateChain (alias);

        if (chain.Length == 0)
            continue;

        var keyEntry = pkcs12.GetKey (alias);

        if (!keyEntry.Key.IsPrivate)
            continue;

        privateKey = keyEntry.Key;

        return chain[0].Certificate;
    }

    throw Exception ("Did not find a certificate with a private key.");
}
 

Примечание: Я бы, вероятно, предложил изменить это:

 message.MessageId = "42";
 

к этому:

 message.MessageId = MimeUtils.GenerateMessageId();
 

Это сгенерирует случайное/уникальное значение идентификатора сообщения с использованием правильного синтаксиса (заголовок идентификатора сообщения-это не число, это xyz@abc.com значение типа).