Discuss this help topic in SecureBlackbox Forum

Store an OpenPGP key on hardware devices

As most hardware security modules (HSMs) only work with raw cryptographic keys, using them for storing OpenPGP keys (which come with a bunch of extra data, such as expiration indicator, user ID or signatures) can be tricky. However, it IS possible, and with little effort you can create OpenPGP messages signed with a key residing on hardware device - or decrypt incoming OpenPGP messages with it.

Technically, I was cheating a bit when saying that it is possible to *store* OpenPGP keys on hardware token. It actually isn't. However, it is possible to use raw keys residing on the token as if they were OpenPGP keys - without revealing their real nature.

What we will be doing is building an OpenPGP 'envelope' over the existing raw cryptographic key. We will do that every time that we need to use the key for signing, decryption or when we need to send it to another OpenPGP party. This conversion procedure is strictly defined, so for the same cryptographic key pair we will always end up with the same OpenPGP key, with the same key material and KeyID.

Firstly, you will need to actually create a keypair on the token. This task is outside of this how-to; you will normally do that with a driver software that comes with your token or with other components shipped with SecureBlackbox. One thing should be noted here. Most OpenPGP implementations support RSA, DSA and Elgamal (sometimes erroneously called DH) keys. Most hardware devices don't support Elgamal keys. And, finally, DSA keys can only be used for signing. Therefore choosing RSA algorithm for your keypair might be a good idea from the compatibility viewpoint.

Assume now that we have a hardware device with a [raw] keypair on it and we want to use that keypair as an OpenPGP key. You will need to do the following:

  1. Open your device using TElPKCS11CertStorage component (you may find Enumerate PKCS#11 certificates article useful).
  2. Use Keys[] property (get_Keys() method in some editions) of the storage object to find the needed key. You can use KeyID, KeyLabel or Subject property to identify a particular key on the token. Note that PKCS#11 key ID has no relation to OpenPGP key ID; PKCS#11 key ID is a random identifier assigned to every key object stored on the token. In most cases, public and private parts of the same keypair have the same IDs. OpenPGP key ID, in turn, is a sort of 'key fingerprint', which uniquely identifies a key with specific cryptographic parameters.
    TElRSAKeyMaterial rsakm = null;
    for (int i = 0; i < storage.KeyCount; i++)
    {
        if ((storage.get_Keys(i) is TElRSAKeyMaterial) && (storage.get_Keys(i).SecretKey) && (storage.get_Keys(i).KeyLabel == "My OpenPGP key"))
        {
      rsakm = (TElRSAKeyMaterial)storage.get_Keys(i);
      break;
        }
    }
    
  3. Create an OpenPGP container for your secret key and import the key material there:
    TElPGPSecretKey openPgpKey = new TElPGPSecretKey();
    openPgpKey.ImportKeyMaterial(rsakm, DateTime.UtcNow.AddYears(1));
    

That's it. The openPgpKey object now contains the HSM-based key material and can be used for signing and decryption. You can export its public part if you wish to send it to someone. Note that the public key bound to the secret openPgpKey doesn't contain any user IDs/signatures on this stage, so adding a user ID and signing it with a certification signature might be a good idea if you need to send your public key to an off-the-market OpenPGP software. This can only be done once; after creating a full-featured OpenPGP key you save it to your public keyring and re-use it every time you need to send it to someone.

That is how it is done with RSA keys. If you choose to use DSA keys, the import process becomes slightly more complicated, as DSA keys consist of two pieces when stored on the token - of their private and public parts (RSA keys sometimes do to, but, in contrast to DSA keys, you can build a public RSA key from a private one, so you only need the private key in RSA case).

So, to build a DSA OpenPGP key, do the following:

TElDSAKeyMaterial skm = null;
TElDSAKeyMaterial pkm = null;

// looking for the secret part
for (int i = 0; i < Storage.KeyCount; i++)
{
    if ((storage.get_Keys(i) is TElDSAKeyMaterial) && (storage.get_Keys(i).SecretKey) && (storage.get_Keys(i).KeyLabel == "My OpenPGP key"))
    {
  skm = (TElDSAKeyMaterial)storage.get_Keys(i);
  break;
    }
}

// looking for the public part using the secret part
for (int i = 0; i  < storage.KeyCount; i++)
{
    if ((storage.get_Keys(i) is TElDSAKeyMaterial) && (storage.get_Keys(i).PublicKey))
    {
  TElDSAKeyMaterial cand = (TElDSAKeyMaterial)storage.get_Keys(i);

  if ((SBUtils.Unit.CompareMem(cand.P, skm.P)) && (SBUtils.Unit.CompareMem(cand.Q, skm.Q)) && (SBUtils.Unit.CompareMem(cand.G, skm.G)))
  {
     pkm = cand;
            break;
  }
    }
}

if ((skm != null) && (pkm != null))
{
    TElPGPSecretKey openPgpKey = new TElPGPSecretKey();
    openPgpKey.ImportKeyMaterial(skm, pkm, DateTime.UtcNow.AddYears(1));
}

How To articles about OpenPGP key management

Discuss this help topic in SecureBlackbox Forum