Apereo CAS - OpenID Connect Key Rotation & Revocation

Posted by Misagh Moayyed on November 11, 2021 · 7 mins read ·

When using Apereo CAS as an OpenID Connect OP, a recommended practice for non-trivial deployments typically is one to secure and rotate keys. Key rollover requires that we discard current keys and generate a new pair of keys to be used for signing and verifying the tokens. In this blog post, we will take a look at how to configure Apereo CAS to handle key rotation and revocation automatically, or forcefully.

Our focus is based on the following:

Overview

National Institute of Standards and Technology (NIST) proposes that one should rotate the keys after a certain period. The previous keys must be discarded, and newly generated keys must be used for further cryptographic operations.

According to NIST guidelines, both keys must be rotated at least once every two years. CAS deployments typically rotate keys once every six months.

Key Generation

When it comes to handling keys and keystores for OpenID Connect Protocol, Apereo CAS offers several options.

First, CAS is able to auto-generate a keystore at a defined path:

cas.authn.oidc.jwks.jwks-file=file:/path/to/keystore.jwks

This keystore is only generated when the file pointed out by that setting does NOT exist. If it does, CAS would happily load and parse the file without any complaints.

The keystore that is generated by CAS is quite ordinary, but it has one extra special parameter, state:

{
    "keys": [
        {
            "kty": "RSA",
            "kid": "cas-dlBHszev",
            "n": "...",
            "e": "AQAB",
            "d": "...",
            "p": "...",
            "q": "...",
            "dp": "...",
            "dq": "...",
            "qi": "...",
            "state": 0
        }
    ]
}

The state parameter indicates the status of the key lifecycle. A value of 0 indicates this key is active and current and will be used for required crypto operations as necessary.

Dude, Where’s My…Key?

Per the OpenID Connect specification, you can always ask CAS to produce its keys:

$ curl https://sso.example.org/cas/oidc/jwks | jq

You could also ask CAS to narrow down the list of keys for you by specifying the needed state:

$ curl https://sso.example.org/cas/oidc/jwks\?state=current | jq

Other possible states are previous and future.

Key Rotation

Typically, CAS maintains three different and distinct keys in its keystore:

  • The current key, used for signing operations, etc soon to be rotated when necessary.
  • The future key, which would take the place of the current key once after keys are rotated.
  • The previous key, no longer in rotation and a candidate for removal via revocation operations. CAS does allow a reasonable schedule for key rotation. The schedule can be controlled via:
cas.authn.oidc.jwks.rotation.schedule.repeat-interval=PT30S
cas.authn.oidc.jwks.rotation.schedule.start-delay=PT30S
cas.authn.oidc.jwks.rotation.schedule.enabled=true

During key rotation operations,

  • Current key is put out of rotation; its status changes to previous.
  • Future key takes the place of the current key.
  • A new future key is generated and would be on standby.

If you do not want to wait for the rotation schedule to kick in and wish to forcefully rotate keys, you can always ask CAS to run the rotation routine for you:

$ curl https://sso.example.org/cas/actuator/oidcJwks/rotate | jq

Key Revocation

Key revocation operations begin to refresh the state of the keystore by removing previous keys. Similar to key rotation routines, key revocation may also be designed to follow a reasonable schedule:

cas.authn.oidc.jwks.revocation.schedule.repeat-interval=PT60S
cas.authn.oidc.jwks.revocation.schedule.start-delay=PT30S
cas.authn.oidc.jwks.revocation.schedule.enabled=true

…and as ever, if you do not want to wait for the revocation schedule to kick in and wish to forcefully revoke keys, you can always ask CAS to run the revocation routine for you:

$ curl https://sso.example.org/cas/actuator/oidcJwks/revoke | jq

Need Help?

If you have questions about the contents and the topic of this blog post, or if you need additional guidance and support, feel free to send us a note and ask about consulting and support services.

So…

I hope this review was of some help to you and I am sure that both this post as well as the functionality it attempts to explain can be improved in any number of ways. Please feel free to engage and contribute as best as you can.

Happy Coding,

Misagh Moayyed