S/MIME encrypted data is actually a CMS/PKCS#7 Enveloped data but formatted as MIME (Multipurpose Internet Mail Extensions). OpenSSL creates S/MIME encrypted data with the smime command.
For S/MIME encrypting we need the X.509 certificate of the recipient(s). For decrypting we need a private key corresponding to a certificate used for the encryption.
The default symmetric cipher used for the session key is TrippleDES (Des3) – same as with OpenSSL. This can be specified as method call parameter.
This tutorial will illustrate how to create S/MIME data compatible with OpenSSL using DidiSoft OpenSSL Library for .NET and the DidiSoft.OpenSsl.Cms.OpenSslSmime class
Table of contents
- Encrypt file
- Encrypt string
- Encrypt Stream
- Encrypt bytes
- Encrypting with X509Certificate2 instance
- Encrypting for multiple recipients
- Decrypt file
- Decrypt string
- Decrypt Stream
- Decrypt bytes
- Decrypting with X509Certificate2 instance
- Exception handling
Encrypt file
In order to encrypt a file in S/MIME format we need the X.509 certificate of the recipient. The equivalent OpenSSL command is:
openssl smime -encrypt -des3 -in input.txt -out output.p7m recipient.crt
In the example code below the preferred symmetric cipher for encrypting the data is also specified:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 | using System; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeFile { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); CmsCipherAlgorithm encAlgorithm = CmsCipherAlgorithm.Des3; smime.EncryptFile(@"input.txt", @"recipient.crt", @"output.p7m", encAlgorithm); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 | Imports System Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeFile Public Shared Sub Demo() Dim smime As New OpenSslSmime() Dim encAlgorithm As CmsCipherAlgorithm = CmsCipherAlgorithm.Des3 smime.EncryptFile("input.txt", "recipient.crt", "output.p7m", encAlgorithm) End Sub End Class |
The classic OpenSSL tool provides also a -binary switch in which case the output is the raw encrypted email without any headers. In this case, we shall use the OpenSslSmime.EncryptFileBinary method.
Encrypt string
When S/MIME encrypting a string message the output is returned as a result of the method call:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | using System; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeString { public void Demo() { OpenSslSmime smime = new OpenSslSmime(); string certificate = @"Data\public.crt"; string messageToBeEncrypted = "Hello World"; CmsCipherAlgorithm encAlgorithm = CmsCipherAlgorithm.Blowfish; string smimeData = smime.EncryptString(messageToBeEncrypted, certificate, encAlgorithm); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Imports System Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeString Public Sub Demo() Dim smime As New OpenSslSmime() Dim certificate As String = "Data\public.crt" Dim messageToBeEncrypted As String = "Hello World" Dim encAlgorithm As CmsCipherAlgorithm = CmsCipherAlgorithm.Blowfish Dim smimeData As String = smime.EncryptString(messageToBeEncrypted, certificate, encAlgorithm) End Sub End Class |
Encrypt Stream
The OpenSslSmime.EncryptStream produces S/MIME encrypted mail message with Body in Base-64 encoded format:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.IO; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeStream { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); CmsCipherAlgorithm encAlgorithm = CmsCipherAlgorithm.Aes256; MemoryStream mem = new MemoryStream(); using (Stream inputStream = File.OpenRead(@"input.txt")) { smime.EncryptStream(inputStream, @"public.crt", mem, encAlgorithm); } } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Imports System Imports System.IO Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeStream Public Shared Sub Demo() Dim smime As New OpenSslSmime() Dim encAlgorithm As CmsCipherAlgorithm = CmsCipherAlgorithm.Aes256 Dim mem As New MemoryStream() Using inputStream As Stream = File.OpenRead("Data\Input.txt") smime.EncryptStream(inputStream, "Data\public.crt", mem, encAlgorithm) End Using End Sub End Class |
OpenSSL provides also a -binary parameter when we want only the encrypted body in raw binary format like:
openssl smime -encrypt -binary -aes-256-cbc -in FileToEncrypt -out EncryptedFilename -outform DER certificateFile.crt
In this case, we must use the EncryptStreamBinary method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.IO; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeStreamBinary { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); DidiSoft.OpenSsl.X509.Certificate certificate = DidiSoft.OpenSsl.X509.Certificate.Load(@"C:\Data\cert.crt"); using (Stream inputStream = File.OpenRead(@"C:\Data\message.eml")) using (Stream outputStream = File.Create(@"C:\Data\message_out.dat")) { smime.EncryptStreamBinary(inputStream, certificate, outputStream, null, CmsCipherAlgorithm.Aes256); } } } |
Encrypt bytes
Byte array encryption is no different than encrypting file or string. The result encrypted S/MIME data is returned as byte array:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeBytes { public void Demo() { OpenSslSmime smime = new OpenSslSmime(); string certificate = @"Data\public.crt"; byte[] dataToBeEncrypted = new byte[] { 1, 2, 3 }; CmsCipherAlgorithm encAlgorithm = CmsCipherAlgorithm.Blowfish; byte[] smimeData = smime.EncryptBytes(dataToBeEncrypted, certificate, encAlgorithm); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Imports System Imports DidiSoft.OpenSsl Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeBytes Public Sub Demo() Dim smime As New OpenSslSmime() Dim certificate As String = "Data\public.crt" Dim dataToBeEncrypted As Byte() = New Byte() {1, 2, 3} Dim encAlgorithm As CmsCipherAlgorithm = CmsCipherAlgorithm.Blowfish Dim smimeData As Byte() = smime.EncryptBytes(dataToBeEncrypted, certificate, encAlgorithm) End Sub End Class |
Encrypting with X509Certificate2 instance
Overloaded Encrypt methods exist that accept an encryption certificate directly from a System.Security.Criptography.X509Certificates.X509Certificate2 instance as you can see from the example below:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | using System; using System.Security.Cryptography.X509Certificates; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeWithX509Certificate2 { public static void Demo() { X509Certificate2 cert2 = new X509Certificate2(@"C:\Projects\didisoft.pkcs12.p12", "cobra34", X509KeyStorageFlags.Exportable); OpenSslSmime smime = new OpenSslSmime(); CmsCipherAlgorithm encAlgorithm = CmsCipherAlgorithm.Des3; smime.EncryptFile(@"input.txt", cert2, @"smime.p7m", encAlgorithm); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 | Imports System Imports System.Security.Cryptography.X509Certificates Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeWithX509Certificate2 Public Shared Sub Demo() Dim cert2 As New X509Certificate2("C:\Projects\didisoft.pkcs12.p12", "cobra34", X509KeyStorageFlags.Exportable) Dim smime As New OpenSslSmime() Dim encAlgorithm As CmsCipherAlgorithm = CmsCipherAlgorithm.Des3 cms.EncryptFile("input.txt", cert2, "smime.p7m", encAlgorithm) End Sub End Class |
Encrypting for multiple recipients
An encrypted S/MIME message can be created for multiple recipients. In that case we must use the overloaded Encrypt methods that expect an array of X.509 certificates :
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System; using DidiSoft.OpenSsl.X509; using DidiSoft.OpenSsl.Cms; public class EncryptSmimeMultipleRcpt { public void Demo() { Certificate cert1 = Certificate.Load("recipient1.crt"); Certificate cert2 = Certificate.Load("recipient2.crt"); OpenSslSmime smime = new OpenSslSmime(); Certificate[] recipients = new Certificate[] { cert1, cert2 }; smime.EncryptFile(@"input.txt", recipients, @"smime.p7m"); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Imports System Imports DidiSoft.OpenSsl.X509 Imports DidiSoft.OpenSsl.Cms Public Class EncryptSmimeMultipleRcpt Public Sub Demo() Dim cert1 As Certificate = Certificate.Load("recipient1.crt") Dim cert2 As Certificate = Certificate.Load("recipient2.crt") Dim smime As New OpenSslSmime() Dim recipients As Certificate() = New Certificate() {cert1, cert2} smime.EncryptFile("input.txt", recipients, "smime.p7m") End Sub End Class |
Decrypt file
In order to decrypt S/MIME encrypted message we must poses a private key corresponding to a certificate used for the encryption. OpenSSL Library for .NET will also detect automatically the symmetric cipher used so there is no need to specify it:
C# example
1 2 3 4 5 6 7 8 9 10 11 | using System; using DidiSoft.OpenSsl.Cms; public class DecryptSmimeFile { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); cms.DecryptFile(@"smime.p7m", @"private_key.pem", @"Output.txt"); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 | Imports System Imports DidiSoft.OpenSsl.Cms Public Class DecryptSmimeFile Public Shared Sub Demo() Dim smime As New OpenSslSmime() smime.DecryptFile("smime.p7m", "private_key.pem", "Output.txt") End Sub End Class |
Decrypt S/MIME string
The OpenSslSmime.DecryptString is similar to the DecryptFile illustrated above:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System; using DidiSoft.OpenSsl.Cms; public class DecryptSmimeString { public void Demo(string smimeEncryptedData) { OpenSslSmime smime = new OpenSslSmime(); string decryptedMessage = smime.DecryptString(smimeEncryptedData, "private_key.pem"); Console.WriteLine(decryptedMessage); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 | Imports System Imports DidiSoft.OpenSsl.Cms Public Class DecryptSmimeString Public Sub Demo(smimeEncryptedData As String) Dim cms As New OpenSslSmime() Dim decryptedMessage As String = smime.DecryptString(smimeEncrypted, "private_key.pem") Console.WriteLine(decryptedMessage) End Sub End Class |
Decrypt Stream
The OpenSslSmime.DecryptStream methods just like the Stream encryption one will leave the input and output Streams open! The invoking code block has the obligation to close them:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.IO; using DidiSoft.OpenSsl.Cms; public class DecryptSmimeStream { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); MemoryStream mem = new MemoryStream(); using (Stream inputStream = File.OpenRead(@"smime.p7m")) { smime.DecryptStream(inputStream, @"Data\private_key.pem", mem); } // mem can be read } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Imports System Imports System.IO Imports DidiSoft.OpenSsl.Cms Public Class DecryptCmsStream Public Shared Sub Demo() Dim smime As New OpenSslSmime() Dim mem As New MemoryStream() Using inputStream As Stream = File.OpenRead("smime.p7m") cms.DecryptStream(inputStream, "Data\private_key.pem", mem) End Using ' mem can be read End Sub End Class |
Decrypt bytes
Decrypted byte arrays obtained from S/MIME encrypted data will produce the raw content as byte array:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 | using System; using DidiSoft.OpenSsl.Cms; public class DecryptSmimeBytes { public void Demo(byte[] smimeData) { OpenSslSmime smime = new OpenSslSmime(); string privateKey = @"Data\private_key.pem"; byte[] decryptedData = smime.DecryptBytes(smimeData, privateKey); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 | Imports System Imports DidiSoft.OpenSsl Imports DidiSoft.OpenSsl.Cms Public Class DecryptBytes Public Sub Demo(smimeData As Byte()) Dim smime As New OpenSslSmime() Dim privateKey As String = "Data\private_key.pem" Dim decryptedData As Byte() = smime.DecryptBytes(smimeData, privateKey) End Sub End Class |
Decrypting with X509Certificate2 instance
If an instance of System.Security.Cryptography.X509Certificates.X509Certificate2 contains a private key it can also be used for decrypting:
C# example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using System; using System.Security.Cryptography.X509Certificates; using DidiSoft.OpenSsl.Cms; public class DecryptSmimeWithX509Certificate2 { public static void Demo() { OpenSslSmime smime = new OpenSslSmime(); X509Certificate2 cert2 = new X509Certificate2(@"C:\Projects\didisoft.pkcs12.p12", "cobra34", X509KeyStorageFlags.Exportable); smime.DecryptFile(@"smime.p7m", cert2, @"output.txt"); } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 | Imports System Imports System.Security.Cryptography.X509Certificates Imports DidiSoft.OpenSsl.Cms Public Class DecryptSmimeWithX509Certificate2 Public Shared Sub Demo() Dim cert2 As New X509Certificate2("C:\Projects\didisoft.pkcs12.p12", "cobra34", X509KeyStorageFlags.Exportable) Dim smime As New OpenSslSmime() smime.DecryptFile("smime.p7m", cert2, "output.txt") End Sub End Class |
Exception handling
All the methods provided from the OpenSslCms class will throw DidiSoft.OpenSsl.OpenSslException in case of cryptography related error.
Specific sub classes can be caught beforehand in order to identify more thoroughly what went wrong and possibly take recovery cations.
All Encrypt methods will throw a DidiSoft.OpenSsl.Exceptions.WrongPublicKeyException if the provided certificate is not a valid X.509 certificate.
1 2 3 4 5 6 7 8 9 10 11 12 13 | OpenSslSmime smime = new OpenSslSmime(); try { smime.Encrypt... } catch (DidiSoft.OpenSsl.Exceptions.WrongPublicKeyException exc) { // handle case when the provided certificate is actually not a X509.Certificate } catch (DidiSoft.OpenSsl.OpenSslException e) { // general OpenSSL error } |
All Decrypt methods will throw a DidiSoft.OpenSsl.Exceptions.WrongPrivateKeyException if the provided private key doesn’t match any certificate used to encrypt the message.
If the input provided for decryption is not S/MIME encrypted Data, then a DidiSoft.OpenSsl.Exceptions.CmsFormatException will be thrown:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | OpenSslSmime smime = new OpenSslSmime(); try { smime.Decrypt... } catch (DidiSoft.OpenSsl.Exceptions.WrongPrivateKeyException exc) { // handle case when the provided private key doesn't match any certificate } catch (DidiSoft.OpenSsl.Exceptions.WrongPasswordException exc) { // password provided for encrypted private key doesn't match } catch (DidiSoft.OpenSsl.Exceptions.CmsFormatException exc) { // the provided input is not S/MIME encrypted Data } catch (DidiSoft.OpenSsl.OpenSslException e) { // general OpenSSL error } |
Summary
This chapter illustrated how to use DidiSoft.OpenSsl.Cms.OpenSslSmime class to encrypt and decrypt S/MIME data.
All the methods demonstrated here will produce content compatible with the popular OpenSSL software and its smime command. The decryption method provided from the OpenSslCms class will automatically determine the symmetric cipher used for encrypted the data in the S/MIME message.