From 221f83decccc1b52d426a4464e0a38b5b83879b8 Mon Sep 17 00:00:00 2001 From: Jeffrey Stedfast Date: Sat, 13 Jan 2024 09:44:30 -0500 Subject: [PATCH] Moved the MimeMessage.Sign/Encrypt methods into extension methods This is the first step towards splitting crypto out of MimeKit into a separate extension package (e.g. MimeKit.Cryptography). The start of a fix for issue #820 --- .../MimeMessageCryptographyExtensions.cs | 611 ++++++++++++++++++ MimeKit/MimeMessage.cs | 569 +--------------- 2 files changed, 616 insertions(+), 564 deletions(-) create mode 100644 MimeKit/Cryptography/MimeMessageCryptographyExtensions.cs diff --git a/MimeKit/Cryptography/MimeMessageCryptographyExtensions.cs b/MimeKit/Cryptography/MimeMessageCryptographyExtensions.cs new file mode 100644 index 0000000000..effada2594 --- /dev/null +++ b/MimeKit/Cryptography/MimeMessageCryptographyExtensions.cs @@ -0,0 +1,611 @@ +// +// MimeMessageCryptographyExtensions.cs +// +// Author: Jeffrey Stedfast +// +// Copyright (c) 2013-2023 .NET Foundation and Contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Collections.Generic; + +using MimeKit.IO; + +namespace MimeKit.Cryptography { + /// + /// Extension methods for that add convenient S/MIME and DKIM support. + /// + /// + /// Extension methods for that add convenient S/MIME and DKIM support. + /// + public static class MimeMessageCryptographyExtensions + { + static MailboxAddress GetMessageSigner (MimeMessage message) + { + if (message.ResentSender != null) + return message.ResentSender; + + if (message.ResentFrom.Count > 0) + return message.ResentFrom.Mailboxes.FirstOrDefault (); + + if (message.Sender != null) + return message.Sender; + + return message.From.Mailboxes.FirstOrDefault (); + } + + static IList GetEncryptionRecipients (MimeMessage message) + { + return message.GetMailboxes (true, true); + } + + internal static byte[] HashBody (this MimeMessage message, FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) + { + using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { + using (var filtered = new FilteredStream (stream)) { + DkimBodyFilter dkim; + + if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) + dkim = new DkimRelaxedBodyFilter (); + else + dkim = new DkimSimpleBodyFilter (); + + filtered.Add (options.CreateNewLineFilter ()); + filtered.Add (dkim); + + if (message.Body != null) { + try { + message.Body.EnsureNewLine = message.Compliance == RfcComplianceMode.Strict || options.EnsureNewLine; + message.Body.WriteTo (options, filtered, true, CancellationToken.None); + } finally { + message.Body.EnsureNewLine = false; + } + } + + filtered.Flush (); + + if (!dkim.LastWasNewLine) + stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); + } + + return stream.GenerateHash (); + } + } + + /// + /// Sign the message using the specified cryptography context and digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// + /// The message to sign. + /// The cryptography context. + /// The digest algorithm. + /// The cancellation token. + /// + /// is null. + /// + /// + /// The message has not been set. + /// -or- + /// A sender has not been specified. + /// + /// + /// The was out of range. + /// + /// + /// The is not supported. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A signing certificate could not be found for the sender. + /// + /// + /// The private key could not be found for the sender. + /// + public static void Sign (this MimeMessage message, CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var signer = GetMessageSigner (message) ?? throw new InvalidOperationException ("The sender has not been set."); + message.Body = MultipartSigned.Create (ctx, signer, digestAlgo, message.Body, cancellationToken); + } + + /// + /// Asynchronously sign the message using the specified cryptography context and digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// + /// An asynchronous task context. + /// The message to sign. + /// The cryptography context. + /// The digest algorithm. + /// The cancellation token. + /// + /// is null. + /// + /// + /// The message has not been set. + /// -or- + /// A sender has not been specified. + /// + /// + /// The was out of range. + /// + /// + /// The is not supported. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A signing certificate could not be found for the sender. + /// + /// + /// The private key could not be found for the sender. + /// + public static async Task SignAsync (this MimeMessage message, CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var signer = GetMessageSigner (message) ?? throw new InvalidOperationException ("The sender has not been set."); + message.Body = await MultipartSigned.CreateAsync (ctx, signer, digestAlgo, message.Body, cancellationToken).ConfigureAwait (false); + } + + /// + /// Sign the message using the specified cryptography context and the SHA-1 digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// + /// The message to sign. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// The message has not been set. + /// -or- + /// A sender has not been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A signing certificate could not be found for the sender. + /// + /// + /// The private key could not be found for the sender. + /// + public static void Sign (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + Sign (message, ctx, DigestAlgorithm.Sha1, cancellationToken); + } + + /// + /// Asynchronously sign the message using the specified cryptography context and the SHA-1 digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// + /// An asynchronous task context. + /// The message to sign. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// The message has not been set. + /// -or- + /// A sender has not been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A signing certificate could not be found for the sender. + /// + /// + /// The private key could not be found for the sender. + /// + public static Task SignAsync (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + return SignAsync (message, ctx, DigestAlgorithm.Sha1, cancellationToken); + } + + /// + /// Encrypt the message to the sender and all of the recipients + /// using the specified cryptography context. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// The message to encrypt. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The message has not been set. + /// -or- + /// No recipients have been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for one or more of the recipients. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static void Encrypt (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var recipients = GetEncryptionRecipients (message); + if (recipients.Count == 0) + throw new InvalidOperationException ("No recipients have been set."); + + if (ctx is SecureMimeContext smime) { + message.Body = ApplicationPkcs7Mime.Encrypt (smime, recipients, message.Body, cancellationToken); + } else if (ctx is OpenPgpContext pgp) { + message.Body = MultipartEncrypted.Encrypt (pgp, recipients, message.Body, cancellationToken); + } else { + throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); + } + } + + /// + /// Asynchronously encrypt the message to the sender and all of the recipients + /// using the specified cryptography context. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// An asynchronous task context. + /// The message to encrypt. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The message has not been set. + /// -or- + /// No recipients have been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for one or more of the recipients. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static async Task EncryptAsync (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var recipients = GetEncryptionRecipients (message); + if (recipients.Count == 0) + throw new InvalidOperationException ("No recipients have been set."); + + if (ctx is SecureMimeContext smime) { + message.Body = await ApplicationPkcs7Mime.EncryptAsync (smime, recipients, message.Body, cancellationToken).ConfigureAwait (false); + } else if (ctx is OpenPgpContext pgp) { + message.Body = await MultipartEncrypted.EncryptAsync (pgp, recipients, message.Body, cancellationToken).ConfigureAwait (false); + } else { + throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); + } + } + + /// + /// Sign and encrypt the message to the sender and all of the recipients using + /// the specified cryptography context and the specified digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the + /// message will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// The message to sign and encrypt. + /// The cryptography context. + /// The digest algorithm. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The was out of range. + /// + /// + /// The message has not been set. + /// -or- + /// No sender has been specified. + /// -or- + /// No recipients have been specified. + /// + /// + /// The is not supported. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for the signer or one or more of the recipients. + /// + /// + /// The private key could not be found for the sender. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static void SignAndEncrypt (this MimeMessage message, CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var signer = GetMessageSigner (message) ?? throw new InvalidOperationException ("The sender has not been set."); + var recipients = GetEncryptionRecipients (message); + + if (ctx is SecureMimeContext smime) { + message.Body = ApplicationPkcs7Mime.SignAndEncrypt (smime, signer, digestAlgo, recipients, message.Body, cancellationToken); + } else if (ctx is OpenPgpContext pgp) { + message.Body = MultipartEncrypted.SignAndEncrypt (pgp, signer, digestAlgo, recipients, message.Body, cancellationToken); + } else { + throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); + } + } + + /// + /// Asynchronously sign and encrypt the message to the sender and all of the recipients using + /// the specified cryptography context and the specified digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the + /// message will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// An asynchronous task context. + /// The message to sign and encrypt. + /// The cryptography context. + /// The digest algorithm. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The was out of range. + /// + /// + /// The message has not been set. + /// -or- + /// No sender has been specified. + /// -or- + /// No recipients have been specified. + /// + /// + /// The is not supported. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for the signer or one or more of the recipients. + /// + /// + /// The private key could not be found for the sender. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static async Task SignAndEncryptAsync (this MimeMessage message, CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) + { + if (ctx is null) + throw new ArgumentNullException (nameof (ctx)); + + if (message.Body is null) + throw new InvalidOperationException ("No message body has been set."); + + var signer = GetMessageSigner (message) ?? throw new InvalidOperationException ("The sender has not been set."); + var recipients = GetEncryptionRecipients (message); + + if (ctx is SecureMimeContext smime) { + message.Body = await ApplicationPkcs7Mime.SignAndEncryptAsync (smime, signer, digestAlgo, recipients, message.Body, cancellationToken).ConfigureAwait (false); + } else if (ctx is OpenPgpContext pgp) { + message.Body = await MultipartEncrypted.SignAndEncryptAsync (pgp, signer, digestAlgo, recipients, message.Body, cancellationToken).ConfigureAwait (false); + } else { + throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); + } + } + + /// + /// Sign and encrypt the message to the sender and all of the recipients using + /// the specified cryptography context and the SHA-1 digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the + /// message will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// The message to sign and encrypt. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The message has not been set. + /// -or- + /// No sender has been specified. + /// -or- + /// No recipients have been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for the signer or one or more of the recipients. + /// + /// + /// The private key could not be found for the sender. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static void SignAndEncrypt (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + SignAndEncrypt (message, ctx, DigestAlgorithm.Sha1, cancellationToken); + } + + /// + /// Asynchronously sign and encrypt the message to the sender and all of the recipients using + /// the specified cryptography context and the SHA-1 digest algorithm. + /// + /// + /// If either of the Resent-Sender or Resent-From headers are set, then the message + /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) + /// address as the signer address, otherwise the Sender or From address will be + /// used instead. + /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the + /// message will be encrypted to all of the addresses specified in the Resent headers + /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), + /// otherwise the message will be encrypted to all of the addresses specified in + /// the standard address headers (Sender, From, To, Cc, and Bcc). + /// + /// An asynchronous task context. + /// The message to sign and encrypt. + /// The cryptography context. + /// The cancellation token. + /// + /// is null. + /// + /// + /// An unknown type of cryptography context was used. + /// + /// + /// The message has not been set. + /// -or- + /// No sender has been specified. + /// -or- + /// No recipients have been specified. + /// + /// + /// The operation was canceled via the cancellation token. + /// + /// + /// A certificate could not be found for the signer or one or more of the recipients. + /// + /// + /// The private key could not be found for the sender. + /// + /// + /// The public key could not be found for one or more of the recipients. + /// + public static Task SignAndEncryptAsync (this MimeMessage message, CryptographyContext ctx, CancellationToken cancellationToken = default) + { + return SignAndEncryptAsync (message, ctx, DigestAlgorithm.Sha1, cancellationToken); + } + } +} diff --git a/MimeKit/MimeMessage.cs b/MimeKit/MimeMessage.cs index 3254ac9496..001846fb34 100644 --- a/MimeKit/MimeMessage.cs +++ b/MimeKit/MimeMessage.cs @@ -36,10 +36,6 @@ using System.Net.Mail; #endif -#if ENABLE_CRYPTO -using MimeKit.Cryptography; -#endif - using MimeKit.IO; using MimeKit.Text; using MimeKit.Utils; @@ -266,6 +262,10 @@ public MimeMessage () : this (ParserOptions.Default.Clone ()) Dispose (false); } + internal RfcComplianceMode Compliance { + get { return compliance; } + } + /// /// Get or set the mbox marker. /// @@ -1122,7 +1122,7 @@ static void AddMailboxes (List recipients, HashSet uniqu } } - IList GetMailboxes (bool includeSenders, bool onlyUnique) + internal IList GetMailboxes (bool includeSenders, bool onlyUnique) { HashSet unique = onlyUnique ? new HashSet (MimeUtils.OrdinalIgnoreCase) : null; var recipients = new List (); @@ -1697,565 +1697,6 @@ public Task WriteToAsync (string fileName, CancellationToken cancellationToken = return WriteToAsync (FormatOptions.Default, fileName, cancellationToken); } - MailboxAddress GetMessageSigner () - { - if (ResentSender != null) - return ResentSender; - - if (ResentFrom.Count > 0) - return ResentFrom.Mailboxes.FirstOrDefault (); - - if (Sender != null) - return Sender; - - return From.Mailboxes.FirstOrDefault (); - } - - IList GetEncryptionRecipients () - { - return GetMailboxes (true, true); - } - -#if ENABLE_CRYPTO - internal byte[] HashBody (FormatOptions options, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm bodyCanonicalizationAlgorithm, int maxLength) - { - using (var stream = new DkimHashStream (signatureAlgorithm, maxLength)) { - using (var filtered = new FilteredStream (stream)) { - DkimBodyFilter dkim; - - if (bodyCanonicalizationAlgorithm == DkimCanonicalizationAlgorithm.Relaxed) - dkim = new DkimRelaxedBodyFilter (); - else - dkim = new DkimSimpleBodyFilter (); - - filtered.Add (options.CreateNewLineFilter ()); - filtered.Add (dkim); - - if (Body != null) { - try { - Body.EnsureNewLine = compliance == RfcComplianceMode.Strict || options.EnsureNewLine; - Body.WriteTo (options, filtered, true, CancellationToken.None); - } finally { - Body.EnsureNewLine = false; - } - } - - filtered.Flush (); - - if (!dkim.LastWasNewLine) - stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length); - } - - return stream.GenerateHash (); - } - } - - /// - /// Sign the message using the specified cryptography context and digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// The cryptography context. - /// The digest algorithm. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public void Sign (CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner () ?? throw new InvalidOperationException ("The sender has not been set."); - Body = MultipartSigned.Create (ctx, signer, digestAlgo, Body, cancellationToken); - } - - /// - /// Asynchronously sign the message using the specified cryptography context and digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// An asynchronous task context. - /// The cryptography context. - /// The digest algorithm. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// The was out of range. - /// - /// - /// The is not supported. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public async Task SignAsync (CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner () ?? throw new InvalidOperationException ("The sender has not been set."); - Body = await MultipartSigned.CreateAsync (ctx, signer, digestAlgo, Body, cancellationToken).ConfigureAwait (false); - } - - /// - /// Sign the message using the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public void Sign (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - Sign (ctx, DigestAlgorithm.Sha1, cancellationToken); - } - - /// - /// Asynchronously sign the message using the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// - /// An asynchronous task context. - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// The has not been set. - /// -or- - /// A sender has not been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A signing certificate could not be found for the sender. - /// - /// - /// The private key could not be found for the sender. - /// - public Task SignAsync (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - return SignAsync (ctx, DigestAlgorithm.Sha1, cancellationToken); - } - - /// - /// Encrypt the message to the sender and all of the recipients - /// using the specified cryptography context. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for one or more of the recipients. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void Encrypt (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var recipients = GetEncryptionRecipients (); - if (recipients.Count == 0) - throw new InvalidOperationException ("No recipients have been set."); - - if (ctx is SecureMimeContext smime) { - Body = ApplicationPkcs7Mime.Encrypt (smime, recipients, Body, cancellationToken); - } else if (ctx is OpenPgpContext pgp) { - Body = MultipartEncrypted.Encrypt (pgp, recipients, Body, cancellationToken); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Asynchronously encrypt the message to the sender and all of the recipients - /// using the specified cryptography context. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// An asynchronous task context. - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for one or more of the recipients. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public async Task EncryptAsync (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var recipients = GetEncryptionRecipients (); - if (recipients.Count == 0) - throw new InvalidOperationException ("No recipients have been set."); - - if (ctx is SecureMimeContext smime) { - Body = await ApplicationPkcs7Mime.EncryptAsync (smime, recipients, Body, cancellationToken).ConfigureAwait (false); - } else if (ctx is OpenPgpContext pgp) { - Body = await MultipartEncrypted.EncryptAsync (pgp, recipients, Body, cancellationToken).ConfigureAwait (false); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the specified digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// The digest algorithm. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The was out of range. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The is not supported. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void SignAndEncrypt (CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner () ?? throw new InvalidOperationException ("The sender has not been set."); - var recipients = GetEncryptionRecipients (); - - if (ctx is SecureMimeContext smime) { - Body = ApplicationPkcs7Mime.SignAndEncrypt (smime, signer, digestAlgo, recipients, Body, cancellationToken); - } else if (ctx is OpenPgpContext pgp) { - Body = MultipartEncrypted.SignAndEncrypt (pgp, signer, digestAlgo, recipients, Body, cancellationToken); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Asynchronously sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the specified digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// An asynchronous task context. - /// The cryptography context. - /// The digest algorithm. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The was out of range. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The is not supported. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public async Task SignAndEncryptAsync (CryptographyContext ctx, DigestAlgorithm digestAlgo, CancellationToken cancellationToken = default) - { - if (ctx is null) - throw new ArgumentNullException (nameof (ctx)); - - if (Body is null) - throw new InvalidOperationException ("No message body has been set."); - - var signer = GetMessageSigner () ?? throw new InvalidOperationException ("The sender has not been set."); - var recipients = GetEncryptionRecipients (); - - if (ctx is SecureMimeContext smime) { - Body = await ApplicationPkcs7Mime.SignAndEncryptAsync (smime, signer, digestAlgo, recipients, Body, cancellationToken).ConfigureAwait (false); - } else if (ctx is OpenPgpContext pgp) { - Body = await MultipartEncrypted.SignAndEncryptAsync (pgp, signer, digestAlgo, recipients, Body, cancellationToken).ConfigureAwait (false); - } else { - throw new ArgumentException ("Unknown type of cryptography context.", nameof (ctx)); - } - } - - /// - /// Sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public void SignAndEncrypt (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - SignAndEncrypt (ctx, DigestAlgorithm.Sha1, cancellationToken); - } - - /// - /// Asynchronously sign and encrypt the message to the sender and all of the recipients using - /// the specified cryptography context and the SHA-1 digest algorithm. - /// - /// - /// If either of the Resent-Sender or Resent-From headers are set, then the message - /// will be signed using the Resent-Sender (or first mailbox in the Resent-From) - /// address as the signer address, otherwise the Sender or From address will be - /// used instead. - /// Likewise, if either of the Resent-Sender or Resent-From headers are set, then the - /// message will be encrypted to all of the addresses specified in the Resent headers - /// (Resent-Sender, Resent-From, Resent-To, Resent-Cc, and Resent-Bcc), - /// otherwise the message will be encrypted to all of the addresses specified in - /// the standard address headers (Sender, From, To, Cc, and Bcc). - /// - /// An asynchronous task context. - /// The cryptography context. - /// The cancellation token. - /// - /// is null. - /// - /// - /// An unknown type of cryptography context was used. - /// - /// - /// The has not been set. - /// -or- - /// No sender has been specified. - /// -or- - /// No recipients have been specified. - /// - /// - /// The operation was canceled via the cancellation token. - /// - /// - /// A certificate could not be found for the signer or one or more of the recipients. - /// - /// - /// The private key could not be found for the sender. - /// - /// - /// The public key could not be found for one or more of the recipients. - /// - public Task SignAndEncryptAsync (CryptographyContext ctx, CancellationToken cancellationToken = default) - { - return SignAndEncryptAsync (ctx, DigestAlgorithm.Sha1, cancellationToken); - } -#endif // ENABLE_CRYPTO - IEnumerable
MergeHeaders () { int mesgIndex = 0, bodyIndex = 0;