CMS/PKCS#7 Signed Data

CMS Signed Data content type can be created with the DidiSoft.OpenSsl.Cms.OpenSslCms class. It consists of content and one or more signed digests (hashes) created with the private keys of the sender(s). The content can be omitted, which results in detached signature and a smaller signed data output.

Equivalent OpenSSL command line operations:

openssl cms -sign for creating CMS Signed Data
openssl cms -verify for verifying CMS Signed Data

In order to create a Signed Data object we need the private key and Certificate of each sender.
An ordinary Signed Data object can be verified with the object itself as the verification Certificate is contained inside. For detached Signed Data we also need the original content.

The digest hash and the output format (PEM or DER) are specified as last parameters of the OpenSslCms.Sign methods.

Table of samples

Sign file

This example shows how to create a CMS Signed Data output in file. The private key and certificate are located in files. Both ordinary and detached options are illustrated.

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.Cms;
 
public class SignVerifyCmsFile
{
  public static void Demo()
  {
    OpenSslCms cms = new OpenSslCms();
    // sign with content inside
    cms.SignFile(@"Input.txt", @"private_key.pem", @"public.cert", @"signed.dat");
 
    bool detached = true;
    HashAlgorithm hash = HashAlgorithm.Sha256;
    bool pem = true;
    // sign detached
    cms.SignFile(@"Input.txt", @"private_key.pem", @"public.cert",  @"signed_detached.dat", detached, hash, pem);
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Imports System
Imports DidiSoft.OpenSsl.Cms
 
Public Class SignVerifyCmsFile
 Public Shared Sub Demo()
  Dim cms As New OpenSslCms()
  cms.SignFile("Input.txt", "private_key.pem", "public.crt", "signed.dat")
 
  Dim detached As Boolean = True
  Dim hash As HashAlgorithm = HashAlgorithm.Sha256
  Dim pem As Boolean = True
  cms.SignFile("Input.txt", "private_key.pem", "public.crt", "signed_detached.dat", detached, hash, pem)
 End Sub
End Class

Sign file with PrivateKey and Certificate

This example shows how to create a CMS Signed Data file with the PrivateKey and Certificate classes:

C# example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System;
using DidiSoft.OpenSsl.X509;
using DidiSoft.OpenSsl.Cms;
 
public class SignCmsFileWithCertificate
{
  public static void Demo()
  {
     PrivateKey key = PrivateKey.Load(@"Data\private_key.pem");
     Certificate cert = Certificate.Load(@"Data\public.crt");
 
     OpenSslCms cms = new OpenSslCms();
     cms.SignFile(@"Data\Input.txt", key, cert, @"Data\signed.dat");
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
Imports System
Imports DidiSoft.OpenSsl.Cms
Imports DidiSoft.OpenSsl.X509
 
Public Class SignVerifyCmsFile
 Public Shared Sub Demo()
  Dim key As PrivateKey = PrivateKey.Load("Data\private_key.pem")
  Dim cert As Certificate = Certificate.Load("Data\public.crt")
 
  Dim cms As New OpenSslCms()
  cms.SignFile("Data\Input.txt", key, cert, "Data\signed.dat")
 End Sub
End Class

 

Sign file with X509Certificate2

This example shows how to create a CMS Signed Data file with keys in .NET Framework class X509Certificate2. Here it is interesting that we can specify how much of the available certificate chain shall be included in the result Signed Data output going from X509IncludeOption.WholeChain for the whole certificate chain to X509IncludeOption.None for nothing, even the verification certificate is absent in this case.

C# example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
using System;
using System.Security.Cryptography.X509Certificates;
using DidiSoft.OpenSsl;
using DidiSoft.OpenSsl.Cms;
 
public class SignCmsFileWithX509Certificate2
{
  public static void Demo()
  {
     X509Certificate2 cert2 = new X509Certificate2(@"C:\Projects\didisoft.pkcs12.p12", "changeit", X509KeyStorageFlags.Exportable);
 
     OpenSslCms cms = new OpenSslCms();
     bool detached = true;
     HashAlgorithm hash = HashAlgorithm.Sha256;
     bool pemOutput = true;
     cms.SignFile(@"Data\Input.txt", cert2, X509IncludeOption.WholeChain, @"Data\signed.dat", detached, hash, pemOutput);
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Imports System
Imports System.Security.Cryptography.X509Certificates
Imports DidiSoft.OpenSsl.Cms
Imports DidiSoft.OpenSsl
 
Public Class SignVerifyCmsFile
 Public Shared Sub Demo()
  Dim cert2 As New X509Certificate2("C:\Projects\didisoft.pkcs12.p12", "cobra34", X509KeyStorageFlags.Exportable)
 
  Dim cms As New OpenSslCms()
  Dim detached As Boolean = True
  Dim hash As HashAlgorithm = HashAlgorithm.Sha256
  Dim pemOutput As Boolean = True
  cms.SignFile("Data\Input.txt", cert2, X509IncludeOption.WholeChain, "Data\signed.dat", detached, hash, pemOutput)
 End Sub
End Class

 

Sign String

String signing can be done with the same set of parameters as with the file example. Here the difference is that the result is a PEM formatted string:
C# example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using DidiSoft.OpenSsl;
using DidiSoft.OpenSsl.Cms;
 
public class SignCmsString
{
  public static void Demo()
  {
       PrivateKey key = PrivateKey.Load(@"Data\private_key.pem");
       Certificate cert = Certificate.Load(@"Data\public.crt");
 
       bool detached = true;
       HashAlgorithm hash = HashAlgorithm.Sha256;
       string pemSignedData = cms.SignString("Hello World", key, cert, detached, hash);
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Imports System
Imports DidiSoft.OpenSsl.Cms
Imports DidiSoft.OpenSsl
 
Public Class SignCmsString
 Public Shared Sub Demo()
  Dim cms As New OpenSslCms()
  Dim key As PrivateKey = PrivateKey.Load("Data\private_key.pem")
  Dim cert As Certificate = Certificate.Load("Data\public.crt")
 
  Dim detached As Boolean = True
  Dim hash As HashAlgorithm = HashAlgorithm.Sha256
  Dim pemSignedData As String = cms.SignString("Hello World", key, cert, detached, hash)
 End Sub
End Class

Sign Stream

When the SignStream method is used the output Signed Data object is written to an output Stream. In that case the output Stream and the input data Stream are both left open after the method call and the invoking higher level 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
18
19
20
21
using System;
using System.IO;
using DidiSoft.OpenSsl;
using DidiSoft.OpenSsl.Cms;
 
public class SignCmsStream
{
  public static void Demo()
  {
            OpenSslCms cms = new OpenSslCms();
            MemoryStream outputStream = new MemoryStream();
            using (Stream inputStream = File.OpenRead(@"Data\Input.txt"))
            using (Stream keyStream = File.OpenRead(@"Data\private_key.pem"))
            using (Stream certificateStream = File.OpenRead(@"Data\public.crt"))
            {
                cms.SignStream(inputStream, keyStream, certificateStream, outputStream);
            }
            // we can manipulate the output stream as it is still not closed
            outputStream.Position = 0;
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Imports System
Imports System.IO
Imports DidiSoft.OpenSsl.Cms
Imports DidiSoft.OpenSsl
 
Public Class SignCmsString
 Public Shared Sub Demo()
     Dim cms As New OpenSslCms()
     Dim outputStream As New MemoryStream()
     Using inputStream As Stream = File.OpenRead("Data\Input.txt")
       Using keyStream As Stream = File.OpenRead("Data\private_key.pem")
         Using certificateStream As Stream = File.OpenRead("Data\public.crt")
           cms.SignStream(inputStream, keyStream, certificateStream, outputStream)
         End Using
       End Using
     End Using
     ' we can manipulate the output stream as it is still not closed
     outputStream.Position = 0
 End Sub
End Class

Verify ordinary Signed Data file

In order to verify a Signed Data file with the signed content embedded inside we usually don’t need anything else as both the content, signature and verification key (inside the Certificate) is there:

C# example

1
2
3
4
5
6
7
8
9
10
11
using System;
using DidiSoft.OpenSsl.Cms;
 
public class VerifyCmsFile
{
  public static void Demo()
  {
    OpenSslCms cms = new OpenSslCms();
    bool verifiedSignature = cms.VerifyFile(@"signed.dat");
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
Imports System
Imports DidiSoft.OpenSsl.Cms
 
Public Class SignVerifyCmsFile
 Public Shared Sub Demo()
  Dim cms As New OpenSslCms()
  Dim verifiedSignature As Boolean = cms.VerifyFile("signed.dat")
 End Sub
End Class

Verify detached Signed Data file

In order to verify a Signed Data file with the detached content we need both the content and the signature (file containing the Signed Data object)

C# example

1
2
3
4
5
6
7
8
9
10
11
using System;
using DidiSoft.OpenSsl.Cms;
 
public class VerifyCmsFile
{
  public static void Demo()
  {
    OpenSslCms cms = new OpenSslCms();
    bool verifiedSignature = cms.VerifyFile(@"Input.txt", @"signed_detached.dat");
  }
}

VB.NET example

1
2
3
4
5
6
7
8
9
Imports System
Imports DidiSoft.OpenSsl.Cms
 
Public Class SignVerifyCmsFile
 Public Shared Sub Demo()
  Dim cms As New OpenSslCms()
  Dim verifiedSignature As Boolean = cms.VerifyFile("Input.txt", "signed_detached.dat")
 End Sub
End Class

Summary

This chapter demonstrated how to create and verify Signed Data CMS output. In order to create such data format we need the private key and Certificate of each sender, but as the format is self contained we can verify the message integrity on its own.