问题
I was required to send signed and encrypted mails to our customers, however, this is my first time with the fight of sign and encrypt (I want to highlight this point).
I have tried with OpaqueMail, and MimeKit. Because I really don't understand deeply OpaqueMail and I have my own clients to retrieve emails, I found far better to understand and implement MimeKit.
I know it is a rudimentary implementation what I did in the following lines, but it is just the first contact with it and just a test. I can send signed emails with encrypted body, the problem come with the attachments (we just sent empty bodies with the attachment file, that comes from a DB).
try
{
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection collection = store.Certificates.Find(X509FindType.FindBySubjectName, "senderEmail@something.com", false); //TODO Change to true after test
X509Certificate2 senderCertificate = collection[0];
store = new X509Store(StoreName.AddressBook, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
collection = store.Certificates.Find(X509FindType.FindBySubjectName, "recipientEmail@something.com", false); //TODO Change to true after test
X509Certificate2 recipientCertificate = collection[0];
MimeMessage mimeMessage = new MimeMessage
{
Date = DateTime.Now,
};
mimeMessage.From.Add(
new SecureMailboxAddress(
"senderEmail@gmail.com",
"senderEmail@gmail.com",
senderCertificate.Thumbprint));
mimeMessage.To.Add(
new SecureMailboxAddress(
"recipientEmail@gmail.com",
"recipientEmail@gmail.com",
recipientCertificate.Thumbprint));
mimeMessage.Subject = "S/MIME Test";
using (Stream stream = "TestAttachmentFile".ToStream())
{
//Attachment
MimePart attachment = new MimePart(new ContentType("text", "plain"))
{
ContentTransferEncoding =
ContentEncoding.Base64,
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment),
FileName = "TestAttachmentFileName.txt",
ContentObject = new ContentObject(stream)
};
Multipart multipart = new Multipart("mixed") { attachment};
mimeMessage.Body = multipart;
//Sign / Encryption
CmsSigner signer = new CmsSigner(senderCertificate);
CmsRecipientCollection colle = new CmsRecipientCollection();
X509Certificate bountyRecipientCertificate = DotNetUtilities.FromX509Certificate (recipientCertificate)
CmsRecipient recipient = new CmsRecipient(bountyRecipientCertificate);
colle.Add(recipient);
using (var ctx = new MySecureMimeContext ())
{
var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);
}
//Sending
using (SmtpClient smtpClient = InitSmtpClient(
"mail.smtp.com",
465,
"sender@something.com",
"Pwd",
true))
{
smtpClient.Send(mimeMessage);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
Well here the questions:
Sign and body encrypt works. But when I try to add an attachment I can open it but always appear the BOM () I can see the attachment however thunderbird doesn't tell me it is an attachment, it's just like the body of the email. I don't know if it is a problem from the ToStream() I have implemented. Also Thunderbird was not able to show correct german characters (ÜüÖöÄäß) neither for spanish ñ
EDIT MimeKit.Decryption methods works pretty well too, and also I get the correct encoding of the message without BOM and the attachment is there. It could be a problem for Thunderbird clients.
Related to SecureMimeContext, we are using HanaDB and we want to store the certificates there, retrieve and use them, but I was not able to find the proper conversion for IX509CertificateDatabase, so, using WindowsStore for the moment.
EDIT, I solve the problem with the DB creating a WindowsSecureMimeContext and overriding the import to get the certificates from the DB. Quick and dirty.
EDIT 2, This was hard to implement, because our implementation with DAO templates, I've made subclass from SecureMimeContext, I look on the WindowsSecureMimeContext to understand what the methods exactly does and just change the code to fit with our DAO stuff.
How can I convert from X509Certificate2 to X509Certificate(BouncyCastle) as is the parameter CmsRecipient?
EDIT, DotNetUtilities.FromX509Certificate did the job.
Is it possible to make a "Tripple Wrap"? Sign, Encrypt, Sign again.
EDIT, Yes
using (var ctx = new MySecureMimeContext ()) {
var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
mimeMessage.Body = MultipartSigned.Create (ctx, signer, encrypted);
}
回答1:
It sounds like you're most of the way there now that you figured out to use DotNetUtilities.FromX509Certificate()
.
Looks like your last remaining question is about how to "triple-wrap".
What I would recommend is this:
using (var ctx = new MySecureMimeContext ()) {
var encrypted = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, colle, mimeMessage.Body);
mimeMessage.Body = ApplicationPkcs7Mime.Sign (ctx, signer, encrypted);
}
or:
using (var ctx = new MySecureMimeContext ()) {
var encrypted = ApplicationPkcs7Mime.SignAndEncrypt(ctx, signer, colle, mimeMessage.Body);
mimeMessage.Body = MultipartSigned.Sign (ctx, signer, encrypted);
}
or:
using (var ctx = new MySecureMimeContext ()) {
var signed = MultipartSigned.Create (ctx, signer, mimeMessage.Body);
var encrypted = ApplicationPkcs7Mime.Encrypt (ctx, colle, signed);
mimeMessage.Body = MultipartSigned.Sign (ctx, signer, encrypted);
}
You might want to play around with all 3 of those options to see which one works best with the mail clients your customers use (Outlook, Thunderbird, Apple Mail?).
ApplicationPkcs7Mime.SignAndEncrypt()
uses the application/pkcs7-mime; smime-type=signed-data
format and then encrypts that which is different from encrypting a multipart/signed
and different clients may handle those to various degrees of success.
A good reason for using multipart/signed
is that the email will still be readable even if the user's client cannot decode S/MIME because it uses a detached signature which means that the original text of the message is not encapsulated within binary signature data. But... since you are encrypting as well, it might not make a difference.
Related to SecureMimeContext, we are using HanaDB and we want to store the certificates there, retrieve and use them, but I was not able to find the proper conversion for IX509CertificateDatabase, so, using WindowsStore for the moment.
I would recommend taking a look at DefaultSecureMimeContext
and implementing your own IX509CertificateDatabase
.
If HanaDB is SQL-based, you could probably subclass SqlCertificateDatabase
which is an abstract SQL-based certificate database implementation. You can take a look at SqliteCertificateDatabase.cs
or NpgsqlCertificateDatabase.cs
(PostgreSQL) to get a feel for how to do it.
Or you could take a look at X509CertificateDatabase.cs
to see how to implement a generic version.
I would honestly avoid WindowsSecureMimeContext
unless you are actually storing the certificates in the Windows certificate stores.
来源:https://stackoverflow.com/questions/30983883/sign-and-encrypt-on-mimekit