OpenPGP Smartcard

As of version 1.9.1 DidiSoft OpenPGP Library for .NET supports OpenPGP smartcards. In this article, we are going to illustrate how to use an OpenPGP smartcard (here Yubikey) and perform the OpenPGP operations that require a private key – sign, clear text sign, detached sign and decrypt.

In order to use the example code from this chapter, you will need an OpenPGP smartcard, like Yubikey, Nitrokey, or another brand compatible with the OpenPGP smartcard specification version 2 or above.

The smartcard functionality resides in its own assembly DidiSoft.Pgp.Smartcard.dll available for .NET Framework only.

Table of contents

Initializing the Smartcard

The physical smartcard is represented by the class DidiSoft.Pgp.Smartcard.SmartcardKeyStore. An instance of this class must be created and used along with DidiSoft.Pgp.PGPLib to perform PGP cryptography operations

Get a list of available smartcards

A smartcard is referred by its name. To get a list of the names of the available smartcards we use the static GetCards() method:
C#

1
string[] smartcards = DidiSoft.Pgp.Smartcard.SmartcardKeyStore.GetCards();

VB.NET

1
Dim smartcards As String() = DidiSoft.Pgp.Smartcard.SmartcardKeyStore.GetCards()

Create an instance of SmartcardKeyStore

There are several ways to create an instance of the SmartcardKeyStore class:

1
2
3
4
5
6
7
8
9
10
using DidiSoft.Pgp.Smartcard;
...
// open the first available smartcard
SmartcardKeyStore ks = SmartcardKeyStore.OpenDefaultSmartcard("123456");
// equivalent to the above
SmartcardKeyStore ks = SmartcardKeyStore.OpenSmartcard(SmartcardKeyStore.GetCards()[0], "123456");
// open a smartcard by name
SmartcardKeyStore ks = new SmartcardKeyStore("Yubikey", "123456");
// equivalent to the above
SmartcardKeyStore ks = SmartcardKeyStore.OpenSmartcard("Yubikey", "123456");

 

Importing keys into the Smartcard

The current version of the library still doesn’t provide methods for importing private keys into the smartcard. This feature will be available in the next version.

You have to use the tools provided by the smartcard vendor or GnuPG.

Signing

After we have initialized an instance of SmartcardKeyStore we can perform OpenPGP sign operation similar to the way we do it with keys contained in a standard KeyStore. The example code below will use the master signing key from the smartcard.

If there is no such key an exception of type DidiSoft.Pgp.Exceptions.WrongPrivateKeyException will be thrown :

C# example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using DidiSoft.Pgp;
using DidiSoft.Pgp.Smartcard;
 
class SignSmartcardExample
{
 public static void Main()
 {
  // initialize the smartcard
  SmartcardKeyStore ks = new SmartcardKeyStore("Yubikey", "123456");
 
  // create an instance of the library
  PGPLib pgp = new PGPLib();
 
  bool asciiOutput = true;
  // sign a file
  pgp.SignFile("INPUT.txt", ks, "INPUT.pgp", asciiOutput);
 
  // sign a string message
  string signedAsciiArmored = pgp.SignString("Hello World", ks);
 }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Smartcard
 
Class SignSmartcardExample
 Public Shared Sub Demo()
  ' initialize the smartcard
  Dim ks As New SmartcardKeyStore("Yubikey", "123456")
 
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  ' sign file
  Dim asciiOutput As Boolean = True
  pgp.SignFile("INPUT.txt", ks, "OUTPUT.pgp", asciiOutput)
 
 ' sign string message
  Dim signedAsciiArmored As String = pgp.SignString("Hello World", ks)
 End Sub
End Class

Clear text sign

Clear text signing is similar to the example above. Again the master signing key from the smartcard will be used for the operation and the actual signature creation will be performed on the smartcard itself:

C# example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using DidiSoft.Pgp;
using DidiSoft.Pgp.Smartcard;
 
class ClearSignSmartcardExample
{
 public static void Main()
 {
  // initialize the smartcard
  SmartcardKeyStore ks = new SmartcardKeyStore("Yubikey", "123456");
 
  // create an instance of the library
  PGPLib pgp = new PGPLib();
 
  // clear sign a file
  pgp.ClearSignFile("INPUT.txt", ks, HashAlgorithm.SHA1, "OUTPUT.pgp");
 
  // cler sign a string message
  string clearSigned = pgp.ClearSignString("Hello World", ks, HashAlgorithm.SHA1);
 }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Smartcard
 
Class ClearSignSmartcardExample
 Public Shared Sub Demo()
  ' initialize the smartcard
  Dim ks As New SmartcardKeyStore("Yubikey", "123456")
 
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  ' clear sign file
  pgp.ClearSignFile("INPUT.txt", ks, HashAlgorithm.SHA1, "OUTPUT.pgp")
 
 ' clear sign string message
  Dim clearSigned As String = pgp.ClearSignString("Hello World", ks, HashAlgorithm.SHA1)
 End Sub
End Class

 

Detached sign

Detached signatures are created the same way as with keys contained in an ordinary KeyStore. Again the master signing key is used for the signature operation

C# example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using DidiSoft.Pgp;
using DidiSoft.Pgp.Smartcard;
 
class DtachedSignSmartcardExample
{
 public static void Main()
 {
  // initialize the smartcard
  SmartcardKeyStore ks = new SmartcardKeyStore("Yubikey", "123456");
 
  // create an instance of the library
  PGPLib pgp = new PGPLib();
 
  bool asciiOutput = true;
  // create a detached signature for a file
  pgp.DetachedSignFile("INPUT.txt", ks, "INPUT.sig", asciiOutput);
 
  // create a detached signature for a string message
  string detachedSignatureAsciiArmored = pgp.DetachedSignString("Hello World", ks);
 }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Smartcard
 
Class SignSmartcardExample
 Public Shared Sub Demo()
  ' initialize the smartcard
  Dim ks As New SmartcardKeyStore("Yubikey", "123456")
 
  ' create an instance of the library
  Dim pgp As New PGPLib()
 
  ' create a detached signature for a file
  Dim asciiOutput As Boolean = True
  pgp.DetachedSignFile("INPUT.txt", ks, "OUTPUT.pgp", asciiOutput)
 
 ' create a detached signature for string message
  Dim detachedSsignatureAsciiArmored As String = pgp.DetachedSignString("Hello World", ks)
 End Sub
End Class

Decrypting

Decrypting with an OpenPGP smartcard involves the decryption sub-key located on the card. The actual decrypt operation over the symmetric session key is performed transparently on the smartcard.

In case there is no such key DidiSoft.Pgp.Exceptions.WrongPrivateKeyException will be thrown.

C# example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System;
using DidiSoft.Pgp;
using DidiSoft.Pgp.Smartcard;
 
public class KeyStoreDecryptFile
{
 public static void Demo()
 {
  // initialize theKeyStore
  SmartcardKeyStore keyStore = new SmartcardKeyStore("Yubikey", "123456");
 
  // initialize the library
  PGPLib pgp = new PGPLib();	
  pgp.DecryptFile(@"c:\INPUT.pgp", keyStore, @"c:\OUTPUT.txt");			
 }
}

VB.NET example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Imports System
Imports DidiSoft.Pgp
Imports DidiSoft.Pgp.Smartcard
 
Public Class KeyStoreDecryptFile
 Public Shared Sub Demo()
  ' initialize theKeyStore
  Dim keyStore As New SmartcardKeyStore("Yubikey", "123456")
 
  ' initialize the library
  Dim pgp As New PGPLib()
  pgp.DecryptFile("c:\INPUT.pgp", keyStore, "c:\OUTPUT.txt")
 End Sub
End Class

Exception handling

When working with an external source of data like a smartcard exceptional conditions may occur. In order to be able to handle such cases gracefully, you can catch these exceptions:

DidiSoft.Pgp.Smartcard.SmartcardException – an IOException subclass thrown in cases when the smartcard is not available or the communication with the smartcard is malfunctioning.

The following subclasses of DidiSoft.Pgp.PGPException may also be thrown

DidiSoft.Pgp.Exceptions.WrongPasswordException – when constructing the SmartcardKeyStore class the provided unlock password (PIN1) is wrong.

DidiSoft.Pgp.Exceptions.WrongPrivateKeyException – when trying to perform signing operation and no signing key is found on the smartcard, or when trying to decrypt and no decryption key is loaded on the smartcard.

Summary

This tutorial chapter was a starting point for using OpenPGP smartcards with DidiSoft OpenPGP Library for .NET. For the listed examples you need a smartcard compatible with the OpenPGP smartcard specification.