Domanda

I have to send an EDI message to a government body, which is signed/encrypted in a particular way.

According to https://docs.google.com/document/d/1xOxsZG7nCXdd3ucFKJObheW4G6kFwflGFkzURS_haTY/edit?usp=sharing

I am trying this code, but the encrypted S/MIME isn’t correctly formatted according to the government gateway.

Email reply from them:

The Error Code I am getting is a decryption failure.
You should have signed your EDI message using your Gatekeeper Certificate first. This produces an S/MIME blob. We call this the “signed” S/MIME .
Then, you take your signed blob and encrypt it using the Customs Gateway Certificate downloaded from our cargo web site.
This produces another S/MIME, we call the “encrypted” S/MIME.

I am signing and encrypting using the correct encryption certificates.

Have also tried 3rd party libraries ActiveUp and Chilkat to no avail so far.

Any help in interpreting the Customs Spec and adjusting where I might have gone wrong much appreciated. I have been working on this issue for more than a week.

    public static void SendEmail(string ediMsg, string clientCertificatePath,
        string clientCertificatePassword, string sender, string receiver, string subject, SmtpClient smtp,
        string customsCertificatePath)
    {
        //Load the certificate
        X509Certificate2 EncryptCert = new X509Certificate2(customsCertificatePath);

        X509Certificate2 SignCert =
           new X509Certificate2(clientCertificatePath, clientCertificatePassword);

        //Build the body into a string
        StringBuilder Message = new StringBuilder();

        ediMsg = "UNB+IATB:1+6XPPC+LHPPC+940101:0950+1' ...";

        /*The EDI document is first formatted as a MIME message [MIME], 
         * as the EDI Document may contain special characters, non-printable ASCII and binary data. */

        byte[] arrayToEncode = System.Text.Encoding.UTF8.GetBytes(ediMsg);
        ediMsg = Convert.ToBase64String(arrayToEncode);

        /*Within the MIME message, the Content-Transfer-Encoding header must be either “quoted-printable” 
         * or “base64”, and the Content-Type header should be set to “Application/EDIFACT”. */
        Message.AppendLine("Content-Type: Application/EDIFACT");
        Message.AppendLine("Content-Transfer-Encoding: base64");

        //The file name of the attachment for inbound e-mails (to Customs) must be the same as the Subject Line
        //(section 3.3) with the .edi suffix.
        Message.AppendLine("Content-Disposition: attachment; filename=\"" + subject + ".edi\"");

        /*I have tried this with 
         * (a) the raw ediMsg, 
         * (b) the base64 version of (a)
         * (c) quoted-printable version of (a)
         * (d) base64 version of (c)
         */ 
        Message.AppendLine(ediMsg);

        //Text must not be included in the body of the e-mail. EDI documents must be sent as an attachment.

        //Convert the body to bytes
        byte[] BodyBytes = Encoding.UTF8.GetBytes(Message.ToString());

        //sign
        var signedBytes = SignMsg(BodyBytes, SignCert);

        //Build the e-mail body bytes into a secure envelope
        EnvelopedCms Envelope = new EnvelopedCms(new ContentInfo(signedBytes));
        CmsRecipient Recipient = new CmsRecipient(
            SubjectIdentifierType.IssuerAndSerialNumber, EncryptCert);
        Envelope.Encrypt(Recipient);
        byte[] EncryptedBytes = Envelope.Encode();

        //Create the mail message
        MailMessage Msg = new MailMessage();
        Msg.To.Add(new MailAddress(receiver));
        Msg.From = new MailAddress(sender);
        Msg.Subject = subject;

        //Attach the encrypted body to the email as and ALTERNATE VIEW
        MemoryStream ms = new MemoryStream(EncryptedBytes);
        AlternateView av =
            new AlternateView(ms,
            "application/pkcs7-mime; smime-type=signed-data;name=smime.p7m");
        Msg.AlternateViews.Add(av);

        //SmtpClient smtp = new SmtpClient(MailServer, 25);
        //send the email                                       
        smtp.Send(Msg);
    }
È stato utile?

Soluzione

I'm not sure the issues I'm about to point out are the problem, but they might be worth looking into...

First, Convert.ToBase64String(arrayToEncode); does not wrap lines as needed in MIME. What you'll need to use is this variant with Base64FormattingOptions.InsertLineBreaks.

Secondly, I don't know what SignMsg() does, but make sure that you prepend the proper Content-Type, Content-Transfer-Encoding, and (possibly) Content-Disposition headers as well. The Content-Type should be application/pkcs7-mime; smime-type=signed-data; name=smime.p7s and Content-Transfer-Encoding should be base64 once you've base64 encoded the data.

Thirdly, the Content-Type header you gave to the encrypted outer part is wrong. It should be application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m.

Fourthly, Make sure that the encrypted data gets base64 encoded and that the AlternativeView gets a Content-Transfer-Encoding of base64.

I'm not sure if adding it as an alternative view will necessarily work or not, I'd have to see the generated MIME to be sure.

Something you might consider using instead of IP*Works which is payware is my Open Source library called MimeKit which already supports generating S/MIME messages. I also have a library called MailKit which supports SMTP which you seem to be using.

Both libraries are easily available via NuGet: MimeKit and MailKit

What you would do is something like this:

// Note: if the email addresses do not match the certificates, you can
// use a SecureMailboxAddress instead, which allows you to specify the
// Fingerprint (aka Thumbprint) of the certificate to use for signing
// or encrypting.
var recipient = new MailboxAddress ("Receiver Name", "receiver@example.com");
var sender = new MailboxAddress ("Sender Name", "sender@example.com");

var message = new MimeMessage ();
message.To.Add (recipient);
message.From.Add (sender);
message.Subject = subject;

// create the application/edifact MIME part
var edifact = new MimePart ("application", "edifact");

// set the filename of the MIME part (adds a Content-Disposition header
// if not already present)
edifact.FileName = subject + ".edi";

// create the content stream of the MIME part
var content = new MemoryStream (Encoding.UTF8.GetBytes (ediMsg), false);

// set the content of the MIME part (we use ContentEncoding.Default because
// it is not encoded... yet)
edifact.ContentObject = new ContentObject (content, ContentEncoding.Default);

// encode the content using base64 *and* set the Content-Transfer-Encoding header
edifact.ContentTransferEncoding = ContentEncoding.Base64;

using (var ctx = new TemporarySecureMimeContext ()) {
    ctx.Import (clientCertificatePath, clientCertificatePassword);
    ctx.Import (customsCertificatePath);

    // sign and then encrypt the edifact part and then set the result as the
    // message body.
    message.Body = ApplicationPkcs7Mime.SignAndEncrypt (ctx, sender,
        DigestAlgorithm.Sha1, new [] { recipient }, edifact);
}

// MailKit's SMTP API is very similar to System.Net.Mail's SmtpClient API,
// so that shouldn't pose a problem.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top