The standard OpenPGP signed format contains the data and the digital signature combined in one file. In contrast, OpenPGP detached signatures are stored in a separate file from the data. The signature is produced by our OpenPGP private key. For the verification of detached signatures, we need the public key of the sender.
A common file naming convention for files containing detached signatures is to have the same name as the original file with an additional .sig filename extension at the end.
Below we are going to demonstrate how to perform detached signing with DidiSoft OpenPGP Library for .NET
Detached signing a file
1. with a private key located in a file
2. with a private key located in a KeyStore
Detached signing a Stream
3. with a private key supplied as Stream
4. with a private key located in a KeyStore
Detached signing a String
5. Detached signing a string message
Verifying detached signed data
6. verify detached String message
7. verify detached Stream data
Appendix
A. Exception Handling
1. Detached signing a file
In this example, we will produce a detached signature for a file and store the signature also in a file.
C# example
using System; using DidiSoft.Pgp; public class DetachedSignDemo { public static void Demo() { // create an instance of the library PGPLib pgp = new PGPLib(); // should the output be binary (false) or ASCII armored (true) bool asciiArmor = true; pgp.DetachedSignFile(@"c:\INPUT.txt", @"c:\my_private_key.asc", "my key password", @"c:\INPUT.txt.sig", asciiArmor); } } |
VB.NET example
Imports System Imports DidiSoft.Pgp Public Class DetachedSignDemo Public Shared Sub Demo() ' create an instance of the library Dim pgp As New PGPLib() ' should the output be binary (false) or ASCII armored (true) Dim asciiArmor As Boolean = True pgp.DetachedSignFile("c:\INPUT.txt", _ "c:\my_private_key.asc", _ "my key password", _ "c:\INPUT.txt.sig", _ asciiArmor) End Sub End Class |
2. Detached signing a file with a private key located in a KeyStore
This example is equivalent to the above one, except that the private key used for signing is located in a KeyStore. The private signing key can be referred with its User Id or Key Hex Id.
C# example
using System; using DidiSoft.Pgp; public class DetachedSignDemo { public void Demo() { // initialize the key store KeyStore ks = KeyStore.OpenFile(@"c:\mykey.store", "key store password"); // create an instance of the library PGPLib pgp = new PGPLib(); // should the output be ASCII (true) or binary (false) bool asciiArmor = true; // The private key can be specified through it's Key Hex Id too string privateKeyUserId = "support@didisoft.com"; string privateKeyPassword = "key password"; pgp.DetachedSignFile(@"C:\Test\INPUT.txt", ks, privateKeyUserId, privateKeyPassword, @"C:\Test\INPUT.txt.sig", asciiArmor); } } |
VB.NET example
Imports System Imports DidiSoft.Pgp Public Class DetachedSignDemo Public Shared Sub Demo() ' initialize the key store Dim ks As KeyStore = KeyStore.OpenFile("DataFiles\key.store", "key store password") ' create an instance of the library Dim pgp As New PGPLib() ' should the output be ASCII (true) or binary (false) Dim asciiArmor As Boolean = True ' The private key can be specified through it's Key Hex Id too Dim privateKeyUserId As String = "support@didisoft.com" Dim privateKeyPassword As String = "private key password" pgp.DetachedSignFile("C:\Test\INPUT.txt", _ ks, _ privateKeyUserId, _ privateKeyPassword, _ "C:\Test\INPUT.txt.sig", _ asciiArmor) End Sub End Class |
3. Produce a detached signature from a Stream
In this example, we will produce a detached signature for data provided as Stream. The detached signature is also stored in a Stream.
C# example
using System; using DidiSoft.Pgp; public class DetachedSignStreamDemo { public static void Demo() { // initialize the library PGPLib pgp = new PGPLib(); // should the output be binary or ASCII armored bool asciiArmor = true; Stream dataFileStream = File.Open(@"C:\INPUT.txt"); Stream privateKeyStream = File.Open(@"C:\private_key.asc"); using (Stream outputSigned = File.Create(@"C:\INPUT.txt.sig") { pgp.DetachedSignStream(dataFileStream, privateKeyStream, "private key password", outputSigned, asciiArmor); } } } |
VB.NET example
Imports System Imports DidiSoft.Pgp Public Class DetachedSignStreamDemo Public Shared Sub Demo() ' initialize the library Dim pgp As New PGPLib() ' should the output be binary or ASCII armored Dim asciiArmor As Boolean = True Dim dataFileStream As Stream = File.Open("C:\INPUT.txt") Dim privateKeyStream As Stream = File.Open("C:\private_key.asc") Using outputSigned As Stream = File.Create("C:\INPUT.txt.sig") pgp.DetachedSignStream(dataFileStream, _ privateKeyStream, _ "private key password", _ outputSigned, _ asciiArmor) End Using End Sub End Class |
4. Produce a detached signature from a Stream with а key located in a KeyStore
This example is equivalent to the above one except that the private signing key is located in a KeyStore.
C# example
using System; using DidiSoft.Pgp; public class DetachedSignStreamDemo { public static void Demo() { // create an instance of the library PGPLib pgp = new PGPLib(); // should output be binary (false) or ASCII armored (true) bool asciiArmor = true; // initialize the KeyStore KeyStore ks = new KeyStore(@"c:\mykey.store", "key store password"); // The private key can be specified through it's Key Hex Id too string privateKeyUserId = "support@didisoft.com"; Stream dataFileStream = File.Open(@"C:\INPUT.txt"); using (Stream outputSigned = File.Create(@"C:\INPUT.txt.sig")) { pgp.DetachedSignStream(dataFileStream, ks, privateKeyUserId, "private key password", outputSigned, asciiArmor); } } } |
VB.NET example
Imports System Imports DidiSoft.Pgp Public Class DetachedSignStreamDemo Public Shared Sub Demo() ' initialize the library Dim pgp As New PGPLib() ' should the output be binary or ASCII armored Dim asciiArmor As Boolean = True ' initialize the key store Dim ks As New KeyStore("DataFiles\key.store", "key store password") ' The private key can be specified through it's Key Hex Id too Dim privateKeyUserId As String = "support@didisoft.com" Dim dataFileStream As Stream = File.Open("C:\INPUT.txt") Using outputSigned As Stream = File.Create("C:\INPUT.txt.sig") pgp.DetachedSignStream(dataFileStream, _ ks, _ privateKeyUserId, _ "private key password", _ outputSigned, _ asciiArmor) End Using End Sub End Class |
5. Detached signing a string message
Below is shown how to detached sign a string message and get the signature as a string too.
C# example
using System; using System.IO; using DidiSoft.Pgp; class DetachedSignStringDemo { public void Demo() { String plainString = "Hello World"; // initialize the library PGPLib pgp = new PGPLib(); Stream privateKeyStream = File.OpenRead(@"c:\my_private_key.asc"); String signatureString = pgp.DetachedSignString(plainString, privateKeyStream, "my key password"); Console.WriteLine(signatureString); } } |
VB.NET example
Imports System Imports System.IO Imports DidiSoft.Pgp Class DetachedVerifyString Public Sub Demo() Dim plainString As String = "Hello World" ' initialize the library Dim pgp As New PGPLib() Dim privateKeyStream As Stream = File.OpenRead("c:\my_private_key.asc") Dim signatureString As String = _ pgp.DetachedSignString(plainString, _ privateKeyStream, _ "my key password") Console.WriteLine(signatureString) End Sub End Class |
6. Verify a detached signed String
Below is shown how to verify a detached String signature. For the verification, we also need the String message for which it was created.
C# example
using System; using System.IO; using DidiSoft.Pgp; class DetachedVerifyStringDemo { public void Demo() { String plainString = "Hello World"; // initialize the library PGPLib pgp = new PGPLib(); Stream privateKeyStream = File.OpenRead(@"c:\private_key.asc"); String signedString = pgp.DetachedSignString(plainString, privateKeyStream, "key password"); Console.WriteLine(signedString); Stream publicKeyStream = File.OpenRead(@"c:\public_key.asc"); bool correct = pgp.DetachedVerifyString(plainString, signedString, publicKeyStream); if (correct) { Console.WriteLine("Signature is correct"); } else { Console.WriteLine("Signature is wrong."); } } } |
VB.NET example
Imports System Imports System.IO Imports DidiSoft.Pgp Class DetachedVerifyString Public Sub Demo() Dim plainString As String = "Hello World" ' initialize the library Dim pgp As New PGPLib() Dim privateKeyStream As Stream = File.OpenRead("c:\private_key.asc") Dim signedString As String = pgp.DetachedSignString(plainString, _ privateKeyStream, _ "key password") Console.WriteLine(signedString) Dim publicKeyStream As Stream = File.OpenRead("c:\private_key.asc") Dim correct As Boolean = pgp.DetachedVerifyString(plainString, _ signedString, _ publicKeyStream) If correct Then Console.WriteLine("Signature is correct") Else Console.WriteLine("Signature is wrong.") End If End Sub End Class |
7. Verify a detached signed Stream data
This example demonstrates how to verify detached Stream data. The data, the detached signature and the public key that will be used for the verification are obtained as Streams from files, but of course, they can be any kind of Stream subclass.
C# example
using System; using System.IO; using DidiSoft.Pgp; class DetachedVerifyDemo { public static void Demo() { // create an instance of the library PGPLib pgp = new PGPLib(); bool signatureIsValid = false; using (Stream dataStream = File.OpenRead(@"c:\INPUT.txt")) { using (Stream signatureStream = File.OpenRead(@"c:\INPUT.txt.sig")) { using (Stream publicKeyStream = File.OpenRead(@"c:\public_key.asc")) { signatureIsValid = pgp.DetachedVerifyStream(dataStream, signatureStream, publicKeyStream); } } } if (signatureIsValid) { Console.WriteLine("Signature is valid."); } else { Console.WriteLine("Signature is invalid!"); } } } |
VB.NET example
Imports System Imports System.IO Imports DidiSoft.Pgp Class DetachedVerifyDemo Public Shared Sub Demo() ' create an instance of the library Dim pgp As New PGPLib() Dim signatureIsValid As Boolean = False Using dataStream As Stream = File.OpenRead("DataFiles\INPUT.txt") Using signatureStream As Stream = File.OpenRead("DataFiles\INPUT.txt.sig") Using publicKeyStream As Stream = File.OpenRead("DataFiles\public.key") signatureIsValid = _ pgp.DetachedVerifyStream(dataStream, _ signatureStream, _ publicKeyStream) End Using End Using End Using If signatureIsValid Then Console.WriteLine("Signature is valid.") Else Console.WriteLine("Signature is invalid!") End If End Sub End Class |
Appendix A. Exception Handling
All DetachedSign and DetachedVerify methods throw System.IO.IOException in case of an I/O error and DidiSoft.Pgp.PGPException in case of an OpenPGP related error.
We can investigate further an OpenPGP exception cause by trying to check is the exception of a given subclass defined in the DidiSoft.Pgp.Exceptions namespace.
Below is an example that illustrates what are the expected exception subclasses from the DetachedSign and DetachedVerify methods.
C# example
PGPLib pgp = new PGPLib(); try { pgp.DetachedSign... } 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 } } try { pgp.DetachedVerify... } 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.NonPGPDataException) { // The supplied detached signed data is corrupted or is not an OpenPGP data at all } else { // General OpenPGP error non among the above } } |
VB.NET example
Dim pgp As New PGPLib() Try pgp.DetachedSign... 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 Try pgp.DetachedVerify... 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.NonPGPDataException Then ' The supplied detached signed data is corrupted or is not an OpenPGP data at all Else ' General OpenPGP error non among the above End If End Try |
Summary
In this chapter we have discussed OpenPGP detached signatures and how to produce them using DidiSoft OpenPGP Library for .NET.
List of methods used:
PGPLib.DetachedSignFile | Creates an OpenPGP detached signature for a file |
PGPLib.DetachedSignStream | Creates an OpenPGP detached signature for the contents of a Stream |
PGPLib.DetachedSignFile | Creates an OpenPGP detached signature for a String message |
PGPLib.DetachedVerifyString | Verifies an OpenPGP detached signature available as String |
PGPLib.DetachedVerifyStream | Verifies an OpenPGP detached signature available as Stream |