I'm currently stuck on a problem somewhere between MimeKit and Bouncycastle: I try to create multiple certificates for multiple postboxes using ECC instead of RSA. The creation of the certificates works nice and fast as expected. But when I try to use them to encrypt an mail with MimeKit using S/MIME an error is thrown that tells me that the Cipher is unknown. Disclaimer: It may well be that I'm getting things mixed up now that I've dug very deeply into MimeKit. So don't be afraid to point out my errors in thinking and get me back on track.
Now let's start the journey ...
Step 1 - the generation of the certificates
Maybe I just messed up the certificate during its creation? I tried to tell bouncycastle (2.2.1) that I'd like to have an ECC certificate based on the curve "secp521r1". Then I added some information, signed the cert using ECDSA and saved it into an file.
// init the generator
using CryptoApiRandomGenerator carg = new();
var srnd = new SecureRandom(carg);
var certGen = new X509V3CertificateGenerator();
var ecdp = new ECDomainParameters(ECNamedCurveTable.GetByName("secp521r1"));
var kgp = new ECKeyGenerationParameters(ecdp, srnd);
// set the cert data, cert usage, S/MIME capabilties and more
...
certGen.SetIssuerDN(new X509Name("issuer"));
certGen.SetSubjectDN(new X509Name("subject"));
certGen.AddExtension(X509Extensions.KeyUsage, true, new KeyUsage(
KeyUsage.DigitalSignature | KeyUsage.KeyEncipherment | KeyUsage.DataEncipherment | KeyUsage.NonRepudiation
));
certGen.AddExtension(X509Extensions.ExtendedKeyUsage, false, new ExtendedKeyUsage(
KeyPurposeID.id_kp_emailProtection
));
...
// init generators & create key pair
AsymmetricCipherKeyPair keyPair = ConsoleHelper.DisplayWaitIndicator($"[Generator] Generating new key pair for {subject}", () =>
{
var kpg = new ECKeyPairGenerator();
kpg.Init(kgp);
return kpg.GenerateKeyPair();
});
// update cert
certGen.SetPublicKey(keyPair.Public);
ISignatureFactory signatureFactory = new Asn1SignatureFactory(X9ObjectIdentifiers.ECDsaWithSha256.Id, keyPair.Private, srnd);
var cert = certGen.Generate(signatureFactory);
// pack into pcks12 store
var store = new Pkcs12StoreBuilder().Build();
var certEntry = new X509CertificateEntry(cert);
store.SetCertificateEntry(subject, certEntry);
// and finally save it
// save pkcs store file (with private key)
store.SetKeyEntry(subject, new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });
store.Save(pfxTarget, pfxPassword, srnd);
Step 2 - the certificate management
Since I have some special requirements regarding certificate management, I had to derive my own SecureMimeContext "MySecuryMimeContext" from the BouncyCastleSecureMimeContext given in MimeKit (4.3.0). But I omitted everything that may handle cryptography so this is just the code of the BouncyCastleSecureMimeContext. I may provide some parts if required but the whole thing is way to big for posting.
Step 3 - encrypting mails
I just created an sample mail using MailKit with an attached file and tried to encrypt it as usual (this code worked with the RSA certificates). But whenever I try enrypting a mail using an ECC certificate, the error tells me, that the cipher is unkown, where OID is exactly the OID of the ECC I tried to use. (I'll add an image of the error later, when I cleaned the mess of debugging in my code)
var mail = new MimeMessage();
mail.From.Add(new MailboxAddress("from", "[email protected]"));
mail.From.Add(new MailboxAddress("to", "[email protected]"));
//mail.Cc.Add(cc);
//mail.Bcc.Add(bcc);
mail.Subject = "subject";
// read file
using MemoryStream ms = new(new byte[localFile.Length]);
using (FileStream fs = localFile.OpenRead())
{
fs.CopyTo(ms);
}
// building the plain body
mail.Body = new Multipart("mixed")
{
new TextPart { Text = mail.Subject },
new MimePart(new ContentType("plain", "text") { Name = localFile.Name })
{
Content = new MimeContent(ms),
ContentTransferEncoding = ContentEncoding.Base64,
ContentDisposition = new ContentDisposition(ContentDisposition.Attachment)
{ FileName = localFile.Name }
}
};
// encrypt
using var ctx = new MySecuryMimeContext();
mail.Body = ApplicationPkcs7Mime.Encrypt(ctx, mail.GetRecipients(), mail.Body);
Questions I have
Thanks for helping out!
More discussion of this happening on the MimeKit GitHub issue tracker.
The gist of it is that ECC was not supported in MimeKit <= 4.3.0 but that with the help of someone from the BouncyCastle team, I have added it to MimeKit and it will be supported in the next release (v4.4.0).