1

I need to digitally sign email using s/mime. I've created a detached signature for my attachment but outlook can't open it: "Cannot open this item. Your Digital ID name cannot be found by the underlying security system". Certificate is installed on this PC and there is smth need to be tweaked in this code:

const int CHARS_IN_LINE = 64;

StringBuilder message = new StringBuilder();
message.AppendLine("Content-Type: multipart/signed; protocol=\"application/pkcs7-signature\";");
message.AppendLine(" boundary=\"__multipart-signed-boundary__\"");
message.AppendLine("Content-Transfer-Encoding: 7bit");
message.AppendLine("MIME-Version: 1.0");
message.AppendLine("Subject: " + tbMessageSubject.Text);
message.AppendLine();
message.AppendLine("--__multipart-signed-boundary__");
message.AppendLine("Content-Type: text/xml; charset=\"windows - 1251\"");
message.AppendLine("Content-Disposition: attachment; ");
message.AppendLine(" filename=\"" + Path.GetFileName(filename) + "\"");
message.AppendLine("Content-Transfer-Encoding: base64");
message.AppendLine();

var dataToSign = File.ReadAllBytes(filePath);

var base64Data = Convert.ToBase64String(dataToSign, Base64FormattingOptions.None);
var base64DataSb = new StringBuilder(base64Data);
for (int i = CHARS_IN_LINE; i < base64DataSb.Length; i += CHARS_IN_LINE + 2) // \r\n
    base64DataSb.Insert(i, "\r\n");

message.AppendLine(base64DataSb.ToString());

message.AppendLine("--__multipart-signed-boundary__");
message.AppendLine("Content-Type: application/pkcs7-signature; name=\"smime.p7m\"");
message.AppendLine("Content-Transfer-Encoding: base64");
message.AppendLine("Content-Disposition: attachment; filename=smime.p7s");
message.AppendLine();

CmsSigner signer = new CmsSigner(clientCert);
SignedCms cms = new SignedCms(new ContentInfo(dataToSign), true);
cms.ComputeSignature(signer);

var signature = cms.Encode();

var base64Signature = Convert.ToBase64String(signature, Base64FormattingOptions.None);
var base64Sb = new StringBuilder(base64Signature);

for (int i = CHARS_IN_LINE; i < base64Sb.Length; i += CHARS_IN_LINE + 2) // \r\n
    base64Sb.Insert(i, "\r\n");

message.AppendLine(base64Sb.ToString());
message.AppendLine();
message.AppendLine("--__multipart-signed-boundary__--");
message.AppendLine(".");

var stream = new MemoryStream(Encoding.ASCII.GetBytes(message.ToString()));

MailMessage mail = new MailMessage();
mail.From = new MailAddress(tbEmailFrom.Text);
mail.To.Add(new MailAddress(tbEmailTo.Text));
AlternateView alternateView = new AlternateView(stream, "application/pkcs7-mime; smime-type=signed-data; name=smime.p7m");
alternateView.TransferEncoding = TransferEncoding.SevenBit;
mail.AlternateViews.Add(alternateView);

SmtpClient client = new SmtpClient(host, port);
client.EnableSsl = chbUseSSL.Checked;
client.Send(mail);
Andrey
  • 65
  • 1
  • 8
  • See following : https://helpdesk.it.helsinki.fi/en/instructions/information-security-and-cloud-services/information-security/smime-encryption – jdweng Nov 25 '20 at 16:07
  • sign/encrypt message via outlook is working fine.but not using my code above... – Andrey Nov 25 '20 at 16:16
  • See following : https://social.msdn.microsoft.com/Forums/en-US/74e4711e-1f66-43a7-9e3b-bc9cfbcd1b73/sending-smime-encrypted-email-using-c?forum=netfxnetcom – jdweng Nov 25 '20 at 16:28
  • thanks, I've already seen this post, it almost equal to mine except some changes in headers – Andrey Nov 25 '20 at 16:32
  • Are you using the same encryption mode that is in the certificate? Are you getting any exception in posted code? Add a try/catch. Does above code work if you send an email with no attachments? – jdweng Nov 25 '20 at 16:40
  • I'm not encrypting, I'm just sign attachment. I'm not getting any exceptions and mail is delivered but cannot be displayed in outlook, I'll try to send without attachment tomorrow. Is seems smth missing in AlternateView or smth else... I believe I just missed a bit but cannot find it – Andrey Nov 25 '20 at 17:28
  • You code looks nothing like the link above. Where are you signing the data? You are missing : byte[] data = Encoding.ASCII.GetBytes(strbody); ContentInfo content = new ContentInfo(data); SignedCms signedCms = new SignedCms(content, false); – jdweng Nov 25 '20 at 17:36
  • I don't need to sign body, just attachment. In my code it was done in this lines: CmsSigner signer = new CmsSigner(clientCert); SignedCms cms = new SignedCms(new ContentInfo(dataToSign), true); cms.ComputeSignature(signer); – Andrey Nov 25 '20 at 17:41
  • Where is the attachment when you are signing? The signing should include the body data. – jdweng Nov 25 '20 at 17:43
  • Attachment is located on the hard drive (xml-file). If I'm not mistaken, I don't need to sign the whole body, just attached file. I'm not 100% sure and can try to sign the whole body – Andrey Nov 25 '20 at 17:46
  • Where are you reading the attachment to get the hash? What signing does is to take a private key from the certificate and the body (and attachments) of the email and using an encryption algorithm creates a hash that is sent with the email. Then receive end verifies the email using same process to make sure the email wasn't modified during transmission. – jdweng Nov 25 '20 at 17:55
  • var dataToSign = File.ReadAllBytes(filePath); Then I'm converting it to base64 string with max 64 chars in line. I believe I should create a detached signature only for the attachment. On the other end attachment should be downloaded and hash verified. – Andrey Nov 25 '20 at 18:01
  • I missed the line at top of message. But you are not signing the body. Look at example the headers are part of the body : string strbody = @"Content-Type: text/plain;charset=""iso-8859-1"" Content-Transfer-Encoding: quoted-printable – jdweng Nov 25 '20 at 18:12
  • thanks, I'll try to sign the body tomorrow (have no token at home) and report back my results – Andrey Nov 25 '20 at 18:15
  • I've signed the body part and still have the issue in outlook. I've tried to save a signed message on hard drive as .eml file and it's opening fine and signature is valid. It seems that smth wrong with AlternateView – Andrey Nov 25 '20 at 20:21
  • My first link step 6 is troubleshooting. Are you still getting the ID error? Step 6 says : The sender has sent an encrypted email (by accident) to your unencrypted address (without the .s at the end). Ask the sender to send it to your correct address. Read the first link completely including everything in the pictures. – jdweng Nov 25 '20 at 21:43
  • I believe it's not my case, my mail is not encrypted, but signed. I've tried to send it using special cryptography app and it's working fine, so smth wrong on sending mail... – Andrey Nov 26 '20 at 05:00
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/225156/discussion-between-user8388835-and-jdweng). – Andrey Nov 26 '20 at 10:53

1 Answers1

0

Finally, the issue was in specified mediaType in AlternateView, the correct way:

AlternateView alternateView = new AlternateView(stream, "multipart/signed; protocol=\"application/pkcs7-signature\"; boundary=\"__multipart-signed-boundary__\"");
alternateView.TransferEncoding = TransferEncoding.SevenBit;

and thanks to jdweng, I was need to sign not the attachment, but the attachment with it's headers (first __multipart-signed-boundary with attachment)

Andrey
  • 65
  • 1
  • 8