OpenPGP detached signatures are stored in a separate file from the data, which differs from the standard OpenPGP signature format containing the data and the signature combined in one file.
The detached signature is produced by a PGP private key. For the verification of detached signatures, we need the corresponding public key.
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 OpenPGP detached signing with DidiSoft OpenPGP Library for .NET
Detached signatures can be in binary format or in the OpenPGP ASCII armored form, so all methods used in the example below have a ascii armor boolean parameter (when true the output will be ASCII armored otherwise binary).
Detached signing a file
1. with a private key located in a file | Async
2. with a private key located in a KeyStore | Async
Detached signing a Stream
3. with a private key supplied as Stream | Async
4. with a private key located in a KeyStore | Async
Detached signing a String
5. Detached signing a string message | Async
Verifying detached signed data
6. verify detached signed file | Async
7. verify detached signed String message | Async
8. verify detached signed Stream data | Async
Appendix
A. Exception Handling
1. Detached signing a file
In this example, we will produce an OpenPGP 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 |
1-A. Async Detached signing a file
We can create an OpenPGP detached signature for a file with the help of the DetachedSignFileAsync method from the PGPLibAsync class:
using System; using System.Threading; using System.Threading.Tasks; using DidiSoft.Pgp; class DetachedSignFileAsyncDemo { public async Task Demo() { // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // should the output be binary (false) or ASCII armored (true) bool asciiArmor = true; // optional CancellationToken token = default(CancellationToken); await pgp.DetachedSignFileAsync(@"c:\INPUT.txt", @"c:\my_private_key.asc", "my key password", @"c:\INPUT.txt.sig", asciiArmor, token); } } |
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 |
2-A. Async creation of an OpenPGP detached signature from a File with a key in a KeyStore
We can also asynchronously create a PGP detached signature from a file with a private key located in a KeySore file.
For simplicity we use a dummy CancellationToken but it can be skipped altogether as it is an optional parameter.
using System; using System.Threading; using System.Threading.Tasks; using DidiSoft.Pgp; class DetachedSignFileAsyncKSDemo { public async Task Demo() { // initialize the key store KeyStore ks = KeyStore.OpenFile(@"c:\mykey.store", "key store password"); // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // 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"; // optional parameter CancellationToken token = default(CancellationToken); await pgp.DetachedSignFileAsync(@"C:\Test\INPUT.txt", ks, privateKeyUserId, privateKeyPassword, @"C:\Test\INPUT.txt.sig", asciiArmor, token); } } |
3. Producing a PGP 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; using (Stream dataFileStream = File.OpenRead(@"C:\INPUT.txt")) using (Stream privateKeyStream = File.OpenRead(@"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.OpenRead("C:\INPUT.txt") Dim privateKeyStream As Stream = File.OpenRead("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 |
3-A. Async PGP detached signature from a Stream
Detached OpenPGP signature from character data provided as a Stream can be created with PGPLibAsync.DetachedSignStream:
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using DidiSoft.Pgp; class DetachedSignStreamAsyncDemo { public async Task Demo() { // initialize the library PGPLibAsync pgp = new PGPLibAsync(); // should the output be binary or ASCII armored bool asciiArmor = true; // optional parameter CancellationToken token = default(CancellationToken); using (Stream dataFileStream = File.OpenRead(@"C:\INPUT.txt")) using (Stream privateKeyStream = File.OpenRead(@"C:\private_key.asc")) using (Stream outputSigned = File.Create(@"C:\INPUT.txt.sig")) { await pgp.DetachedSignStreamAsync(dataFileStream, privateKeyStream, "private key password", outputSigned, asciiArmor, token); } } } |
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"; using (Stream dataFileStream = File.OpenRead(@"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.OpenRead("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 |
4-A. Async detached signatures from a data Stream with а key located in a KeyStore
We can also create detached OpenPGP signatures with a key obtained from a KeyStore as you can see in the sample code below:
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using DidiSoft.Pgp; class DetachedSignStreamAsyncDemo2 { public async Task Demo() { // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // 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 = "office@didisoft.com"; // optional parameter CancellationToken token = default(CancellationToken); using (Stream dataFileStream = File.OpenRead(@"C:\INPUT.txt")) using (Stream outputSigned = File.Create(@"C:\INPUT.txt.sig")) { await pgp.DetachedSignStreamAsync(dataFileStream, ks, privateKeyUserId, "private key password", outputSigned, asciiArmor); } } } |
5. Detached signing a string message
Below is shown how to detached sign a string message and get the signature as a string too.
All DetachedSignString provided by the PGPLib class produce detached signatures in ASCII armored format as a String can contain only character data.
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 |
5-A. Async Detached signing a string message
OpenPGP detached signature over data contained in a variable of type String can be created with the DetachedSignStringAsync method from the PGPLibAsync class:
using System; using System.Threading; using System.Threading.Tasks; using DidiSoft.Pgp; class DetachedSignStringAsync { public async Task Demo() { String plainString = "Hello World"; // initialize the library PGPLibAsync pgp = new PGPLibAsync(); // this can also be the key serialzied as ASCII string string privateKey = @"c:\my_private_key.asc"; // optional parameter CancellationToken token = default(CancellationToken); String signatureString = await pgp.DetachedSignStringAsync(plainString, privateKey, "my key password", token); Console.WriteLine(signatureString); } } |
6. Verify a detached signed file
We need the original data file in order to verify against it the detached signature file.
Also the public OpenPGP key corresponding to the private key that created the signature is needed. When using a KeyStore we can verify the signature against all public keys contained in the KeyStore, this way omitting the need to provide different public key when receiving data from multiple recipients.
This is illustrated in the example code below:
using System; using DidiSoft.Pgp; using DidiSoft.Pgp.Exceptions; class DetachedVerifyFile { public static void Demo() { // create an instance of the library PGPLib pgp = new PGPLib(); SignatureCheckResult signatureCheck = pgp.DetachedVerifyFile(@"DataFiles\INPUT.txt", @"DataFiles\INPUT.txt.sig", @"DataFiles\public.key"); // the same verification process but against all public keys from a KeyStore KeyStore keystore = new KeyStore(@"DataFile\my.keystore", "my kjeystore pass"); signatureCheck = pgp.DetachedVerifyFile(@"DataFiles\INPUT.txt", @"DataFiles\INPUT.txt.sig", keystore); 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-A. Async Verifing a detached signed file
Asynchronous verification of a detached signature is provided by PGPLibAsync.DetachedVerifyFileAsync:
using System.Threading; using DidiSoft.Pgp; ... PGPLibAsync pgp = new PGPLibAsync(); // optional parameter CancellationToken token = default(CancellationToken); SignatureCheckResult check = await pgp.DetachedVerifyFileAsync(@"c:\INPUT.txt", @"c:\INPUT.txt.sig", @"c:\public.asc"); |
7. 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 |
6-A. Async verification of a detached signed String
Asynchronous verification of a detached signature is provided by PGPLibAsync.DetachedVerifyStringAsync:
using System.Threading; using DidiSoft.Pgp; ... PGPLibAsync pgp = new PGPLibAsync(); // optional parameter CancellationToken token = default(CancellationToken); // this can also be the public key serialized in ASCII armored format string publicKey = @"c:\public.asc"; SignatureCheckResult check = await pgp.DetachedVerifyStringAsync(messageString, signatureString, publicKey); |
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 |
7-A. Async verifying a detached signed Stream
When the input data and the signature are available as Streams we can used the set of DetachedVerifyStreamAsync methods in order to verify the consistency of the signature and the data.
using System.Threading; using DidiSoft.Pgp; ... PGPLibAsync pgp = new PGPLibAsync(); // optional parameter CancellationToken token = default(CancellationToken); using (Stream dataFileStream = File.OpenRead(@"C:\INPUT.txt")) using (Stream publicKeyStream = File.OpenRead(@"C:\public_key.asc")) using (Stream signedStream = File.OpenRead(@"C:\INPUT.txt.sig")) { SignatureCheckResult check = await pgp.DetachedVerifyStreamAsync(dataFileStream, signedStream, publicKeyStream, token); } |
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.DetachedVerifyFile | Verifies an OpenPGP detached signature available as file |
PGPLib.DetachedVerifyString | Verifies an OpenPGP detached signature available as String |
PGPLib.DetachedVerifyStream | Verifies an OpenPGP detached signature available as Stream |
PGPLibAsync.DetachedSignFileAsync | Asynchronosuly Creates an OpenPGP detached signature for a file |
PGPLibAsync.DetachedSignStreamAsync | Asynchronosuly Creates an OpenPGP detached signature for the contents of a Stream |
PGPLibAsync.DetachedSignFileAsync | Asynchronosuly Creates an OpenPGP detached signature for a String message |
PGPLibAsync.DetachedVerifyFileAsync | Asynchronosuly Verifies an OpenPGP detached signature available as file |
PGPLibAsync.DetachedVerifyStringAsync | Asynchronosuly Verifies an OpenPGP detached signature available as String |
PGPLibAsync.DetachedVerifyStreamAsync | Asynchronosuly Verifies an OpenPGP detached signature available as Stream |