/// <summary>
        /// Cryptographically signs the specified entity.
        /// </summary>
        /// <remarks>
        /// <para>Signs the entity using the supplied signer, digest algorithm and <see cref="SecureMimeContext"/>.</para>
        /// <para>For better interoperability with other mail clients, you should use
        /// <see cref="MultipartSigned.Create(SecureMimeContext, CmsSigner, MimeEntity)"/>
        /// instead as the multipart/signed format is supported among a much larger
        /// subset of mail client software.</para>
        /// </remarks>
        /// <returns>The signed entity.</returns>
        /// <param name="ctx">The S/MIME context to use for signing.</param>
        /// <param name="signer">The signer.</param>
        /// <param name="digestAlgo">The digest algorithm to use for signing.</param>
        /// <param name="entity">The entity.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="ctx"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="signer"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="entity"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="CertificateNotFoundException">
        /// A signing certificate could not be found for <paramref name="signer"/>.
        /// </exception>
        /// <exception cref="Org.BouncyCastle.Cms.CmsException">
        /// An error occurred in the cryptographic message syntax subsystem.
        /// </exception>
        public static ApplicationPkcs7Mime Sign(SecureMimeContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, MimeEntity entity)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            if (signer == null)
            {
                throw new ArgumentNullException("signer");
            }

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            using (var memory = new MemoryBlockStream()) {
                var options = FormatOptions.GetDefault();
                options.NewLineFormat = NewLineFormat.Dos;

                entity.WriteTo(options, memory);
                memory.Position = 0;

                return(ctx.EncapsulatedSign(signer, digestAlgo, memory));
            }
        }
        /// <summary>
        /// Encrypts the specified entity.
        /// </summary>
        /// <remarks>
        /// Encrypts the entity to the specified recipients using the supplied <see cref="SecureMimeContext"/>.
        /// </remarks>
        /// <returns>The encrypted entity.</returns>
        /// <param name="ctx">The S/MIME context to use for encrypting.</param>
        /// <param name="recipients">The recipients.</param>
        /// <param name="entity">The entity.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="ctx"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="recipients"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="entity"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="System.ArgumentException">
        /// Valid certificates could not be found for one or more of the <paramref name="recipients"/>.
        /// </exception>
        /// <exception cref="CertificateNotFoundException">
        /// A certificate could not be found for one or more of the <paramref name="recipients"/>.
        /// </exception>
        /// <exception cref="Org.BouncyCastle.Cms.CmsException">
        /// An error occurred in the cryptographic message syntax subsystem.
        /// </exception>
        public static ApplicationPkcs7Mime Encrypt(SecureMimeContext ctx, IEnumerable <MailboxAddress> recipients, MimeEntity entity)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            if (recipients == null)
            {
                throw new ArgumentNullException("recipients");
            }

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            using (var memory = new MemoryBlockStream()) {
                var options = FormatOptions.GetDefault();
                options.NewLineFormat = NewLineFormat.Dos;

                entity.WriteTo(options, memory);
                memory.Position = 0;

                return((ApplicationPkcs7Mime)ctx.Encrypt(recipients, memory));
            }
        }
        /// <summary>
        /// Verifies the multipart/signed part.
        /// </summary>
        /// <remarks>
        /// Verifies the multipart/signed part using the supplied cryptography context.
        /// </remarks>
        /// <returns>A signer info collection.</returns>
        /// <param name="ctx">The cryptography context to use for verifying the signature.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <paramref name="ctx"/> is <c>null</c>.
        /// </exception>
        /// <exception cref="System.FormatException">
        /// The multipart is malformed in some way.
        /// </exception>
        /// <exception cref="System.NotSupportedException">
        /// <paramref name="ctx"/> does not support verifying the signature part.
        /// </exception>
        /// <exception cref="Org.BouncyCastle.Cms.CmsException">
        /// An error occurred in the cryptographic message syntax subsystem.
        /// </exception>
        public DigitalSignatureCollection Verify(CryptographyContext ctx)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            var protocol = ContentType.Parameters["protocol"];

            if (string.IsNullOrEmpty(protocol))
            {
                throw new FormatException("The multipart/signed part did not specify a protocol.");
            }

            if (!ctx.Supports(protocol.Trim()))
            {
                throw new NotSupportedException("The specified cryptography context does not support the signature protocol.");
            }

            if (Count < 2)
            {
                throw new FormatException("The multipart/signed part did not contain the expected children.");
            }

            var signature = this[1] as MimePart;

            if (signature == null || signature.ContentObject == null)
            {
                throw new FormatException("The signature part could not be found.");
            }

            var ctype = signature.ContentType;
            var value = string.Format("{0}/{1}", ctype.MediaType, ctype.MediaSubtype);

            if (!ctx.Supports(value))
            {
                throw new NotSupportedException(string.Format("The specified cryptography context does not support '{0}'.", value));
            }

            using (var signatureData = new MemoryBlockStream()) {
                signature.ContentObject.DecodeTo(signatureData);
                signatureData.Position = 0;

                using (var cleartext = new MemoryBlockStream()) {
                    // Note: see rfc2015 or rfc3156, section 5.1
                    var options = FormatOptions.GetDefault();
                    options.NewLineFormat = NewLineFormat.Dos;

                    this[0].WriteTo(options, cleartext);
                    cleartext.Position = 0;

                    return(ctx.Verify(cleartext, signatureData));
                }
            }
        }
        /// <summary>
        /// Creates a new <see cref="MultipartEncrypted"/>.
        /// </summary>
        /// <remarks>
        /// Signs the entity using the supplied signer and digest algorithm and then encrypts to
        /// the specified recipients, encapsulating the result in a new multipart/encrypted part.
        /// </remarks>
        /// <returns>A new <see cref="MultipartEncrypted"/> instance containing
        /// the signed and encrypted version of the specified entity.</returns>
        /// <param name="ctx">The OpenPGP cryptography context to use for signing and encrypting.</param>
        /// <param name="signer">The signer to use to sign the entity.</param>
        /// <param name="digestAlgo">The digest algorithm to use for signing.</param>
        /// <param name="recipients">The recipients for the encrypted entity.</param>
        /// <param name="entity">The entity to sign and encrypt.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="ctx"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="signer"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="recipients"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="entity"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="PrivateKeyNotFoundException">
        /// The private key for <paramref name="signer"/> could not be found.
        /// </exception>
        /// <exception cref="PublicKeyNotFoundException">
        /// A public key for one or more of the <paramref name="recipients"/> could not be found.
        /// </exception>
        /// <exception cref="System.OperationCanceledException">
        /// The user chose to cancel the password prompt.
        /// </exception>
        /// <exception cref="System.UnauthorizedAccessException">
        /// 3 bad attempts were made to unlock the secret key.
        /// </exception>
        public static MultipartEncrypted Create(OpenPgpContext ctx, MailboxAddress signer, DigestAlgorithm digestAlgo, IEnumerable <MailboxAddress> recipients, MimeEntity entity)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            if (signer == null)
            {
                throw new ArgumentNullException("signer");
            }

            if (recipients == null)
            {
                throw new ArgumentNullException("recipients");
            }

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            using (var memory = new MemoryBlockStream()) {
                var options = FormatOptions.GetDefault();
                options.NewLineFormat = NewLineFormat.Dos;

                entity.WriteTo(options, memory);
                memory.Position = 0;

                var encrypted = new MultipartEncrypted();
                encrypted.ContentType.Parameters["protocol"] = ctx.EncryptionProtocol;

                // add the protocol version part
                encrypted.Add(new ApplicationPgpEncrypted());

                // add the encrypted entity as the second part
                encrypted.Add(ctx.SignAndEncrypt(signer, digestAlgo, recipients, memory));

                return(encrypted);
            }
        }
        /// <summary>
        /// Compresses the specified entity.
        /// </summary>
        /// <remarks>
        /// <para>Compresses the specified entity using the specified <see cref="SecureMimeContext"/>.</para>
        /// <para>It should be noted that this feature is not supported by most mail clients,
        /// even among those that support S/MIME.</para>
        /// </remarks>
        /// <returns>The compressed entity.</returns>
        /// <param name="ctx">The S/MIME context to use for compressing.</param>
        /// <param name="entity">The entity.</param>
        /// <exception cref="System.ArgumentNullException">
        /// <para><paramref name="ctx"/> is <c>null</c>.</para>
        /// <para>-or-</para>
        /// <para><paramref name="entity"/> is <c>null</c>.</para>
        /// </exception>
        /// <exception cref="Org.BouncyCastle.Cms.CmsException">
        /// An error occurred in the cryptographic message syntax subsystem.
        /// </exception>
        public static ApplicationPkcs7Mime Compress(SecureMimeContext ctx, MimeEntity entity)
        {
            if (ctx == null)
            {
                throw new ArgumentNullException("ctx");
            }

            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }

            using (var memory = new MemoryBlockStream()) {
                var options = FormatOptions.GetDefault();
                options.NewLineFormat = NewLineFormat.Dos;

                entity.WriteTo(options, memory);
                memory.Position = 0;

                return(ctx.Compress(memory));
            }
        }