POP - Using JWT to prove possession of a key

Today I want to talk about using JWT to enable a client prove possession of a key.

(Proof of key possession is commonly abbreviated as POP.)

Why?

In which cases is it interesting for a client to be able to prove possession of a key to a third party? Mike Jones has articulated a few in this paper. A good example is when you would like to allow a client to generate a public/private key pair that could be used as the basis for message-level (or application-level) security, signing or encryption. (Message or app-level as opposed to transport-level, which is covered by TLS.)

A keypair is a good basis for message or app-level signing or encryption. The client uses its private key to sign a thing, and then a receiver can use the corresponding public key to verify the signed thing. But the question is, how can the receiver be assured that it is this particular client that owns the key?

There are a number of different ways to do this.

Mike Jones and John Bradley have authored IETF RFC 7800 to describe how to do it, with JWT. The title of this RFC is: "Proof of Possession Key Semantics for JSON Web Tokens." If you are like me, you have a hard time parsing that phrase. I don't know why they didn't use a simpler phrase, like "Using JWT to prove possession of a key".

JWT POP in Apigee Edge

Let's explore that memo and see how we might use Apigee Edge to implement the flow described within it.

RFC 7800 identifies three parties in the POP token flow:

  • Presenter
  • Issuer
  • Recipient

The presenter is... typically the client. It contacts the issuer and asks for a POP token, and presumably authenticates to that issuer in that request. Later the presenter presents that token to the recipient.

The Issuer issues the POP token to the presenter; the token is signed with his own key.

The recipient verifies the POP token.

Because the recipient verifies the token, which is signed by the issuer, the recipient can be assured that the issuer has verified the presenter's identity.

If you're familiar with JWT, you may be aware that they are most often used as Bearer tokens. That means: when ANY client presents a JWT signed by the issuer, the recipient assumes that the "sub" claim (subject) identifies the holder (or Bearer). There are some attacks in which that assumption is false. Suppose Bob is the presenter, and he wants to present a JWT to Alice, the recipient . Since Alice treats the JWT as a Bearer token, any party presenting the token is assumed to be Bob.

If a malicious third party ("Mallory") has hijacked the JWT, then Mallory can present that JWT to Alice, and Alice will believe that Mallory is Bob.

POP Tokens address that problem

A POP Token exchange is designed to circumvent that problem. The issuer, when it generates the initial JWT, wraps within it, a claim referring to a public key. RFC 7800 specifies the "cnf" claim for this purpose.

When the presenter (Bob) then presents that JWT to the recipient (Alice), the presenter can also send a nonce signed with the private key corresponding to the public key asserted in the JWT.

Then, to verify that Bob (presenter) possesses the key, Alice (the recipient) checks

  • that the issuer signed the POP JWT
  • that the POP JWT contains a cnf claim
  • that the cnf claim points to a public key in some way (jwk)
  • and finally, that the presenter holds the private key that matches the public key.
    This last step involves verifying a second signature, on the nonce. You could use a 2nd JWT for that nonce. Or a JWS. Or some other signed payload.

Attached please find an example showing how this might work.

I have a proxy with 2 endpoints: one for the issuer and one for the recipient.

It isn't necessary that Apigee Edge act as both issuer and recipient, but it could.

This is just an example to illustrate the mechanics. There's a README that describes how to use it.

apiproxy-jwt-pop-20190806.zip

Version history
Last update:
‎08-07-2019 09:59 AM
Updated by: