OpenPGP signing in .NET

The OpenPGP sign operation converts the input data into OpenPGP packet format and appends a digital signature produced with the private key of the sender. The opposite command for extracting signed content is verify or simply decrypt if we do not wish to check the validity of the signature.

Older OpenPGP implementations (like PGP 6.5.x) used a different digital signature format called version 3 signatures. In order to produce signed content  compatible with older implementations we should used the Sign methods that end with V3 (for example SignFileV3).

Note that OpenPGP signed data is not encrypted. If the data has to be also encrypted check the one pass sign and encrypt section.

The examples below demonstrate signing with DidiSoft OpenPGP Library for .NET

Signing a file

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

Signing a String message

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

Signing a Stream

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

Signing a file with the older OpnePGP version 3 signature.

7. with a private key located in a file 
8. with a private key located in a KeyStore

Appendix

A. Compression and Hash function
B. Exception Handling


1. Signing a file with private key located in a file

C# example

using System;
using DidiSoft.Pgp;
 
public class SignDemo
{
 public void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
   // should the output be ASCII or binary
   bool asciiArmor = true;
 
   pgp.SignFile(@"C:\INPUT.txt",
                @"C:\private_key.asc",
                "private key passphrase",
                @"C:\OUTPUT.pgp",
                asciiArmor);
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Public Class SignDemo
 Public Sub Demo()
    ' create an instance of the library
    Dim pgp As New PGPLib()
 
    ' should the output be ASCII or binary
    Dim asciiArmor As Boolean = True
 
    pgp.SignFile("C:\INPUT.txt", _
                  "C:\private_key.asc", _
                  "private key passphrase", _
                  "C:\OUTPUT.pgp", _
                  asciiArmor)
 End Sub
End Class

Back to Top

2. Signing a file with private key located in a KeyStore

This example is equivalent to the above one, except that the signing key resides in a KeyStore file. We specify which key to be used for signing by it’s User Id.

C#

using System;
using DidiSoft.Pgp;
 
class KeyStoreSignFile
{
 public static void Demo()
 {
  // initialize the key store
  KeyStore store = new KeyStore(@"c:\key.store",
				"changeit");
 
  string signingKeyUserId = "support@didisoft.com";
  string signingKeyPassword = "changeit";
 
  // if this key store contains a key with this User Id,
  // then sign,
  // otherwise notify that there is no such key
  if (store.ContainsKey(signingKeyUserId))
  {
	// create an instance of the library
	PGPLib pgp = new PGPLib();
 
	// should the output be ASCII or binary
	bool asciiArmor = true;
 
	pgp.SignFile(@"c:\INPUT.txt", store,
		     signingKeyUserId,
		     signingKeyPassword,
		     @"c:\INPUT.sig.txt",
		     asciiArmor);
 
  }
  else
  {
	Console.WriteLine("The key was not found!");
  }
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Class KeyStoreSignFile
 Public Shared Sub Demo()
  ' initialize the key store
  Dim store As New KeyStore("c:\key.store", "changeit")
 
  Dim signingKeyUserId As String = "support@didisoft.com"
  Dim signingKeyPassword As String = "changeit"
 
  ' if this key store contains a key with this User Id,
  ' then sign,
  ' otherwise notify that there is no such key
  If store.ContainsKey(signingKeyUserId) Then
    ' create an instance of the library
    Dim pgp As New PGPLib()
 
    ' should the output be ASCII or binary
    Dim asciiArmor As Boolean = True
 
    pgp.SignFile("c:\INPUT.txt", store, _
		     signingKeyUserId, _
		     signingKeyPassword, _
		     "c:\INPUT.sig.txt", _
		     asciiArmor)
  Else
	Console.WriteLine("The key was not found!")
  End If
 End Sub
End Class

Back to Top

3. Signing a String message with a private key located in a file

If we wish to sign a string message directly we should use one of the SignString methods:

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
class SignString
{
  public static String Demo()
  {
    // message to be signed
    String plainString = "Hello World";
 
    // create an instance of the library
    PGPLib pgp = new PGPLib();
 
    // sign
    String signedString =
	pgp.SignString(plainString,
		       new FileInfo(@"c:\private_key.asc"),
		       "private key password");
 
    return signedString;
  }
}

VB.NET example

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Class SignString
  Public Shared Function Demo() As String
    ' message to be signed
    Dim plainString As String = "Hello World"
 
    ' create an instance of the library
    Dim pgp As New PGPLib()
 
    ' sign
    Dim signedString As String = _
	pgp.SignString(plainString, _
			New FileInfo("DataFiles\private_key.asc"), _
			"private key password")
    Return signedString
  End Function
End Class

Back to Top

4. Signing a String message with a private key located in a KeyStore

If we keep our keys in a KeyStore object we should use the overloaded version of the SignString method that accepts KeyStore in order to sign a String message:

C# example

using System;
using DidiSoft.Pgp;
 
class KeyStoreSignString
{
  public static String Demo()
  {
    string signingKeyUserId = "support@didisoft.com";
    string signingKeyPassword = "changeit";
 
    // initialize the key store
    KeyStore ks = new KeyStore(@"DataFiles\key.store",
			  "keystore password");
 
   // if this key store contains the desired key - sign,
   // otherwise notify that there is no such key
   if (ks.ContainsKey(signingKeyUserId))
   {
	PGPLib pgp = new PGPLib();
 
	string plainText = "Hello World";
 
	string signedString =
		pgp.SignString(plainText, ks,
				signingKeyUserId,
				signingKeyPassword);
	return signedString;
    }
    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 KeyStoreSignString
  Public Sub Demo()
    Dim signingKeyUserId As String = "support@didisoft.com"
    Dim signingKeyPassword As String = "changeit"
 
    ' initialize the key store
    Dim ks As New KeyStore("DataFiles\key.store", "changeit")
 
    ' if this key store contains a key with this recipient userId
    ' then 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 signedString As String = pgp.SignString(plainText, _
						ks, _
						signingKeyUserId, _
						signingKeyPassword)
    Else
     Console.WriteLine("No key with user Id:" + _
			signingKeyUserId + _
			" was found in this key store.")
    End If
  End Sub
End Class

Back to Top

5. Signing a stream with a private key supplied as Stream

We can also sign a stream directly by passing the data and the private key as streams.

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
public class SignStreamDemo
{
 public void Demo()
 {
  // create an instance of the library
  PGPLib pgp = new PGPLib();
  // should the output be ASCII or binary
  bool asciiArmor = true;
 
  // Just for the example the signed output is a 
  // MemoryStream object, It's Position property
  // will point at it's end after the method ends            
  Stream signedOutputStream = new MemoryStream();
 
  using (Stream dataStream = File.OpenRead(@"C:\INPUT.txt"))
  using (Stream keyStream = File.OpenRead(@"C:\private_key.asc")) 
  {
   // This is an arbitrary file name string that is 
   // associated with the signed data in the OpenPGP archive
   string internalFileNameLabel = "INPUT.txt";
   pgp.SignStream(dataStream,
		 internalFileNameLabel,
		 keyStream,
		 "private key passphrase",
		 signedOutputStream,
		 asciiArmor);
  }
 }
}

VB.NET example

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Public Class SignStreamDemo
 Public Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
  ' should the output be ASCII or binary
  Dim asciiArmor As Boolean = True
 
  ' Just for the example the signed output is a 
  ' MemoryStream object, It's Position property
  ' will point at it's end after the method ends		
  Dim signedOutputStream As Stream = New MemoryStream()
  Using dataStream As Stream = File.OpenRead("C:\INPUT.txt")
   Using keyStream As Stream = File.OpenRead("C:\private_key.asc")
    ' This is an arbitrary file name string that is 
    ' associated with the signed data in the OpenPGP archive
    Dim internalFileNameLabel As String = "INPUT.txt"
    pgp.SignStream(dataStream, _
                   internalFileNameLabel, _
                   keyStream, _
                   "private key passphrase", _
                   signedOutputStream, _
                   asciiArmor)
   End Using
  End Using
 End Sub
End Class

Back to Top

6. Signing a Stream with a private key located in a KeyStore

In this example the private key is stored in a KeyStore object and we want to sign data available as a Stream for reading.

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
public class SignStreamDemo
{
 public void Demo()
 {
  // create an instance of the library
  PGPLib pgp = new PGPLib();
  // should the output be ASCII or binary
  bool asciiArmor = true;
 
  // initialize the key store
  KeyStore ks = new KeyStore(@"c:\key.store", "keystore password");
 
  // Just for the example the signed output is a 
  // MemoryStream object, It's Position property
  // will point at it's end after the method ends            
  Stream signedOutputStream = new MemoryStream();
 
  using (Stream dataStream = File.OpenRead(@"C:\INPUT.txt"))
  {
     string privateKeyID = "A32BF480";
 
     // This is an arbitrary file name string that is 
     // associated with the signed data in the OpenPGP archive
     string internalFileNameLabel = "INPUT.txt";
     pgp.SignStream(dataStream,
                    internalFileNameLabel,
                    ks,
                    privateKeyID,
                    "private key passphrase",
                     signedOutputStream,
                     asciiArmor);
  }
 }
}

VB.NET example

Imports System
Imports System.IO
Imports DidiSoft.Pgp
 
Public Class SignStreamDemo
 Public Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
  ' should the output be ASCII or binary
  Dim asciiArmor As Boolean = True
 
  ' initialize the key store
  Dim ks As New KeyStore("c:\key.store", "keystore password")
 
  ' Just for the example the signed output is a 
  ' MemoryStream object, It's Position property
  ' will point at it's end after the method ends		
  Dim signedOutputStream As Stream = New MemoryStream()
  Using dataStream As Stream = File.OpenRead("C:\INPUT.txt")
   Dim privateKeyID As String = "A32BF480"
 
   ' This is an arbitrary file name string that is 
   ' associated with the signed data in the OpenPGP archive
   Dim internalFileNameLabel As String = "INPUT.txt"
   pgp.SignStream(dataStream, _
                  internalFileNameLabel, _
                  ks, _
                  privateKeyID, _
                  "private key passphrase", _
                  signedOutputStream, _
		  asciiArmor)
  End Using
 End Sub
End Class

Back to Top

7. Signing a file with the older version 3 OpenPGP signature format

Some old OpenPGP implementations do not recognize the current signature format and may require the old version 3 signature format. In order to create compatible signed data we can invoke the Sign methods that end with V3.

The example below illustrates signing a file with the old OpenPGP digital signature format.

C# example

using System;
using DidiSoft.Pgp;
 
public class SignDemoV3
{
 public void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
   // should the output be ASCII or binary
   bool asciiArmor = true;
 
   pgp.SignFileV3(@"C:\INPUT.txt",
                @"C:\private_key.asc",
                "private key passphrase",
                @"C:\OUTPUT.pgp",
                asciiArmor);
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Public Class SignDemoV3
 Public Sub Demo()
    ' create an instance of the library
    Dim pgp As New PGPLib()
 
    ' should the output be ASCII or binary
    Dim asciiArmor As Boolean = True
 
    pgp.SignFileV3("C:\INPUT.txt", _
                  "C:\private_key.asc", _
                  "private key passphrase", _
                  "C:\OUTPUT.pgp", _
                  asciiArmor)
 End Sub
End Class

Back to Top

8. Signing a file with a private key located in a KeyStore

This example is equivalent to the above one, except that the signing key resides in a KeyStore file. We can specify which key to be used for signing by it’s User Id, Key Hex ID as String or Key ID of type System.Int64.

C#

using System;
using DidiSoft.Pgp;
 
class KeyStoreSignFileV3
{
 public static void Demo()
 {
  // initialize the key store
  KeyStore store = new KeyStore(@"c:\key.store",
				"changeit");
 
  string signingKeyUserId = "support@didisoft.com";
  string signingKeyPassword = "changeit";
 
  // if this key store contains a key with this User Id,
  // then sign,
  // otherwise notify that there is no such key
  if (store.ContainsKey(signingKeyUserId))
  {
	// create an instance of the library
	PGPLib pgp = new PGPLib();
 
	// should the output be ASCII or binary
	bool asciiArmor = true;
 
	pgp.SignFileV3(@"c:\INPUT.txt", store,
		     signingKeyUserId,
		     signingKeyPassword,
		     @"c:\INPUT.sig.txt",
		     asciiArmor);
 
  }
  else
  {
	Console.WriteLine("The key was not found!");
  }
 }
}

VB.NET example

Imports System
Imports DidiSoft.Pgp
 
Class KeyStoreSignFileV3
 Public Shared Sub Demo()
  ' initialize the key store
  Dim store As New KeyStore("c:\key.store", "changeit")
 
  Dim signingKeyUserId As String = "support@didisoft.com"
  Dim signingKeyPassword As String = "changeit"
 
  ' if this key store contains a key with this User Id,
  'then sign,
  ' otherwise notify that there is no such key
  If store.ContainsKey(signingKeyUserId) Then
    ' create an instance of the library
    Dim pgp As New PGPLib()
 
    ' should the output be ASCII or binary
    Dim asciiArmor As Boolean = True
 
    pgp.SignFileV3("c:\INPUT.txt", store, _
		     signingKeyUserId, _
		     signingKeyPassword, _
		     "c:\INPUT.sig.txt", _
		     asciiArmor)
  Else
	Console.WriteLine("The key was not found!")
  End If
 End Sub
End Class

(Appendix A) Compression and Signature Hash

The default compression of the signed file is ZIP. It can be changed through the Compression property of the PGPLib class.

The default signature hash function is SHA-1 . It can be changed through the Hash property of the PGPLib class.
Back to Top

(Appendix B) Exception Handling

The main two checked exceptions thrown by all Sign methods are System.IO.IOException in case of an I/O error and DidiSoft.Pgp.PGPException in case of an OpenPGP related error.

In order to identify more thoroughly what went wrong, we can check is the thrown PGPException of a specific sub class.

Below is an example that illustrates how to perform that check and the possible exception sub classes expected from the Sign methods.

C# example

PGPLib pgp = new PGPLib();
try
{
  pgp.Sign...
}
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.Sign...
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

As you have learned from this chapter, OpenPGP signing does not protect the data from being read. It just appends a digital signature to verify the sender.

You may also consider reading about clear text signatures, detached signatures and one pass signed and encrypted data.

Methods used in this chapter:

PGPLib.SignFile Produces an OpenPGP archive containing the unencrypted data and an additional signature
PGPLib.SignString Produces an ASCII armored OpenPGP signed message
PGPLib.SignStream OpenPGP signs data supplied as Stream
PGPLib.SignFileV3 Produces an OpenPGP signed archive but with the older version 3 signature format