The KeyStore class encapsulates storage that contains public and private OpenPGP keys. Referencing the keys in the key store is done either by their Key ID, Key Hex ID or by their User ID.
The DidiSoft.Pgp.KeyStore object is an implementation of the PKCS 12 standard
Common operations
- In a File
- In-Memory
- Custom storage (e.g. database)
2. Save
3. Auto save and backup
4. List keys
5. Referencing a key
6. Check for a key
7. Reusing an existing .pkr and .skr files from PGP®, GnuPG, EBS®
8. Importing and Exporting
9. Change password
1. Create and open a KeyStore
1.1 KeyStore in a File
The KeyStore data is usually stored in a file. The KeyStore constructor shown below is used when we want to create or open an existing KeyStore file:
// C# KeyStore ks = new KeyStore(@"c:\my.keystore", "my password"); |
' VB.NET Dim ks As New KeyStore("c:\my.keystore", "my password") |
An equivalent static construction is available:
// C# KeyStore ks = KeyStore.OpenFile(@"c:\my.keystore", "my password"); |
' VB.NET Dim ks As KeyStore = KeyStore.OpenFile("c:\my.keystore", "my password") |
The key store location can be also a valid shared network path with read\write access, e.g. \\SharedServer\\SharedFolder\\my.keystore
1.2 In-memory KeyStore
An in-memory KeyStore stores temporary its content in memory and disappears if not serialized explicitly on program exit.
// C# KeyStore ks = KeyStore.OpenInMemory(); |
' VB.NET Dim ks As KeyStore = KeyStore.OpenInMemory() |
The property IsInMemory can be used to check is this instance backed by a file or is a temporary in-memory key storage.
1.3 Custom storage
A custom storage for the KeyStore class provides a custom serialization mechanism. Examples of such storage can be a relational or NoSQL database, a remote web service, etc. Using the custom constructor we can rely on the Auto-save mechanism and thus don’t have to call Save explicitly on each key modification operation.
In order to create a KeyStore backed by a custom storage, a constructor that expects a parameter of type DidiSoft.Pgp.Storage.IKeyStorage must be used:
public interface IKeyStorage { // loads the KeyStore from the provided Stream Stream GetInputStream(); // saves the KeyStore into the specified Stream void Store(Stream dataStream, int dataLength); } |
Here is how to create an implementation of the IKeyStorage interface that uses a database table in MS SQL Server® :
CREATE TABLE KeyStore(KeyStoreData varbinary(MAX) NULL); |
The KeyStore contents are stored as one large BLOB (binary object) in either encrypted or non-encrypted format, depending on the KeyStore constructor used.
using System; using System.IO; using System.Data.SqlClient; public class DBKeyStorage : DidiSoft.Pgp.Storage.IKeyStorage { private string connectionString; public DBKeyStorage(string connectionString) { this.connectionString = connectionString; } public Stream GetInputStream() { SqlConnection myConnection = new SqlConnection(this.connectionString); myConnection.Open(); using (SqlCommand cmdIns = new SqlCommand(sqlIns, myConnection)) using (SqlDataReader dr = cmdIns.ExecuteReader()) { byte[] data = new byte[] { }; if (dr.Read()) { data = (byte[])dr["KeyStoreData"]; } return new MemoryStream(data); } } public void Store(Stream dataStream, int dataLength) { SqlConnection myConnection = new SqlConnection(this.connectionString); myConnection.Open(); string sqlIns = "select top 1 KeyStoreData from KeyStore"; SqlCommand cmdSelect = new SqlCommand(sqlIns, myConnection); SqlDataReader dr = cmdSelect.ExecuteReader(); bool rowExists = dr.Read(); dr.Close(); cmdSelect.Dispose(); if (rowExists) { sqlIns = "UPDATE KeyStore SET KeyStoreData = @data"; SqlCommand cmdIns = new SqlCommand(sqlIns, myConnection); cmdIns.Parameters.AddWithValue("@data", dataStream); cmdIns.ExecuteNonQuery(); cmdIns.Dispose(); } else { sqlIns = "INSERT INTO KeyStore(KeyStoreData) VALUES (@data)"; SqlCommand cmdIns = new SqlCommand(sqlIns, myConnection); cmdIns.Parameters.AddWithValue("@data", dataStream); cmdIns.ExecuteNonQuery(); cmdIns.Dispose(); } myConnection.Close(); } } |
Example usage of the above KeyStore custom storage is :
DBKeyStorage keyStorage = new DBKeyStorage(txtConnectionString.Text); // cosntructor without a password expects the data to be unencrypted KeyStore ksUnencrypted = new KeyStore(keyStorage); // cosntructor with a password expects the data to be encrypted KeyStore ksEncrypted = new KeyStore(keyStorage, "changeit"); |
2. Saving a KeyStore
The Save() method of the KeyStore class writes back the KeyStore object to its file on the disk. This operation has no effect over an in-memory KeyStore.
3. Auto save and backup
By default, the AutoSave property is on, and after each operation, the modified state of the KeyStore is saved to the disk automatically. The BackupOnSave property is true by default too and on each save, a backup file is stored with the previous state before the save operation (only one backup though).
4. List the contained keys
A list of all keys (unsorted) is obtained with the KeyStore.GetKeys() method. Each key is represented as an instance of DidiSoft.Pgp.KeyPairInformation.
If you wish the list to be sorted you can do so with by ordering based on properties of the KeyPairInformation class like:
KeyPairInformation[] keysByCreationTime = ks.GetKeys().OrderBy(item => item.CreationTime).ToArray();
The example below lists and prints the keys in a way similar to GnuPG/gpg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | using System; using System.Text; using DidiSoft.Pgp; public class KeyStoreListKeys { public static void Demo() { // initialize the key store KeyStore ks = KeyStore.OpenFile(@"c:\my_key.store", "keystore password"); KeyPairInformation[] keys = ks.GetKeys(); StringBuilder sb = new StringBuilder(); sb.Append("Type".PadRight(10)); sb.Append("Key Id".PadRight(30)); sb.Append("Created".PadRight(20)); sb.Append("User Id"); Console.WriteLine(sb.ToString()); foreach (KeyPairInformation key in keys) { sb.Remove(0, sb.Length); String keyType = null; if (key.HasPrivateKey) { keyType = "pub/sec"; } else { keyType = "pub"; } sb.Append(keyType.PadRight(10)); sb.Append(Convert.ToString(key.KeyId).PadRight(30)); sb.Append(key.CreationTime.ToShortDateString().PadRight(20)); foreach (String id in key.UserIds) { sb.Append(id); } Console.WriteLine(sb.ToString()); } } } |
VB.NET example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | Imports System Imports System.Text Imports DidiSoft.Pgp Public Class KeyStoreListKeys Public Shared Sub Demo() ' initialize the key store Dim ks As KeyStore = KeyStore.OpenFile("c:\my_key.store", "keystore password") Dim keys As KeyPairInformation() = ks.GetKeys() For Each key As KeyPairInformation In keys Dim sb As New StringBuilder() sb.Append("Type".PadRight(10)) sb.Append("Key Id".PadRight(30)) sb.Append("Created".PadRight(20)) sb.Append("User Id") Console.WriteLine(sb.ToString()) sb.Remove(0, sb.Length) Dim keyType As String = Nothing If key.HasPrivateKey Then keyType = "pub/sec" Else keyType = "pub" End If sb.Append(keyType.PadRight(10)) sb.Append(Convert.ToString(key.KeyId).PadRight(30)) sb.Append(key.CreationTime.ToShortDateString().PadRight(20)) For Each id As String In key.UserIds sb.Append(id) Next Console.WriteLine(sb.ToString()) Next End Sub End Class |
5. Referencing a key
Here you can see how to reference a key located in a KeyStore instance through its Key ID, part of or the whole User ID and by the short or long hexadecimal representation of the Key ID.
5.1 Referencing a key by User ID
A full User ID of an OpenPGP key is usually in the format “Name of the user <email>”. In older versions of the library, a key had to be referenced by the full User ID.
As User ID can be specified by only a part of it, for example, “John Doe <john@doe.com>” can be referenced only with “john@doe.com”. Strict User ID referencing can be turned on by setting the property KeyStore.PartialMatchUserIds to false. The case sensitivity of the User ID referencing is controlled with the property KeyStore.CaseSensitiveMatchUserIds.
A key can also have multiple User ID’s associated with it and we can use any of them.
In this example, we show how to export a key to a file by specifying it by its User ID.
C# example
KeyStore ks = KeyStore.OpenFile(@"c:\my_key.store", "keystore password"); String partnerUserId = "partner@company.com"; bool asciiArmor = true; ks.ExportPublicKey(@"c:\public_key.asc", partnerUserId, asciiArmor); |
VB.NET example
Dim ks As KeyStore = KeyStore.OpenFile("c:\my_key.store", "keystore password") Dim partnerUserId As String = "partner@company.com" Dim asciiArmor As Boolean = True ks.ExportPublicKey("c:\public_key.asc", partnerUserId, asciiArmor) |
5.2 Referencing a key by hexadecimal Key ID
The hexadecimal representation of a Key ID is a string 16 characters long (2 characters for each byte). In order to ease users, command line PGP and GnuPG used a short hex Key ID of 8 characters representing the lower 4 bytes of the real Key ID (which is a System.Int64 value). You can use both short and long hexadecimal Key ID’s with the OpenPGP Library for .NET.
This example demonstrates how to reference an OpenPGP public key by its hexadecimal Key ID.
C# example
1 2 3 4 5 6 7 | KeyStore ks = KeyStore.OpenFile(@"c:\my_key.store", "keystore password"); String partnerKeyId = "197B3E74"; // we can use any the short and the long form String partnerLongKeyId = "A01234B7197B3E74"; bool asciiArmor = true; ks.ExportPublicKey(@"c:\public_key.asc", partnerKeyId, asciiArmor); |
VB.NET example
1 2 3 4 5 6 | Dim ks As KeyStore = KeyStore.OpenFile("c:\my_key.store", "keystore password") ' we can use both the short and the long form Dim partnerKeyId As String = "197B3E74" Dim partnerLongKeyId As String = "A01234B7197B3E74" Dim asciiArmor As Boolean = True ks.ExportPublicKey("c:\public_key.asc", partnerKeyId, asciiArmor) |
5.3 Referencing a key by Key ID
The real OpenPGP Key ID is a 64-bit integer value. The Key Hex ID is constructed by the lower 32 bits of the real Key ID. The library accepts the real Key ID as a number of type Long (System.Int64).
Below we demonstrate how to obtain the real Key ID that corresponds to a given User ID.
C# example
KeyStore ks = KeyStore.OpenFile(@"c:\my_key.store", "keystore password"); String partnerUserId = "partner@company.com"; long keyId = ks.GetKeyIdForUserId(partnerUserId); |
VB.NET example
Dim ks As KeyStore = KeyStore.OpenFile("c:\my_key.store", "keystore password") Dim partnerUserId As String = "partner@company.com" Dim keyId As Long = ks.GetKeyIdForUserId(partnerUserId) |
6. Check for a key
Here you can see how to check is a given key contained in a KeyStore instance. A check can be made by part of the key User ID, the hexadecimal Key ID or the raw Key ID:
C# example
KeyStore ks = KeyStore.OpenFile(@"c:\my_key.store", "keystore password"); String partnerUserId = "partner@company.com"; bool containsKey = ks.ContainsKey(partnerUserId); |
VB.NET example
Dim ks As KeyStore = KeyStore.OpenFile("c:\my_key.store", "keystore password") Dim partnerUserId As String = "partner@company.com" Dim containsKey As Boolean = ks.ContainsKey(partnerUserId) |
7. Reusing an existing .pkr and .skr files from PGP®, GnuPG, EBS®
We can easily reuse keys from an existing PGP(r) installation. In the example below, we will create an in-memory located KeyStore instance and load the keys used by PGP(r). The same code can be used with the GnuPG keyring files too.
C# example
KeyStore ks = KeyStore.OpenInMemory(); ks.ImportKeyRing(@"C:\Users\JohnDoe\Documents\PGP\pubring.pkr"); ks.ImportKeyRing(@"C:\Users\JohnDoe\Documents\PGP\secring.skr"); |
VB.NET example
Dim ks As KeyStore = KeyStore.OpenInMemory() ks.ImportKeyRing("C:\Users\JohnDoe\Documents\PGP\pubring.pkr") ks.ImportKeyRing("C:\Users\JohnDoe\Documents\PGP\secring.skr") |
8. Importing and Exporting keys
Please refer to the complete chapters that explain Importing and Exporting keys.
9. Change password
In order to change the password of a KeyStore we have to open it and afterward change its Password property.
(available as of version 1.7.8.8)
// C# KeyStore ks = KeyStore.OpenFile(@"c:\my.keystore", "my password"); ks.Password = "new password"; ks.Save(); |
' VB.NET Dim ks As KeyStore = KeyStore.OpenFile("c:\my.keystore", "my password") ks.Password = "new password" ks.Save() |
Summary
This chapter discussed basic operations with the KeyStore object.
Topics not discussed here which can be of further interest are importing keys, exporting keys, generating keys, deleting keys.