Использование RSASSA-PSS и RSAES-OAEP с MailKit

Мне приходится обмениваться зашифрованными и подписанными электронными письмами с некоторыми деловыми партнерами. Требуются специальные алгоритмы, такие как:

  • для подписи, RSASSA-PSS в качестве алгоритма подписи,
  • для шифрования, RSAES-OAEP для шифрования ключей и AES-128 CBC для шифрования контента

У меня проблемы с настройкой этого с помощью Mailkit, а на самом деле за ним стоят MailKit и BouncyCastle. Вот где я до сих пор:

Для расшифровки и проверки подписи

Расшифровка тела в порядке, я делаю это с помощью WindowsSecureMimeContext после настройки моего закрытого ключа в магазине Windows.

Проверка подписи не подходит

case MultipartSigned signedBody:
    try
    {
        using (var ctx = new WindowsSecureMimeContext(StoreLocation.LocalMachine))
        {
            var verifiedData = signedBody.Verify(ctx);
            return verifiedData.All(o => o.Verify());
        }
    }
    catch (Exception e)
    {
        throw new Exception("Error during signature verification.", e);
    }

Сертификат отправителя подписан общим ЦС, поэтому я снова использую WindowsSecureMimeContext, но VerifiedData.All(o => o.Verify()) выдает исключение DigitalSignatureVerifyException ("Не удалось проверить цифровую подпись: неизвестная ошибка" -1073700864 ".")

Для подписи и шифрования

Ну, выглядит жестко...

Для подписи мне кажется, что где-то мне нужен PssSigner BouncyCastle, который я могу получить, переопределив DkimSigner, и особенно свойство DigestSigner

class TestSigner : DkimSigner
{
    protected TestSigner(string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) 
        : base(domain, selector, algorithm)
    {
    }

    public TestSigner(AsymmetricKeyParameter key, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256) 
        : base(key, domain, selector, algorithm)
    {
    }

    public TestSigner(string fileName, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
        : base(fileName, domain, selector, algorithm)
    {
    }

    public TestSigner(Stream stream, string domain, string selector, DkimSignatureAlgorithm algorithm = DkimSignatureAlgorithm.RsaSha256)
        : base(stream, domain, selector, algorithm)
    {
    }

    public override ISigner DigestSigner => SignerUtilities.GetSigner(PkcsObjectIdentifiers.IdRsassaPss);
}

Однако я не знаю, где именно его использовать. Возможно, при использовании MimeMessage.Sign(), однако я немного потерял необходимые параметры в подписи метода

Что касается шифрования, я мог бы найти RsaesOaepParameters в библиотеке BouncyCastle, но не могу понять, как его использовать.

Любая помощь эксперта по почте будет высоко оценена!


person Adrien    schedule 22.12.2017    source источник
comment
Ваше исключение проверки выглядит так, как будто оно, вероятно, исходит от System.Security (использовали ли вы WindowsSecureMimeContext?). Если да, попробуйте обновиться до MimeKit/MailKit 2.0, который я вчера выпустил на nuget.org. Я сделал много улучшений в WindowsSecureMimeContext для версии 2.0. Я не уверен, было ли это исключение вызвано ошибкой в ​​MimeKit или просто ошибкой на уровне System.Security).   -  person jstedfast    schedule 23.12.2017
comment
Нет, это не помогло. Отладка показывает, что это вызвано System.Security при проверке подписи SignerInfo здесь. Вместо этого я использовал BouncyCastleSecureMimeContext, который успешно проверяет подпись   -  person Adrien    schedule 27.12.2017
comment
FWIW, я только что реализовал RSAES-OAEP в MimeKit (будет включен в версию 2.5.0 после ее выпуска). Также поддерживается RSASSA-PSS (начиная с MimeKit v2.3.1).   -  person jstedfast    schedule 16.12.2019


Ответы (1)


DkimSigner используется для создания подписей DKIM, что не вам нужно. Подписи DKIM не имеют ничего общего с S/MIME.

Подписание S/MIME с использованием RSASSA-PSS

В настоящее время WindowsSecureMimeContext (который использует System.Security в качестве серверной части) НЕ поддерживает RSASSA. -PSS, поэтому вам нужно будет использовать серверную часть Bouncy Castle.

Чтобы использовать серверную часть Bouncy Castle, вам потребуется использовать один из BouncyCastleSecureMimeContext. производные (или создайте свои собственные). В качестве временного решения для экспериментов я могу предложить использовать TemporarySecureMimeContext. , но для долгосрочного использования я бы посоветовал взглянуть на DefaultSecureMimeContext - хотя вы все равно, вероятно, захотите создать подкласс, чтобы заставить его работать.

Теперь, когда вы используете контекст S/MIME Bouncy Castle, чтобы указать, что вы хотите использовать заполнение RSASSA-PSS, вам нужно будет использовать API, которые принимают CmsSigner, такой как MultipartSigned.Create() или ApplicationPkcs7Mime.Sign( ).

Вот пример фрагмента кода:

var signer = new CmsSigner ("certificate.pfx", "password");

// Specify that we want to use RSASSA-PSS
signer.RsaSignaturePaddingScheme = RsaSignaturePaddingScheme.Pss;

// Sign the message body
var signed = MultipartSigned.Create (ctx, signer, message.Body);

// replace the message body with the signed body
message.Body = signed;

Шифрование S/MIME с использованием AES-128 CBC (или любого другого специального алгоритма) с RSAES-OAEP

Во-первых, для шифрования с помощью S/MIME вам понадобится один из ApplicationPkcs7Mime. Методы Encrypt().[2]

Методы Encrypt(), принимающие MailboxAddress, автоматически создают CmsRecipient и CmsRecipientCollection, выполнив поиск сертификатов на основе предоставленного адреса электронной почты (или если какой-либо из этих почтовых ящиков на самом деле является SecureMailboxAddress, Fingerprint, что полезно, если этот пользователь имеет более 1 сертификата в вашей базе данных или вы хотите быть уверены, что MimeKit выбирает правильный вариант).

Другая вещь, которую MimeKit сделает для вас, когда вы предоставите ему список MailboxAddresses, заключается в том, что он будет искать поддерживаемые алгоритмы шифрования, которые хранятся в базе данных для указанного пользователя.

Для WindowsSecureMimeContext это включает просмотр атрибута S/MIME Capabilities X509 Certificate Extension и декодирование поддерживаемых алгоритмов шифрования. Однако, по моему опыту, во многих случаях это расширение отсутствует в сертификатах X509 в хранилище сертификатов Windows, поэтому MimeKit должен будет предположить, что поддерживается только 3DES CBC.

Для DefaultSecureMimeContext, если вы проверили любое сообщение, подписанное S/MIME, указанным получателем, то сертификат (цепочка) этого пользователя и объявленные алгоритмы шифрования будут храниться в пользовательской базе данных SQL MimeKit (когда вы подписываете сообщение с помощью S/MIME, это справедливо обычной практикой для клиентов является включение атрибута S/MIME Capabilities в данные подписи S/MIME).

Теперь, когда вы понимаете, как это работает, если вы хотите принудительно использовать AES-128 CBC, вы можете сделать это вручную, создав CmsRecipientCollection самостоятельно.

Естественно, это предполагает создание нового CmsRecipient для каждого получателя. Чтобы создать этот класс, вам действительно нужен сертификат X509 для этого получателя.

var recipient = new CmsRecipient (certificate);

Поскольку вы хотите принудительно использовать AES-128 CBC, теперь вам просто нужно переопределить алгоритмы шифрования, которые поддерживает этот получатель:

recipient.EncryptionAlgorithms = new EncryptionAlgorithm[] {
    EncryptionAlgorithm.Aes128
};

(По умолчанию для свойства EncryptionAlgorithms будут установлены алгоритмы, перечисленные в атрибут расширения возможностей S/MIME сертификата (в предпочтительном порядке), если он присутствует, в противном случае он будет содержать только 3DES CBC).

Если вы также хотите принудительно использовать RSAES-OAEP, вам необходимо установить:

recipient.RsaEncryptionPadding = RsaEncryptionPadding.OaepSha1;

Добавьте каждый CmsRecipient к вашему CmsRecipientCollection, а затем передайте его предпочитаемому вами методу Encrypt(), и, к черту, он будет зашифрован с использованием AES-128 CBC.

Примечания:

  1. MultipartSigned.Create() создаст часть MIME multipart/signed, а ApplicationPkcs7Mime.Sign() создаст часть application/pkcs7-mime MIME. Какой из них вы хотите использовать, решать вам, просто имейте в виду, что ваш выбор может повлиять на совместимость с любым клиентом, который используют ваши получатели (я думаю, что большинство клиентов поддерживают обе формы, но вы можете проверить, чтобы убедиться).
  2. Если вы зарегистрировали свой пользовательский класс SecureMimeContext с помощью MimeKit (как кратко описано в README), вы можете свободно использовать различные методы Encrypt/Decrypt/Sign/Verify/и т. д., которые не принимают аргумент контекста криптографии, поскольку MimeKit будет создать экземпляр контекста по умолчанию для вас. В противном случае вам нужно будет передать им контекст.
person jstedfast    schedule 23.12.2017
comment
Привет, jstedfast, спасибо за отличный ответ. Если я правильно понимаю, мне нужно использовать закрытый ключ RSA-PSS (полагаю, OpenSSL подойдет). Для шифрования мне действительно нужно принудительно использовать AES-128, спасибо за трюк. Однако где находится РГАЭС-ОАЭП? - person Adrien; 27.12.2017
comment
В MimeKit невозможно указать алгоритм шифрования ключа, и я понятия не имею, какие алгоритмы по умолчанию используются для System.Security или BouncyCastle. Я также не знаю, как указать их для любой из этих библиотек. Я с радостью приму патчи. - person jstedfast; 27.12.2017
comment
посмотрю, правда не в ближайшие дни - person Adrien; 27.12.2017
comment
Мне удалось выяснить, как сделать RSAES-OAEP с помощью BouncyCastle, поэтому я добавил новое свойство в CmsRecipient, чтобы разрешить установку этой схемы заполнения, но я еще не выпустил выпуск с этой функцией. Когда он будет выпущен, он будет иметь версию MimeKit 2.5.0. - person jstedfast; 15.12.2019