Clear text pgp signing in C# and VB.NET

The OpenPGP clear text signed format is designed for text data and contains the data intact plus the signature.

In this format the digital signature is appended after the clear text original message; this way the recipient can still read it without using special software. The examples below show how to produce clear signed messages with DidiSoft OpenPGP Library for .NET

This format differs from the usual OpenPGP signature format but the data can be extracted and the signature verified with the same set of PGP signature verifying methods.

List of examples

Clear text signing a file

1. with a private key located in a file | Async
2. with a private key located in a KeyStore | Async

Clear text signing a String message

3. with a private key located in a file | Async
4. with a private key located in a KeyStore | Async

Clear text signing a Stream

5. with a private key supplied as a Stream | Async
6. with a private key located in a KeyStore | Async

Clear text signing a file with the old version 3 signature

7. with a private key located in a file

Appendix

A. Verifying and extracting clear text signed data

B. Exception Handling

1. Clear text signing a file with a private key located in a file

When producing clear text signed OpenPGP messages, we can provide a file location and with our private key, produce signature over it.

The hash algorithm used form the digital signature is specified explicitly.

C# example

using System.IO;
using DidiSoft.Pgp;
 
class ClearSignFile
{
 public static void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   // clear text sign
   pgp.ClearSignFile(new FileInfo(@"DataFiles\INPUT.txt"),
		     new FileInfo(@"DataFiles\my_private_key.asc"),
		     "private key password",
		     HashAlgorithm.SHA256,
		     new FileInfo(@"DataFiles\OUTPUT.sig.txt"));
 }
}

VB.NET code

Imports System.IO
Imports DidiSoft.Pgp
 
Class ClearSignFile
 Public Shared Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  ' clear text sign
  pgp.ClearSignFile(New FileInfo("DataFiles\INPUT.txt"), _
		    New FileInfo("DataFiles\private.key"), _
		    "private key password", _
		    HashAlgorithm.SHA256, _
		    New FileInfo("DataFiles\OUTPUT.sig.txt"))
 End Sub
End Class

1-A. Async clear text signing a file

The asynchronous of the ClearSignFile method is ClearSignFileAsync and is available in the sub class PGPLibAsync. This sample code below produces the same clear text signed PGP file but in asynchronous fashion:

using System;
using System.Threading;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class CleartextSignFileAsync
{
 public async Task Demo()
 {
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  string privateKeyPassword = "changeit";
 
  // optional parameter
  CancellationToken token = default(CancellationToken);
 
  // clear text sign
  await pgp.ClearSignFileAsync(@"DataFiles\INPUT.txt",
					@"DataFiles\private.key",
					privateKeyPassword,
					HashAlgorithm.SHA256,
					@"DataFiles\OUTPUT.sig.txt",
					token);
 }
}

2. Clear text sign a file with a private key located in a KeyStore

In this example, the signing key is located in a KeyStore. The key is specified through its User ID but can be specified also with its Key ID or Key Hex ID.

C# example

using System;
using DidiSoft.Pgp;
 
class KeyStoreClearSignFile
{
 public static void Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "keystore password");
 
  string signingKeyUserId = "support@didisoft.com";
  string signingKeyPassword = "my private key password";
 
  // if this key store contains a key with
  // the desired recipient userId, then clear sign,
  // otherwise notify that there is no such key
  if (ks.ContainsKey(signingKeyUserId))
  {
	// create an instance of the library
	PGPLib pgp = new PGPLib();
 
	// clear text sign
	pgp.ClearSignFile(@"DataFiles\INPUT.txt", ks,
			  signingKeyUserId,
			  signingKeyPassword,
			  HashAlgorithm.SHA1,
                          @"DataFiles\OUTPUT.sig.txt");
   }
   else
   {
 	Console.WriteLine("No key with user Id:" +
                          signingKeyUserId +
                          " was found in this key store.");
   }
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Class KeyStoreClearSignFile
 Public Shared Sub Demo()
   ' initialize the key store
   Dim ks As New KeyStore("DataFiles\key.store", "keystore password")
 
   Dim signingKeyUserId As String = "support@didisoft.com"
   Dim signingKeyPassword As String = "my private key password"
 
   ' if this key store contains a key with
   ' the desired recipient userId, then clear sign,
   ' otherwise notify that there is no such key
   If ks.ContainsKey(signingKeyUserId) Then
      ' create an instance of the library
      Dim pgp As New PGPLib()
 
      ' clear text sign
      pgp.ClearSignFile("DataFiles\INPUT.txt", ks, _
                       signingKeyUserId, _
                       signingKeyPassword, _
                       HashAlgorithm.SHA1, _
                       "DataFiles\OUTPUT.sig.txt")
   Else
      Console.WriteLine("No key with user Id:" +
                        signingKeyUserId +
                        " was found in this key store.")
   End If
 End Sub
End Class

2-A. Async clear signing a File message with a key from a KeyStore

We can also clearsign a textual file with private key from a KeyStore.
In this example we have omitted the CancellationToken parameter for simplicity, but it can always be supplied as the last parameter of all Async methods.

using System;
using System;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class ClearsignFileAsyncKS
{
 public async Task Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
  string signingKeyUserId = "contact@didisoft.com";
  string signingKeyPassword = "changeit";
 
  // if this key store contains a key with the desired recipient userId - clear sign,
  // otherwise notify that there is no such key
  if (ks.ContainsKey(signingKeyUserId))
  {
	// create an instance of the library
	PGPLibAsync pgp = new PGPLibAsync();
 
	// clear text sign
	await pgp.ClearSignFileAsync(@"DataFiles\INPUT.txt", ks,
					  signingKeyUserId,
					  signingKeyPassword,
					  HashAlgorithm.SHA1,
					  @"DataFiles\INPUT.sig.txt");
 
  }
  else
  {
	Console.WriteLine("No key with user Id:" + signingKeyUserId + " was found in this key store.");
  }
}

3. Clear signing a String message with a private key located in a file

The ClearSignString method of the PGPLib class returns the clearsigned PGP message as String.
C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
class ClearSignString
{
 public static String Demo()
 {
   String plainString = "Hello World";
 
   PGPLib pgp = new PGPLib();
   String clearSignedString =
     pgp.ClearSignString(plainString,
			 new FileInfo(@"DataFiles\my_private_key.asc"),
			 "private key password",
			 HashAlgorithm.SHA1);
   return clearSignedString;
 }
}

VB.NET code

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Class ClearSignString
 Public Shared Function Demo() As String
   Dim plainString As String = "Hello World"
 
   ' create an instance of the library
   Dim pgp As New PGPLib()
 
   ' clear text sign
   Dim clearSignedString As String = _
      pgp.ClearSignString(plainString, _
			  New FileInfo("DataFiles\my_private_key.asc"), _
			  "my private key password", _
			  HashAlgorithm.SHA1)
	Return clearSignedString
 End Function
End Class

3-A. Async Clear signing a String message

The method for asynchronous clear signing is ClearSignStringAsync and is located in the PGPLibAsync class:

using System;
using System.Threading;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class ClearsignStringAsync
{
 public async Task Demo()
 {
  String plainString = "Hello World";
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // optional parameter
  CancellationToken token = default(CancellationToken);
 
  // clear text sign
  String clearSignedString = await pgp.ClearSignStringAsync(plainString,
							 @"DataFiles\private.key",
							 "changeit",
							 HashAlgorithm.SHA256,
							 token);
  return clearSignedString;
 }
}

4. Clear text signing a String message with a private key located in a KeyStore

This sample is equivalent to the above one, but using keys from a KeyStore file.
C# example

using System;
using DidiSoft.Pgp;
 
class KeyStoreClearSignString
{
 public static String Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "keystore password");
 
  string signingKeyUserId = "support@didisoft.com";
  string signingKeyPassword = "private key password";
 
  // if this key store contains a key with the desired recipient userId - clear sign,
  // otherwise notify that there is no such key
  if (ks.ContainsKey(signingKeyUserId))
  {
 	// create an instance of the library
	PGPLib pgp = new PGPLib();
 
	string plainText = "Hello World";
 
	// clear text sign
	string clearSignedString =
            pgp.ClearSignString( plainText, ks,
				 signingKeyUserId,
				 signingKeyPassword,
				 HashAlgorithm.SHA1);
 
	   return clearSignedString;
  }
  else
  {
	Console.WriteLine("No key with user Id:" +
                           signingKeyUserId +
                           " was found in this key store.");
	return null;
  }
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Class KeyStoreClearSignString
 Public Shared Function Demo() As String
  Dim signingKeyUserId As String = "support@didisoft.com"
  Dim signingKeyPassword As String = "private key password"
 
  ' initialize the key store
  Dim ks As New KeyStore("DataFiles\key.store", "keystore password")
 
  ' if this key store contains a key with the desired recipient userId - clear sign,
  ' otherwise notify that there is no such key
  If ks.ContainsKey(signingKeyUserId) Then
    Dim pgp As New PGPLib()
 
    Dim plainText As String = "Hello World"
 
    Dim clearSignedString As String = _
       pgp.ClearSignString(plainText, ks, _
				signingKeyUserId, _
				signingKeyPassword, _
				HashAlgorithm.SHA1)
 
	Return clearSignedString
  Else
	Console.WriteLine("No key with user Id:" + _
                          signingKeyUserId + _
                          " was found in this key store.")
	Return Nothing
  End If
 End Function
End Class

4-A. Async Clear text signing a String with a key from a KeyStore

We can also use a private key from a KeyStore to OpenPGP clear text sign a String message as you can see from this example code:

using System;
using System.Threading;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class ClearsignStringAsyncKS
{
 public async Task Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
  string signingKeyUserId = "support@didisoft.com";
  string signingKeyPassword = "changeit";
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  string plainText = "Hello World";
 
  // optional parameter
  CancellationToken token = default(CancellationToken);
 
  // clear text sign
  string clearSignedString = await pgp.ClearSignStringAsync(plainText, ks,
							 signingKeyUserId,
							 signingKeyPassword,
							 HashAlgorithm.SHA256);
 
  return clearSignedString;
 }
}

5. Clear text signing a Stream with a private key supplied as a Stream

In this example, we will create a clear text signed output of data available as a Stream for reading.

C# code

using System;
using System.IO;
using DidiSoft.Pgp;
 
public class ClearSignStream
{
 public static void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   Stream outputStream = new MemoryStream();
 
   using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt"))
   using (Stream keyStream = File.OpenRead(@"DataFiles\my_private_key.asc"))
   {
     // clear text sign
     pgp.ClearSignStream(dataStream,
		     keyStream,
		     "private key password",
		     HashAlgorithm.SHA256,
		     outputStream);
   }
   // .. The outputStream Position is at the end of the Stream
 }
}

VB.NET example

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Class ClearSignStream
 Public Shared Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  Dim outputStream As Stream = New MemoryStream()
 
  Using Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt")
   Using Stream keyStream = File.OpenRead(@"DataFiles\my_private_key.asc")
    ' clear text sign
    pgp.ClearSignStream(dataStream, _
		    keyStream, _
		    "private key password", _
		    HashAlgorithm.SHA256, _
		    outputStream)
   End Using
  End Using
  ' .. The outputStream Position is at the end of the Stream
 End Sub
End Class

5-A. Async Clear text signing a Stream

We can also asynchronously clear text sign data from a Stream with the ClearSignStreamAsync method available in the PGPLibAsync class, as you can see in the example below:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
public class ClearsignStreamAsyncDemo
{ 
 public async Task Demo()
 {
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  Stream outputStream = new MemoryStream();
 
  // optional parameter
  CancellationToken token = default(CancellationToken);
 
  // although here we use File Streams 
  // we can use any kinf of Streams as well
  using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt"))
  using (Stream keyStream = File.OpenRead(@"DataFiles\my_private_key.asc"))
  {
  // clear text sign
  await pgp.ClearSignStreamAsync(dataStream,
		keyStream,
		"private key password",
		HashAlgorithm.SHA256,
		outputStream,
		token);
  }
  // .. The outputStream Position is at the end of the Stream
 }
}

6. Clear text signing a Stream with a private key located in a KeyStore

In this example, the signing key is located in a KeyStore. The key is specified through its Key Hex ID but can be specified also with its Key ID (of type System.Int64) or User ID

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
class KeyStoreClearSignStream
{
 public static void Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "keystore password");
 
  string signingKeyHexId = "395A421B";
  string signingKeyPassword = "my private key password";
 
  // create an instance of the library
  PGPLib pgp = new PGPLib();
 
  Stream outputStream = new MemoryStream();
 
  using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt"))
  {
    // clear text sign
    pgp.ClearSignStream(dataStream, ks,
			signingKeyHexId,
			signingKeyPassword,
			HashAlgorithm.SHA1,
                        outputStream);
  }
  // .. The outputStream Position is at the end of the Stream
 }
}

VB.NET example

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Class KeyStoreClearSignStream
 Public Shared Sub Demo()
   ' initialize the key store
   Dim ks As New KeyStore("DataFiles\key.store", "keystore password")
 
   Dim signingKeyHexId As String = "395A421B"
   Dim signingKeyPassword As String = "my private key password"
 
   ' create an instance of the library
   Dim pgp As New PGPLib()
 
   Dim outputStream As Stream = New MemoryStream()
 
   Using Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt")
      ' clear text sign
      pgp.ClearSignStream(dataStream, ks, _
                       signingKeyHexId, _
                       signingKeyPassword, _
                       HashAlgorithm.SHA1, _
                       outputStream)
   End Using
   ' .. The outputStream Position is at the end of the Stream
 End Sub
End Class

6-A. Async Clear text signing a Stream with key from a KeyStore

You can see an Async version of the ClearSignStream method from the PGPLibAsync class in the following example:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
 
using DidiSoft.Pgp;
 
class ClearsignStreamAsyncKS
{
 public async Task Demo()
 {
  // initialize the key store
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "keystore password");
 
  string signingKeyHexId = "395A421B";
  string signingKeyPassword = "my private key password";
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // optional parameter
  CancellationToken token = default(CancellationToken);
 
  using (Stream outputStream = new MemoryStream())
  using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt"))
  {
	// clear text sign
	await pgp.ClearSignStreamAsync(dataStream, ks,
			signingKeyHexId,
			signingKeyPassword,
			HashAlgorithm.SHA256,
			outputStream,
			token);
  }
 }
}

7. Clear text signing a file with the old version 3 signatures

Older OpenPGP implementations used a different signature format and if we wish to create a clear text signed message that can be verified by them, we should use the methods that end with V3. Newer implementations can process both the old and the new signature format.

C# example

using System.IO;
using DidiSoft.Pgp;
 
class ClearSignFile
{
 public static void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   // clear text sign
   pgp.ClearSignFileV3(new FileInfo(@"DataFiles\INPUT.txt"),
			new FileInfo(@"DataFiles\my_private_key.asc"),
			"private key password",
			HashAlgorithm.SHA256,
			new FileInfo(@"DataFiles\OUTPUT.sig.txt"));
 }
}

VB.NET example

Imports System.IO
Imports DidiSoft.Pgp
 
Class ClearSignFile
 Public Shared Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  ' clear text sign
  pgp.ClearSignFileV3(new FileInfo("DataFiles\INPUT.txt"), _
		    new FileInfo("DataFiles\my_private_key.asc"), _
		    "private key password", _
		    HashAlgorithm.SHA256, _
		    new FileInfo("DataFiles\OUTPUT.sig.txt"))
 End Sub
End Class

Appendix A. Verifying and extracting clear text signed data

Verification and extraction of clear-signed data are done through the verification methods.

A key difference is that when we supply a wrong public key nonmatching the signature, or a KeyStore with no matching public key in it a DidiSoft.Pgp.Exceptions.WrongPublicKeyException will be thrown:

C# example

PGPLib pgp = new PGPLib();
string signed = pgp.ClearSignString("Hello world", "my_private_key.asc", "my password", HashAlgorithm.SHA1);
string extracted = string.Empty;
bool verified = false;
try
{
    verified = pgp.VerifyString(signed, "wrong_pub_key.asc", out extracted);
}
catch (DidiSoft.Pgp.Exceptions.WrongPublicKeyException) 
{
    Console.WriteLine("The specified public key doesn't match the signature, you should use another key");
    // simply extract the clear text data
    extracted = pgp.DecryptString(signed, null, string.Empty);
}

VB.NET example

Dim pgp As New PGPLib()
Dim signed As String = pgp.ClearSignString("Hello world", "my_private_key.asc", "my password", HashAlgorithm.SHA1)
Dim extracted As String = String.Empty
Dim verified As Boolean = False
Try
    verified = pgp.VerifyString(signed, "wrong_pub_key.asc", extracted)
Catch (DidiSoft.Pgp.Exceptions.WrongPublicKeyException) 
    Console.WriteLine("The specified public key doesn't match the signature, you should use another key");
    // simply extract the clear text data
    extracted = pgp.DecryptString(signed, null, string.Empty);
End Try

Appendix B. Exception Handling

All ClearSign methods throw System.IO.IOException and DidiSoft.Pgp.PGPException. We can try to cast the DidiSoft.Pgp.PGPException variable in its catch clause in order to identify what exactly went wrong.

Below is an example that illustrates the subclasses that we can try to cast the exception of type DidiSoft.Pgp.PGPException to:

C# example

PGPLib pgp = new PGPLib();
try
{
  pgp.ClearSign...
}
catch (System.IO.IOException e)
{
  // in case of an input file not found or other I/O related error
}
catch (DidiSoft.Pgp.PGPException e)
{
  if (e is DidiSoft.Pgp.Exceptions.WrongPrivateKeyException)
  {
   // The supplied private key source is not a private key at all 
   // or does not contain a signing key
   // For example we have supplied an arbitrary file for the private 
   // key parameter, or in the case with a KeyStore parameter 
   // there is no private key with the specified Key ID or User ID
  }
  else if (e is DidiSoft.Pgp.Exceptions.WrongPasswordException)
  {
   // The supplied private key password is misspelled
  }
  else
  {
   // General OpenPGP error non among the above
  }
}

VB.NET example

Dim pgp As New PGPLib()
Try
  pgp.ClearSign...
Catch e As System.IO.IOException
  ' in case of an input file not found or other I/O related error
Catch e As DidiSoft.Pgp.PGPException
  If TypeOf e Is DidiSoft.Pgp.Exceptions.WrongPrivateKeyException Then
   ' The supplied private key source is not a private key at all 
   ' or does not contain a signing key
   ' For example we have supplied an arbitrary file for the private 
   ' key parameter, or in the case with a KeyStore parameter 
   ' there is no private key with the specified Key ID or User ID	
  ElseIf TypeOf e Is DidiSoft.Pgp.Exceptions.WrongPasswordException Then
   ' The supplied private key password is misspelled	
  Else
   ' General OpenPGP error non among the above	
  End If
End Try

Back to Top

Summary

This chapter presented OpenPGP cleartext signing in C# and VB.NET. The class PGPLib that provides those methods is located in the namespace DidiSoft.Pgp. The methods that perform cleartext signing are:

PGPLib.ClearSignFileProduces PGP clear text signed message from a Text file

PGPLibAsync.ClearSignFileAsync Asynchronously produces PGP clear text signed message from a Text file
PGPLibAsync.ClearSignStringAsync Asynchronously produces PGP clear text signed message from a String
PGPLibAsync.ClearSignStreamAsync Asynchronously produces PGP clear text signed message from Stream containing character data
PGPLib.ClearSignString Produces PGP clear text signed message from a String
PGPLib.ClearSignStream Produces PGP clear text signed message from Stream containing character data
PGPLib.ClearSignFileV3 Produces PGP clear text signed message in the old format