Verify pgp signed data in .NET

When we receive a signed or clear text signed OpenPGP data it contains data and signature combined in one place. We can both verify that it has a good signature from a trusted senderand extract the original message at the same time, or just verify the signature.

For the verification, we need the public key of the sender. Of course, we can ignore completely the digital signature and extract the data with simple decryption.

Table of contents

Verify a signed or clear text signed file

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

Verify a signed or clear text signed String message

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

Verify a signed or clear text signed Stream

5. with a sender’s public key as Stream | Async
6. with a sender’s public key located in a KeyStore | Async

Appendix

A. Exception handling

1. Verifying a signed or clear text signed file using sender’s public key located also in a file

There is also an overloaded version of the VerifyFile method that allows just to verify the digital signature without actually extracting the data.

C# example

using System;
using DidiSoft.Pgp;
 
public class VerifyDemo
{
    public void Demo()
    {
       // create an instance of the library
       PGPLib pgp = new PGPLib();
 
       // check the signature and extract the data 
       SignatureCheckResult signatureCheck = 
           pgp.VerifyFile(@"C:\Test\INPUT.pgp",
                          @"C:\Test\public_key.asc",
                          @"C:\Test\OUTPUT.txt");
 
       if (signatureCheck == SignatureCheckResult.SignatureVerified)
       {
           Console.WriteLine("Signare OK");
       }
       else if (signatureCheck == SignatureCheckResult.SignatureBroken)
       {
           Console.WriteLine("Signare of the message is either broken or forged");
       }
       else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
       {
           Console.WriteLine("The provided public key doesn't match the signature");
       }
       else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
       {
           Console.WriteLine("This message is not digitally signed");
       }
    }
}

A VB.NET example is also available here.

Async verifying OpenPGP signed file


Async version of the VerifyFile method is available in the PGPLibAsync class that extends PGPLib. Here in addition to the same set of parameters an optional last parameter of type CancellationToken is available, which can be used to stop the execution of the method call.

This example just like the one above verifies the signature integrity of the OpenPGP signed data and extracts the data itself.

using System.Threading;
using DidiSoft.Pgp;
 
class VerifyFileAsync
{
 public async void Demo()
 {
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // empty cancellation token for this example
  CancellationToken token = default(CancellationToken);
 
  // verify
  SignatureCheckResult signatureCheck = await pgp.VerifyFileAsync(@"DataFiles\OUTPUTs.pgp",
						 @"DataFiles\public.key",
						 @"DataFiles\OUTPUT.txt",
					        token);
 
 Console.WriteLine(" Signature is " + signatureCheck.ToString());
 }
}

2. Verify a signed or clear text signed file using sender’s public key located in a KeyStore

This example is equivalent to the above one, except that the public key of the sender is located in a KeyStore object. The digital signature will be verified among all the public keys in the specified KeyStore.

C# example

using System;
using DidiSoft.Pgp;
 
class KeyStoreVerifyFile
{
 public static void Demo()
 {
	// create an instance of the library
	PGPLib pgp = new PGPLib();
 
	// initialize the KeyStore
	// If the keystore file does not exists, it is created.
	KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
	// verify OpenPGP signed or clear text signed file
	string outputFileLocation = @"c:\OUTPUT.txt";
	SignatureCheckResult signatureCheck = pgp.VerifyFile(@"c:\signed.pgp",
					                     ks,
				                             outputFileLocation);
 
	// Print the results
	Console.WriteLine("Extracted data in " + outputFileLocation);
        if (signatureCheck == SignatureCheckResult.SignatureVerified)
        {
           Console.WriteLine("Signare OK");
        }
        else if (signatureCheck == SignatureCheckResult.SignatureBroken)
        {
           Console.WriteLine("Signare of the message is either broken or forged");
        }
        else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
        {
           Console.WriteLine("The provided public key doesn't match the signature");
        }
        else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
        {
           Console.WriteLine("This message is not digitally signed");
        }
 }
}

Async verifying OpenPGP signed file against public keys from a KeyStore


The advantage of having keys in a KeyStore is that we can verify OpenPGP signed files from multiple recipients, without specifying explicitly the correct public key to verify the data. That is, we store all known public keys in a single KeyStore and this way we can verify the signed data sent from and anyone who’s key is in the KeyStore.

using System;
using System.Threading;
using System.Threading.Tasks;
using DidiSoft.Pgp;
class VerifyFileAsyncKS
{
 public async Task Demo()
 {
  // obtain an OpenPGP signed or clear text signed file
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // initialize the KeyStore
  // If the keystore file does not exists, it is created.
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
  // empty cancellation token for this example
  CancellationToken token = default(CancellationToken);
 
  // verify
  string outputFileLocation = @"DataFiles\OUTPUT.txt";
  SignatureCheckResult signatureCheck = await pgp.VerifyFileAsync(@"DataFiles\OUTPUT.pgp",
									 ks,
									 outputFileLocation,
									 token);
 
  // Print the results
  Console.WriteLine("Extracted data in " + outputFileLocation);
  Console.WriteLine("Signature verification result " + outputFileLocation);
 }
}

3. Verify a String message using the sender’s public key located in a file

The operation of verifying OpenPGP signed string (which is actually ASCII armored OpenPGP signed message), consists of both extracting the data and verifying the signature attached to it.

The VerifyString method return the SignatureCheckResult and the actual data stored inside the OpenPGP signed message is collected in a out parameter.
Of course an overloaded version of VerifyString exists that only verifies the signature without extracting the data:

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
class VerifyString
{
 public static void Demo()
 {
   String signedString = ...   // obtain an OpenPGP signed message
 
   PGPLib pgp = new PGPLib();   
 
   // Extract the message and check the validity of the signature
   String plainText;
   SignatureCheckResult signatureCheck = pgp.VerifyString(signedString,
					@"c\public_key.asc",
					out plainText);
 
   // the same as above but only verifies the signature without extracting the data
   pgp.VerifyString(signedString, @"c\public_key.asc");
 
   // Print the results
   Console.WriteLine("Extracted plain text message is " + plainText);
   if (signatureCheck == SignatureCheckResult.SignatureVerified)
   {
           Console.WriteLine("Signare OK");
   }
   else if (signatureCheck == SignatureCheckResult.SignatureBroken)
   {
          Console.WriteLine("Signare of the message is either broken or forged");
   }
   else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
   {
           Console.WriteLine("The provided public key doesn't match the signature");
   }
   else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
   {
           Console.WriteLine("This message is not digitally signed");
   }
 }
}

This same code above but in VB.NET can be seen here

Async verifying OpenPGP signed string


Actually we can pass a key file filesystem location or key data in ASCII-armored format in all methods of both PGPLib and PGPLibAsync that expect a key parameter of type string.

This example below illustrates asynchronous verification of OpenPGP signed data available in ASCII-armored format. Because of the nature of OpenPGP signed data, that contains both the data and the signature combined, here the VerifyStringAsync method returns Tuple<SignatureCheckResult, String> so we can get both the signature check and the data:

using System;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class VerifyStringAsyncDemo
{
 public async Task Demo()
 {
  // obtain an OpenPGP signed message
  String signedString = ... // obtain an ASCII armored OpenPGP signed data
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  string publicKey = @"DataFiles\public.key"; // this can also be ASCII armored string with key data
 
  Tuple&lt;SignatureCheckResult, String&gt; signatureCheck = await pgp.VerifyStringAsync(signedString, publicKey);
  SignatureCheckResult signatureCheckResult = signatureCheck.Item1;
  String plainText = signatureCheck.Item2;
 
  // Print the results
  Console.WriteLine("Signature check result is " + signatureCheckResult);
  Console.WriteLine("Extracted plain text message is " + plainText);		
 }
}

4. Verify a string message using sender’s public key located in a KeyStore

If we keep our OpenPGP keys in a KeyStore we should use the overloaded VerifyString method that accepts a KeyStore object:

C# example

using System;
using DidiSoft.Pgp;
 
class KeyStoreVerifyString
{
 public static void Demo()
 {
   // obtain an OpenPGP signed message
   String signedString = KeyStoreSignString.Demo();
 
   // Extract the message and check the validity of the signature
   String plainText;
   PGPLib pgp = new PGPLib();
   KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
   SignatureCheckResult signatureCheck = pgp.VerifyString(signedString,
 					                  ks,
					                  out plainText);
 
   // Print the results
   Console.WriteLine("Extracted plain text message is " + plainText);
 
   if (signatureCheck == SignatureCheckResult.SignatureVerified)
   {
          Console.WriteLine("Signare OK");
   }
   else if (signatureCheck == SignatureCheckResult.SignatureBroken)
   {
          Console.WriteLine("Signare of the message is either broken or forged");
   }
   else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
   {
         Console.WriteLine("The provided public key doesn't match the signature");
   }
   else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
   {
         Console.WriteLine("This message is not digitally signed");
   }
 }
}

Async verifying of OpenPGP string message with key from a Keystore


The equivalent code, but asynchronous is available below.

using System;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class VerifyStringAsyncKS
{
 public async Task Demo()
 {
 
  String signedString = ...   // obtain an OpenPGP signed message
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // initialize the KeyStore
  // If the keystore file does not exists, it is created.
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
  // Extract the message and check the validity of the signature
  Tuple&lt;SignatureCheckResult, String&gt; signatureCheckResult = await pgp.VerifyStringAsync(signedString, ks);
  SignatureCheckResult signatureCheck = signatureCheckResult.Item1;
  String plainText = signatureCheckResult.Item2;
 
  // Print the results
  Console.WriteLine("Extracted plain text message is " + plainText);
	if (signatureCheck == SignatureCheckResult.SignatureVerified)
	{
		Console.WriteLine("Signature OK");
	}
	else if (signatureCheck == SignatureCheckResult.SignatureBroken)
	{
		Console.WriteLine("Signature of the message is either broken or forged");
	}
	else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
	{
		Console.WriteLine("The provided public key doesn't match the signature");
	}
	else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
	{
		Console.WriteLine("This message is not digitally signed");
	}
  }
}

5. Verify a signed or clear text signed Stream with a sender’s public key as Stream

This example shows how to verify data that is in a Stream for reading. The extracted data is stored also in a Stream.
You may notice also a second call of the VerifyStream which illustrates how to only verify without getting the signed data:

using System;
using System.IO;
using DidiSoft.Pgp;
 
public class VerifyStream
{
 public void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   // The data and the public key can be any kind of stream
   Stream dataStream = File.OpenRead(@"c:\signed.pgp");
   Stream publicKeyStream = File.OpenRead(@"c:\public_key.asc");
 
   Stream outputStream = new MemoryStream();
 
   // verify signed or clear text signed stream
   SignatureCheckResult signatureCheck = pgp.VerifyStream(dataStream,
					publicKeyStream,
					outputStream);
 
   // of course we can verify only the signature without extracting the data
   SignatureCheckResult signatureCheckIgnoreData = pgp.VerifyStream(dataStream,
					publicKeyStream,
					outputStream);
 
   if (signatureCheck == SignatureCheckResult.SignatureVerified)
   {
        Console.WriteLine("Signare OK");
   }
   else if (signatureCheck == SignatureCheckResult.SignatureBroken)
   {
        Console.WriteLine("Signare of the message is either broken or forged");
   }
   else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
   {
         Console.WriteLine("The provided public key doesn't match the signature");
   }
   else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
   {
         Console.WriteLine("This message is not digitally signed");
   }
 
   // We must reinitialize the output in order to read from it later
   (outputStream as MemoryStream).Position = 0;
 }
}

Equivalent code in VB.NET is available here

Async verification of OpenPGP data from a Stream


If we want to asynchronously verify signed or clear text signed data coming as a Stream, we should use PGPLibAsync.VerifyStreamAsync:

using System;
using System.IO;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class VerifyStreamAsync
{
 public async Task Demo()
 {
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // The data and the public key can be any kind of stream
  Stream dataStream = File.OpenRead(@"DataFiles\OUTPUTs.pgp");
  Stream publicKeyStream = File.OpenRead(@"DataFiles\public.key");
 
  // for this example the signed data is extracted in a memory Stream 
  // fore illustrative purposes
  using (Stream outputStream = new MemoryStream())
  {
    // verify signed or clear text signed stream
    // and extract the embedded data
    SignatureCheckResult signatureCheck = await pgp.VerifyStreamAsync(dataStream,
									publicKeyStream,
									outputStream);
  }
 
  if (signatureCheck == SignatureCheckResult.SignatureVerified)
  {
	Console.WriteLine("Signature OK");
  }
  else if (signatureCheck == SignatureCheckResult.SignatureBroken)
  {
	Console.WriteLine("Signature of the message is either broken or forged");
  }
  else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
  {
	Console.WriteLine("The provided public key doesn't match the signature");
  }
  else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
  {
	Console.WriteLine("This message is not digitally signed");
  }
 }
}

6. Verify a Stream when the public key of the sender is in a KeyStore

This example is equivalent to the above one, except that the sender’s public key is located in a KeyStore.

C# example

using System;
using System.IO;
using DidiSoft.Pgp;
 
class KeyStoreVerifyStream
{
 public static void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   // initialize the KeyStore that contains the sender's public key
   KeyStore ks = new KeyStore(@"c:\my_key.store", "changeit");
 
   // The data and the output can be any kind of stream
   Stream dataStream = File.OpenRead(@"c:\signed.pgp");
   Stream outputStream = new MemoryStream();
 
   // verify
   string outputFileLocation = @"c:\OUTPUT.txt";
   SignatureCheckResult signatureCheck = pgp.VerifyStream(dataStream,
					 ks,
					 outputStream);
 
   // We must reinitialize the output in order to read from it later
   (outputStream as MemoryStream).Position = 0;
 
   if (signatureCheck == SignatureCheckResult.SignatureVerified)
   {
           Console.WriteLine("Signare OK");
   }
   else if (signatureCheck == SignatureCheckResult.SignatureBroken)
   {
           Console.WriteLine("Signare of the message is either broken or forged");
   }
   else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
   {
           Console.WriteLine("The provided public key doesn't match the signature");
   }
   else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
   {
           Console.WriteLine("This message is not digitally signed");
   }
 }
}

Async verifying Stream with KeyStore


The same code above rewritten to use public keys stored in a KeyStore is available below:

using System;
using System.IO;
using System.Threading.Tasks;
using DidiSoft.Pgp;
 
class VerifyStreamAsyncKS
{
 public async Task Demo()
 {
  // obtain an OpenPGP signed or clear text signed file
  await new SignFileAsync().Demo();
 
  // create an instance of the library
  PGPLibAsync pgp = new PGPLibAsync();
 
  // initialize the KeyStore that contains the sender's public key
  KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit");
 
  // The data and the output can be any kind of stream
  using (Stream dataStream = File.OpenRead(@"DataFiles\OUTPUTs.pgp"))
  using (Stream outputStream = new MemoryStream())
  { 
	// verify
	SignatureCheckResult signatureCheck = await pgp.VerifyStreamAsync(dataStream,
										 ks,
										 outputStream);
 
	if (signatureCheck == SignatureCheckResult.SignatureVerified)
	{
		Console.WriteLine("Signature OK");
	}
	else if (signatureCheck == SignatureCheckResult.SignatureBroken)
	{
		Console.WriteLine("Signature of the message is either broken or forged");
	}
	else if (signatureCheck == SignatureCheckResult.PublicKeyNotMatching)
	{
		Console.WriteLine("The provided public key doesn't match the signature");
	}
	else if (signatureCheck == SignatureCheckResult.NoSignatureFound)
	{
		Console.WriteLine("This message is not digitally signed");
	}
  }
 }
}

Appendix A. Exception Handling

The exception handling code when verifying signed data shall follow the general exception strategy.

This example illustrates the expected exceptions from the methods with a Verify name prefix.

Exception handling in C#

using System;
using DidiSoft.Pgp;
using DidiSoft.Pgp.Exceptions;
 
public class VerifyDemo
{
 public void Demo()
 {
   // create an instance of the library
   PGPLib pgp = new PGPLib();
 
   try
   {
	SignatureCheckResult signatureCheck = pgp.Verify ...
   }
   catch (PGPException e)
   {
     if (e is NonPGPDataException)
     {
	// The input is not an OpenPGP archive or is corrupted
     }
     else if (e is WrongPublicKeyException)
     {
       // The supplied public key is not an OpenPGP public key
       // or is corrupted
     }
     else if (e is FileIsEncryptedException)
     {
	// The input file is OpenPGP encrypted. 
        // You will have to use one of the Decrypt 
        // or DecryptAndVerify methods
     }
     else
     {  // general OpenPGP error
  	Console.WriteLine(e.Message);
     }
   }
 }
}

Exception handling in VB.NET

Imports System
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Exceptions
 
Public Class VerifyDemo
 Public Sub Demo()
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  Try
	Dim signatureCheck As SignatureCheckResult = pgp.Verify...
  Catch e As PGPException
   If TypeOf e Is NonPGPDataException Then
	' The input file is not an OpenPGP archive or is corrupted
   ElseIf TypeOf e Is WrongPublicKeyException Then
	' The supplied public key is not an OpenPGP public key 
        ' or is corrupted
   ElseIf TypeOf e Is FileIsEncryptedException Then
	' The input is OpenPGP encrypted. 
        ' You will have to use one of the Decrypt or 
        ' DecryptAndVerify methods instead
   Else 
        ' general OpenPGP error
	Console.WriteLine(e.Message)
   End If
  End Try
 End Sub
End Class

Back to Top

Summary

In this chapter, we have shown how to verify an OpenPGP digital signatures and extract the signed content. The examples above apply only to signed and clear text signed data.

Additional methods exist for verification of detached signatures and one pass signed and encrypted data.

You may also be interested in analyzing OpenPGP archive and checking the signing Key ids before performing signature verification.

Methods used in this chapter:

PGPLibAsync.VerifyFileAsyncAsync Verifies an OpenPGP signed or clear text signed file

PGPLib.VerifyFile Verifies an OpenPGP signed or clear text signed file
PGPLib.VerifyString Verifies an OpenPGP signed or clear text signed String message
PGPLib.VerifyStream Verifies an OpenPGP signed or clear text signed Stream
PGPLibAsync.VerifyStringAsync Async Verifies an OpenPGP signed or clear text signed String message
PGPLibAsync.VerifyStreamAsync Async Verifies an OpenPGP signed or clear text signed Stream