The OpenPGP sign operation converts the input data into the 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.
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
Synchronous code
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
Async code
Async Signing a file
1-A. with a key in a file
2-A. with a key in a KeyStore
Async Signing a String message
3-A. with a private key from a file
4-A with a private key from a KeyStore
Async Signing a Stream
5-A. with a private key as a Stream
6-A. with a private key from a KeyStore
Appendix
A. Compression and Hash function
B. Exception Handling
1. Signing a file with a private key located in a file
OpenPGP signing combines the data to be signed and the signature in a single output file, much similar to PDF signatures. If you need only signature then you should use detached signatures.
People often get confused that the data is protected but it’s not, if you need signed and protected data then use sign and encrypt.
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 |
2. 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 specify which key to be used for signing by its User Id.
C# sample
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 |
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 |
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 |
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 |
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 |
7. Signing a file with the older version 3 OpenPGP signature format
Note: Use this format only if you know that the recipient require it.
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 |
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 its 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 |
1-A Async signing a file with a key from a file
Async OpenPGP cryptography methods are methods are available in the PGPLibAsync class that extends PGPLib.
This demo function would take in a file to sign, a key file, output file and a parameter for the output type. The function would then sign the file with the key from the key file, and output the result as either binary or ASII armored, depending on the output type parameter.
As the invoked method is asynchronous, there is an optional last parameter of type CancellationToken, that can be used to cancel the execution of the operation.
C# example
using System; using System.Threading; using DidiSoft.Pgp; class SignFileAsync { public async void Demo() { // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // should output be binary (false) or ASCII armored (true) bool asciiArmor = true; // optional parameter CancellationToken token = default(CancellationToken); await pgp.SignFileAsync(@"DataFiles\INPUT.txt", @"DataFiles\private.key", "changeit", @"DataFiles\OUTPUTs.pgp", asciiArmor, token); } } |
2-A Async signing a file with a key from a KeyStore
This example is equivalent to the above one, except that the signing key resides in a KeyStore file. The signing key can be specified by part or the whole User Id string, or by its hexadecimal key 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!"); } } } |
3-A Async OpenPGP signing a String message with a private key from a file
If we wish to sign a string message and get the result as ASCII String then we can use one of the SignStringAsync methods:
C# example
using System; using System.IO; using DidiSoft.Pgp; class SignString { public async Task Demo() { String plainString = "Hello World"; // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); String signedString = await pgp.SignStringAsync(plainString, @"DataFiles\private.key", "changeit"); return signedString; } } |
4-A. Async Signing a String message with a private from a KeyStore
If we keep our keys in a KeyStore object we should use the overloaded version of the SignStringAsync method that accepts KeyStore:
C# example
using System; using DidiSoft.Pgp; class KeyStoreSignString { public async Task Demo() { string signingKeyUserId = "support@didisoft.com"; string signingKeyPassword = "changeit"; // initialize the key store KeyStore ks = new KeyStore(@"DataFiles\key.store", "changeit"); // if this key store contains the desired key - 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"; 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; } } } |
5-A. Async Signing a stream with a private key 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 SignStreamAsyncDemo { public async void Demo() { // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // should output be binary (false) or ASCII armored (true) bool asciiArmor = true; // The output must be a stream that supports writing Stream outputSignedStream = new MemoryStream(); // Optional parameter // dummy cancellation tokjen for the example CancellationToken token = default(CancellationToken); // The data stream and the private key stream can be any kind of stream // that supports reading using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt")) using (Stream privateKeyStream = File.OpenRead(@"DataFiles\private.key")) { string privateKeyPassword = "changeit"; // File name label to be associated with the data in the OpenPGP archive // You can also use the constant PGPLib.CONSOLE // or just make up an arbitrary string here string internalFileNameLabel = "INPUT.txt"; await pgp.SignStreamAsync(dataStream, internalFileNameLabel, privateKeyStream, privateKeyPassword, outputSignedStream, asciiArmor, token); } } } |
6-A. Signing a Stream with a private key located in a KeyStore
In this example, the private key is taken form KeyStore object. The output Stream is just a local variable, but in a real world scenario you will usually obtain a Stream ready from writing from some kind of persistent storage.
C# example
using System; using System.IO; using DidiSoft.Pgp; public class SignStreamAsyncDemo { public async void Demo() { KeyStore store = KeyStore.OpenFile(@"DataFiles\key.store", "changeit"); string signingKeyUserId = "support@didisoft.com"; string signingKeyPassword = "changeit"; // create an instance of the library PGPLibAsync pgp = new PGPLibAsync(); // should the output signed file be ASCII or binary bool asciiArmor = true; // File name label to be associated with the data in the OpenPGP archive // You can also use the constant PGPLib.CONSOLE // or just make up an arbitrary string here string internalFileNameLabel = "INPUT.txt"; // The output must be a stream that supports writing Stream outputSignedStream = new MemoryStream(); // The data stream can be any kind of stream that supports reading using (Stream dataStream = File.OpenRead(@"DataFiles\INPUT.txt")) { await pgp.SignStreamAsync(dataStream, internalFileNameLabel, store, signingKeyUserId, signingKeyPassword, outputSignedStream, 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 |
(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 subclass.
Below is an example that illustrates how to perform that check and the possible exception subclasses 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 |
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.
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 use the Sign methods that end with V3 (for example SignFileV3).
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 |
PGPLibAsync.SignFileAsync | Produces an OpenPGP archive containing the unencrypted data and an additional signature |
PGPLibAsync.SignStringAsync | Produces an ASCII armored OpenPGP signed message |
PGPLibAsync.SignStreamAsync | OpenPGP signs data supplied as Stream |