0
votes

Very new to signing and encrypting file. Found MIMEKIT and code on stackoverflow. With little or no understanding of how the code work have made changes to it. when opening the created file in a package call p7mViewer a message The file is corrupted; Signature information might be missing. Service provider is unable to decrypt file generated suing the below code.

I need to sign with SHA-256 and encrypt using DES-3. Please see code below.

using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.X509;
using MimeKit;
using System.Text;

namespace ConsoleApplicationSignWithBouncyCastle
{
class Program
{

    [STAThread]
    static void Main(string[] args)
    {

        try
        {
            // First load a Certificate, filename/path and certificate password
            Cert = ReadCertFromFile("c:/temp1/cert.pfx", "password");

            //  Select a binary file
            var dialog = new OpenFileDialog
            {
                Filter = "All files (*.*)|*.*",
                InitialDirectory = "./",
                Title = "Select a text file"
            };
            var filename = (dialog.ShowDialog() == DialogResult.OK) ? dialog.FileName : null;

            // Get the file
          byte[] fileContent = File.ReadAllBytes( filename);

            // Create the generator
            var dataGenerator = new CmsEnvelopedDataStreamGenerator();

            // Add receiver
            // Cert is the user's X.509 Certificate set bellow
             dataGenerator.AddKeyTransRecipient(Cert);

            // Make the output stream 
            int startFileName = filename.LastIndexOf(@"\") +1;
            string outFile = @"c:\temp1\"+ filename.Substring(startFileName, filename.LastIndexOf(".") - startFileName) + "_w_2.edi";
            var outStream = new FileStream(outFile, FileMode.Create);

            string Streamline = "";
            byte[] StreamBytes = null;
            byte[] newline = Encoding.ASCII.GetBytes(Environment.NewLine);

            Streamline = "MIME-Version: 1.0";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes , 0,Streamline.Length  );
            outStream.Write(newline, 0, newline.Length );

            Streamline = "Date: " + DateTime.Now.ToString("ddd, dd MMM yyyy HH:mm:ss") + " +0000";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "X-Priority: 3 (Normal)";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "Message-ID: <10000003141>";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "Content-Disposition: attachment; filename="+@"""smime.p7m""";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "Content-Transfer-Encoding: binary";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "AS3-From: PILM";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "AS3-To: SARS";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "Subject: "+ filename.Substring(startFileName, filename.LastIndexOf(".") - startFileName) ;
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);

            Streamline = "Content-Type: application/pkcs7-mime; smime-type="+@"""enveloped - data"""+ ";name="+@"""smime.p7m""";
            StreamBytes = Encoding.ASCII.GetBytes(Streamline);
            outStream.Write(StreamBytes, 0, Streamline.Length);
            outStream.Write(newline, 0, newline.Length);
            outStream.Write(newline, 0, newline.Length);

            // Sign the stream
            var cryptoStream = dataGenerator.Open(outStream, CmsEnvelopedGenerator.DesEde3Cbc  );
            // Store in our binary stream writer and write the signed content
            var binWriter = new BinaryWriter(cryptoStream);
            binWriter.Write(fileContent);



        }
        catch (Exception ex)
        {
            Console.WriteLine("So, you wanna make an exception huh! : " + ex.ToString());
            Console.ReadKey();
        }
    }

    public static Org.BouncyCastle.X509.X509Certificate Cert { get; set; }

    // This reads a certificate from a file.
    // Thanks to: http://blog.softwarecodehelp.com/2009/06/23/CodeForRetrievePublicKeyFromCertificateAndEncryptUsingCertificatePublicKeyForBothJavaC.aspx
    public static Org.BouncyCastle.X509.X509Certificate ReadCertFromFile(string strCertificatePath, string strCertificatePassword)
    {
        try
        {

            X509Certificate2 myCert =  new X509Certificate2(strCertificatePath, strCertificatePassword);
            var cert = Org.BouncyCastle.Security.DotNetUtilities.FromX509Certificate(myCert);

            return cert;
        }
        catch (Exception ex)
        {
            Console.WriteLine("So, you wanna make an exception huh! : " + ex.ToString());
            Console.ReadKey();
            return null;
        }
    }


}

}

1
Where's the MimeKit code? It seems like you are completely bypassing the use of MimeKit in order to try and generate MIME manually.jstedfast

1 Answers

2
votes

If you used MimeKit instead of trying to generate your own MIME manually, you could do something like this:

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;

using MimeKit;
using MimeKit.Cryptography;

namespace ConsoleApplicationSignWithBouncyCastle
{
    class Program
    {
        [STAThread]
        static void Main (string[] args)
        {
            //  Select a binary file
            var dialog = new OpenFileDialog {
                Filter = "All files (*.*)|*.*",
                InitialDirectory = "./",
                Title = "Select a text file"
            };
            var filename = (dialog.ShowDialog () == DialogResult.OK) ? dialog.FileName : null;
            var certificate2 = new X509Certificate2 ("c:/temp1/cert.pfx", "password");
            MimeEntity body;

            using (var content = new MemoryStream (File.ReadAllBytes (filename)))
                var part = new MimePart (MimeTypes.GetMimeType (filename)) {
                    ContentDisposition = new ContentDisposition (ContentDisposition.Attachment),
                    ContentTransferEncoding = ContentEncoding.Binary,
                    FileName = Path.GetFileName (filename),
                    Content = new MimeContent (content)
                };

                var recipient = new CmsRecipient (certificate2) {
                    EncryptionAlgorithms = new EncryptionAlgorithm[] { EncryptionAlgorithm.TripleDes }
                };
                var recipients = new CmsRecipientCollection ();
                recipients.Add (recipient);

                using (var ctx = new TemporarySecureMimeContext ())
                    body = ApplicationPkcs7Mime.Encrypt (ctx, recipients, part);
            }

            var message = new MimeMessage ();
            message.Headers.Add ("AS3-From", "PILM");
            message.Headers.Add ("AS3-To", "SARS");
            message.Subject = Path.GetFileNameWithoutExtension (filename);
            message.Date = DateTimeOffset.Now;
            message.MessageId = "10000003141";
            message.Body = body;

            var outFile = @"c:\temp1\" + Path.GetFileNameWithoutExtension (filename) + "_w_2.edi";
            message.WriteTo (outFile);
        }
    }
}

Notice how much simpler the above code snippet is than your original code snippet. The difference is night and day :)

If you also want to sign using SHA-256, you could do this instead (very minor differences):

using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;

using MimeKit;
using MimeKit.Cryptography;

namespace ConsoleApplicationSignWithBouncyCastle
{
    class Program
    {
        [STAThread]
        static void Main (string[] args)
        {
            //  Select a binary file
            var dialog = new OpenFileDialog {
                Filter = "All files (*.*)|*.*",
                InitialDirectory = "./",
                Title = "Select a text file"
            };
            var filename = (dialog.ShowDialog () == DialogResult.OK) ? dialog.FileName : null;
            var certificate2 = new X509Certificate2 ("c:/temp1/cert.pfx", "password");
            MimeEntity body;

            using (var content = new MemoryStream (File.ReadAllBytes (filename)))
                var part = new MimePart (MimeTypes.GetMimeType (filename)) {
                    ContentDisposition = new ContentDisposition (ContentDisposition.Attachment),
                    ContentTransferEncoding = ContentEncoding.Binary,
                    FileName = Path.GetFileName (filename),
                    Content = new MimeContent (content)
                };

                var recipient = new CmsRecipient (certificate2) {
                    EncryptionAlgorithms = new EncryptionAlgorithm[] { EncryptionAlgorithm.TripleDes }
                };
                var recipients = new CmsRecipientCollection ();
                recipients.Add (recipient);

                var signer = new CmsSigner (certificate2) {
                    DigestAlgorithm = DigestAlgorithm.Sha256
                };

                using (var ctx = new TemporarySecureMimeContext ()) {
                    using (var stream = File.OpenRead ("c:/temp1/cert.pfx"))
                        ctx.Import (stream, "password");
                    body = ApplicationPkcs7Mime.SignAndEncrypt (ctx, signer, recipients, part);
                }
            }

            var message = new MimeMessage ();
            message.Headers.Add ("AS3-From", "PILM");
            message.Headers.Add ("AS3-To", "SARS");
            message.Subject = Path.GetFileNameWithoutExtension (filename);
            message.Date = DateTimeOffset.Now;
            message.MessageId = "10000003141";
            message.Body = body;

            var outFile = @"c:\temp1\" + Path.GetFileNameWithoutExtension (filename) + "_w_2.edi";
            message.WriteTo (outFile);
        }
    }
}