Implementing OpenPGP Web of Trust in Java

The Web of Trust (WoT) is one of the advanced topics in the OpenPGP cryptography. Its primary goal is to provide custom authentication of keys, allowing everyone to participate in a decentralized certificate authority.

The implementation of the Web of Trust has been revised in version 2.6.4 of DidiSoft OpenPGP Library for Java and this article reflects this updated model.

Table of Contents

1. Quick introduction to the WoT
2. Customizing the WoT
2.1. trust inheritance
2.2. marginals needed
3. Signing a key
4. Signing as a trusted introducer
5. Setting full trust to a key
6. Setting marginal trust
7. Current limitations
8. WoT in versions prior to 2.6.4
9. External sources

1. Quick introduction to the WoT

The Web of Trust was first introduced by Phil Zimmermann in 1992 in PGP version 2.0.

The OpenPGP specification defines the trust amount as a value ranging from 0-255. For practical reasons we have adopted only the constants below:

// fully trusted
com.didisoft.pgp.TrustLevel.TRUSTED = 120
// partially trusted
com.didisoft.pgp.TrustLevel.MARGINAL = 60
// do not trust 
com.didisoft.pgp.TrustLevel.NONE

An OpenPGP key is considered trusted (can be also met as “valid” in other sources) if:

  • it is signed by a private key contained in our KeyStore (this applies to the private keys as they are self signed)
  • we have marked it trusted in our key storage
  • it has been signed by a key which we consider trusted
  • it has been signed by a number of keys which we consider marginally trusted
Our WoT implementation works only with keys that are located in a KeyStore object.

2. Customizing the Web of Trust

There are two settings that we can use to customize the Web of Trust implementation in the library: trust depth and number of marginals needed.

2.1 Trust depth inheritance

Let’s suppose that we have the following keys and signature relationship:

A -> B -> C ->D

If  we mark key A as fully trusted and we set the trust depth to be:

KeyStore ks = new KeyStore();
ks.setMaxTrustDepth(3);

then key D won’t be trusted, as the number of hops from a fully trusted key exceeds the maximum trust depth.

2.2 Number of marginals needed

The number of signatures from keys that we trust marginally, needed to consider given key as trusted can be customized with:

KeyStore ks = new KeyStore();
ks.setMarginalsNeeded(3);

3. Signing a key

With the code snippet below we sign a partner’s public key with our private key, and indicate this way that we consider it to be a trusted key.

If afterwards we export the key and send it to a third party, they will also consider it trusted if they trust our key.

KeyStore ks = ...
ks.signPublicKey("partner Id", "my Key Id", "my Key password");

4. Signing as a trusted introducer

We can also sign a key as a trusted introducer. Let’s suppose we have the following signature relationship:
(Our key) -> A -> B
If we sign key A as a trusted introducer then we automatically trust key B as well:

KeyStore ks = ...
ks.signPublicKeyAsTrustedIntroducer("partner Id", "my Key Id", "my Key password");

When the maximum trust depth is larger than 1 it has the same effect as the plain signing. But if we set

KeyStore ks = new KeyStore();
ks.setMaxTrustDepthCheck(1);

Then key B will NOT be trusted if key A is only signed. It has to be signed as a trusted introducer explicitly.

5. Setting full trust to a key

By setting the trust on a key we mark the key as trusted, but that modification is stored only in the containing KeyStore instance. If the key is exported afterwards and sent to a third party, they won’t know of this.

KeyStore ks = ...
ks.setTrust("partner Id", com.didisoft.pgp.TrustLevel.TRUSTED);

As with the signing of a key, if the maximum trust depth is greater than 1 then this key introduces the trust to all keys that have signatures from it (maximum trust depth hops below).

6. Setting marginal trust to a key

The marginal trust of a key indicates that we trust partially that key. But if a few such keys have signed a particular key we can fully trust that key, just like a merchant asking for two forms of ID.

KeyStore ks = ...
ks.setTrust("partner Id", com.didisoft.pgp.TrustLevel.MARGINAL);

If we have the following signature relationship:
A -> X, B -> X, C -> X
and A, B, and C are marginally trusted in our KeyStore then key X is considered fully trusted.

7. Current limitations of the library

The Web of Trust model provided by OpenPGP provides great space for building sophisticated authentication solutions. Below are mentioned some of the current limitations in our implementation:

– Trusted introducer signatures are hard coded only for 1 level down (i.e. apply only for keys that have a signature from the introducer).

– One signature from a trusted key is needed for a key to be treated as trusted (in GnuPG for example this can be customized, the default there is also 1).

8. Web of Trust in versions prior to 2.6.4

The API methods for implementing Web of Trust was first introduced in version 2.5.8 of the library.

Until version 2.6.4 a key is considered trusted if:

  • it is signed by a private key contained in our KeyStore (this applies to the private keys as they are self signed)
  • we have marked it trusted in our key storage
  • it has been signed by a key that we have signed as a trusted introducer

There was no practical application of keys with marginal trust and no nested inheritance of trust.

9. External sources

External sources that you may find interesting and are related to the OpenPGP Web of Trust concept are listed below:

http://en.wikipedia.org/wiki/Web_of_trust

http://www.pgpi.org/doc/pgpintro/#p17

http://www.gnupg.org/gph/en/manual.html#AEN335

http://penguincopter.blogspot.com/2011/10/web-of-trust.html

Summary

In this chapter we have tried to introduce the API provided by OpenPGP Library for Java for implementing the Web of Trust in your applications. Probably you will find some of the explanations confusing, so please don’t hesitate to contact our technical support if you have any additional questions.

List of methods used

KeyStore.signPublicKey signs a third party public key with our private key
KeyStore.signPublicKeyAsTrustedIntroducer signs a third party public key as a trust introducer
KeyStore.setTrust sets our private trust amount in a third party key
KeyStore.isTrusted returns should a third party key be considered trusted
KeyStore.setMaxTrustDepth sets the maximum levels of trust inheritance
KeyStore.setMarginalsNeeded sets how many signatures from marginally trusted keys are needed in order a key to be considered trusted