Publish and generate JWKS?

guycrets
Participant IV

Would like to package results of and OAuth /introspect (rfc7662) call into JWT token. Creating the token is not the issue.

But how to publish public keys as JSON Web Key set (jwks_uri)?

And automatically rotate the keys in the key set? Ideally generating new key pairs in Apigee itself.

In the Apigee Istio adapter, I do find some code for generating a JWKS. It uses javascript library "jsrsasign" (from Yahoo). Is this the approach to copy? But key rotation is manual in the Istio adapter, keypair must be generated outside Apigee. And number of keys is fixed to 2.

Looking forward to your feedback!

2 5 8,475
5 REPLIES 5

First, let's address a common misunderstanding. Some people think that verifying JWTs requires a JWKS endpoint.

This isn't true.

It is possible to use a JWKS to verify a JWT (in Apigee Edge and using other tools and technologies). But a JWKS is not required. (in Apigee Edge and using other tools and technologies)

Apigee Edge can verify signed JWT. Today, Apigee Edge has the ability to verify signatures on JWT that use HMACSHA2 algorithms (HS256, HS384, HS512) or RSASSA-PKCS1.5 algorithms (RS256, RS384, RS512). In either case, verifying requires that the policy configuration specify a key of some type to use in the signature verification process. (BTW, we're adding PS* and ES* algorithms to the JWT policies. Also adding JWS support. And some other enhancements. This is coming soon to a cloud release of Apigee Edge near you!)

HS* algorithms are symmetric, so you can specify a "secret key", something like a password; ideally it is a key derived via PBKDF2 from a password or passphrase. This is generally not something you get from a JWKS endpoint, but instead is a shared secret. In the case of Apigee Edge, you probably would store that shared secret in an encrypted KVM, and then retrieve it with a KVM GET policy.

RS* Algorithms are asymmetric, which means you need to specify a Public Key in order to verify a JWT signed with RS256, RS384, or RS512. The way you specify that key is somewhat flexible. You can specify a JWKS (JWK set) payload. JWK is described in RFC7517 . For example, this is Google's JWKS endpoint. (PS* and ES* algorithms also use asymmetric keys, that can be published in the same was as keys for RS* algorithms. In fact the keys for PS* are RSA keys - the same keys you would use for RS*)

While a typical case is for a signing party or issuer to publish a well-known endpoint that provides the public keys that can be used to verify signatures issued by the signer, the public endpoint part (https://something.com/whatever) is not required. The JWKS could be provided in some other way, or it could be dynamically generated by a program. In the Apigee Edge scenario, it is possible to store a JWKS in the KVM and retrieve it prior to calling VerifyJWT.

OK, so the first option for specifying the key source for verification is JWKS. A second option is to specify a public key directly, in PEM-encoded (PKCS#8) format. This is the thing that looks like this:

  -----BEGIN PUBLIC KEY-----
  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq/c4uCIU3UVFy7ezonkJ
  WVp2HkyZutia5Vg3T6lnWMySLeux34pWEiQdnNomWJvxY1wUc3PDUD8m4dF86Lvd
  Vr9dXisYtWpoaxy+nLGWmkSXv3pv77OfpEmaESbDEN4NlrQQsqnHH21fr8IQGp1e
  IetYOcUkBl97QXETV7fS5gSM2PuqMh7PNKeQot9LAf+0ANLzpCXa7Tx26TXoib2C
  RG5wD2+JDp3wlQtDTmNaHDgz7GDB1HsLYLY+JEFEi0hPY0zzUwxoH8UTlQmHHHWy
  5ewmAAQZ3yasIG0csDM38nKSHcZJMorg3tcJzO/7RS+a/sU8oEJWLkUcGawM33cc
  CQIDAQAB
  -----END PUBLIC KEY-----

And where you get that PEM file is up to you. It could be loaded into a KVM and retrieved (KeyValueMapOperations/Get) prior to VerifyJWT, or it could be embedded directly into the VerifyJWT policy.

OK, so you don't NEED a JWKS service in order to use the VerifyJWT policy in Apigee Edge, or to verify JWT in any platform. Think of the JWKS as a source of the public key, one way to provide a public key.

What if you WANT to use a JWKS? I see two cases:

  • Case 1: you want to verify JWT that are generated and signed by some third party. Something like Google Sign-in, Azure Active Directory, Okta, Auth0, Salesforce.com, Ping, or some other SaaS that identifies people. In that case, If the issuer uses an asymmetric algorithm, then the issuer of the JWT will provide the Public Keys to be used by verifiers. All of the issuers that I have tested, do this. (At one point Paypal.com used symmetric algorithm (signing with secret keys), so... they wouldn't use Public Keys, and JWKS wouldn't apply). When the issuer provides a JWKS endpoint, the verifying party would just use that well-known endpoint as a source for keys.
  • Case 2: you want to verify JWT that are signed by YOU. (Or you want to allow third parties to verify JWT signed by you). In this case, and again, only if you are using the asymmetric algorithm, yes, you should be publishing your keys on a well-known endpoint in JWKS format.

Only in that last case, do you need a "JWKS service". But let's not over-complicate things. The JWKS is just a small static JSON blob. It's going to be less than 1kb, maybe ~2kb if you have a bunch of keys of different types. A teeny file. You can host this as a static file on any any trustworthy web server. It makes sense to put it on the main .com site for your company or organization. A good pattern is to use the .well-known url path segment which is described in RFC 5785. Basically something like https://www.yourcompany.com/.well-known/jwks.json .

Alternatively, if you are using the KVM to store keys, you could easily build that well-known endpoint into a loopback API Proxy in Apigee Edge. The proxy would just read from the KVM and emit a JSON representing the JWKS.

What goes into the JWKS? It's the public key material corresponding to the private keys you use to sign things. The JWKS format is pretty easy to generate using libraries in various languages and platforms. I have used a java program with the library nimbusds for building JWKS from either de-novo generated keys or from keys stored as PEM-encoded private keys. I have also generated private keys using openssl and then converted them with a nodejs module. I'm sure there are powershell, .NET, and python libraries to handle this stuff. How you do this is up to you.

The contents of the JWKS needs to change if you rotate keys. It's a good idea to have the option to rotate keys. Keys can be compromised, and when that happens you want to be able to quickly start signing things with a new uncompromised private key, and of course you need a good way to publish the new corresponding public key. That's where the JWKS comes in, it's great for that. And in support of key rotation, you will also need to associate a unique keyid to each key you generate, and insert the kid claim in the header of each JWT you issue. (standard stuff) The big players I mentioned earlier all do this key rotation thing. So to rotate keys, generate the new key, insert the JWK form of the public key material into the static JWKS document, and voila! Easy. If you are just doing a scheduled, pre-emptive key rotation, then later, eventually you will remove the old key from the JWKS document. If you are reacting to a compromised key, then you will remove the old compromised key from the JWKS immediately.

Key rotation should happen rarely - super security-paranoid Google rotates keys once every couple weeks or months maybe? They're very conservative.

So in short I don't think you need to over-think the "JWKS service". You can host your own JWKS endpoint using a few scripts and hand-built tools, and a cron job to rotate keys.

ps: here is a sample JWKS service for you to play with.

Dino,

Many thanks for your extensive answer! Case 2 is my focus where other system needs to validate server-to-server JWT token generated within Apigee. Will investigate the use of nuimbusds to generate keypair.

Coming from a world of client certs, I'm convinced that key rotation is a must and shared secrets are to be avoided.

It would be nice if the GenerateJWT policy would be accompanied with support for self-generated JWKS. So suggestion that Apigee would extend the own JWT implementation with JWKS, as part of the product or as sample code.

My wishlist:

  • support for multiple keysets
  • ideally configurable from within Apigee's Environment configuration
  • for each keyset, following parameters should be configurable:
    • #keys per keyset (limited to 5 or so)
    • frequency of key rotation configurable (0 = no key rotation)
    • amount of delay between introducing new key and actively using it (allowing JWT validators to update their cached version in the background)

Else will investigate and try to do the dirty work myself. Let's start without key rotation.

Thank you for the helpful feedback. We have a vague "publish JWKS" item in our backlog, and it helps to get feedback from you on specific desires.

ref: b/130217041

I produced an example API Proxy that:

  • generates JWT signed with RS256
  • exposes a JWKS endpoint to allow external systems to verify the JWT

Find it here: https://github.com/DinoChiesa/Apigee-JWT-with-JWKS

Error: Page not found

The requested URL was not found on this server.