Securing you web services at a single/central point like an API gateway is really a nice thing. But as long as someone can access the web service without going through that single point the web service should also do some authentication.

Welcome to JWT and JWK!

JWT provides an essential part of a standardized way for authenticating a request. JWK is rather optional but makes it much easier how secrets and keys are provided to the verifier of token signatures.

I won’t go into too much detail about JWT and JWK here. You can find many web sites explaining both, JWT and JWK.

JSON Web Key

The JSON Web Key describes the necessary details on how to verify or encrypt when it comes to JWT. And as the name suggests the data is provided as a JSON object.

Attributes:

  • kid - key id : Identifies the web key
  • kty - key type : What type of key is stored in this web key
  • alg - algorithm : Algorithm to be used with this key
  • use : What this key should be used for
  • key_ops : What key operations the provided key is intended to be used with (similar to “use”)
  • oct : key , base64url encoded
  • x5c : array of public keys, binary (DER) format base64 encoded (not base64url)

The mentioned attributes are not a full list but rather what is supported by this JWK implementation. No all of these attributes are required for every web key.

Example for HS256:

1
2
3
4
5
6
7
{
"kid": "123-456-789",
"kty": "oct",
"alg": "HS256",
"use": "sig",
"k": "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUI="
}

Example for RS256:

1
2
3
4
5
6
7
8
9
{
"kid": "123-456-789-0",
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"x5c": [
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CfTwHgz2CoNTxx8KYVmstdsVJrvgKqyiui7fO+VFga1pAAPcUsNtU3aRFRE9FWLId9z0AoEdYV7pOWxfFrqJ/M63MByIFfnVLGdR7k6MKKFaiTPNAv7/qAiWvIDHPIyCCbfRRuJ44LOUFTyn6fp9cnUNpAxNCTLJvhebaPB4zF/QroetOHizGfoRybBJyFZ2Rmuog/KKmdgBQSaPqTksOg+SOjf0BuGvycIaIr3rLeK0WO4o9GRBQCtLK6sURwNNO4F7LBPHVC1rtX0GnEZ7FmpOu0UopvYevxgwwNb2FQ5XY3mW4tiAMZLphJehjizXqcghYwu8sczb7QEz611KQIDAQAB"
]
}

JSON Web Key Set

Multiple JSON Web Keys can be added to a JSON Web Key Set (JWKS) which is just a JSON object with the attribute keys. The value of keys is a simple JSON array where each JSON Web Key is an element in that array.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"keys": [
{
"kid": "123-456-789",
"kty": "oct",
"alg": "HS256",
"use": "sig",
"k": "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUI="
},
{
"kid": "456-789-abc",
"kty": "oct",
"alg": "HS512",
"use": "sig",
"k": "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwQUIxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTBBQg=="
}
]
}

Implementation

The JWK implementation in ILEastic supports the key types oct and RSA. oct is used for HMAC-SHA algorithm and RSA … is self-explanatory :-) .

The key type oct requires also the attribute oct. When using the key type RSA the implementation expects the public key to be base64 encoded (not in PEM format) in the attribute x5c.

For token verification either the attribute use with the value sig or key_ops with the value verify needs to be present.

Loading a JWK

If used with ILEastic JWK and JWKS should be saved in a stream file in the IFS. The content of the stream file is loaded as is. No character conversion takes places. The content needs to be in an ASCII compatible format like UTF-8 (CCSID 1208) or LATIN1 (CCSID 1252).

For loading the JWK we need to add at least the JWT and JWT plugin copy books.

1
2
/include 'jwt.rpginc'
/include 'jwtplugin.rpginc'

The procedure il_jwt_addVerifyOptionsFromJwks loads a JWK and JWKS from the stream file and adds it to the ILEastic JWT configuration.

Note: By default when loading a JWK with il_jwt_addVerifyOptionsFromJwks the verify options lookup method will be “by kid”. That means the JWT plugin will choose a JWK which matches the kid attribute in the JWT from the client request.

Selecting a Verify Option

JWKs are added to the plugin configuration as verify options. When adding a verify option an id can be passed for this verify option. This id will be used for determining which verify option to use by comparing it with an attribute from the JWT token.

The ILEastic JWT plugin must decide which JWK to use on a client request. The plugin offers three options:

  • JWT_VERIFY_OPTIONS_LOOKUP_NONE : The first registered verify option will be used on every request.
  • JWT_VERIFY_OPTIONS_LOOKUP_ISSUER : The issuer claim in the JWT token payload will be used for selecting a matching verify option.
  • JWT_VERIFY_OPTIONS_LOOKUP_KID : The kid attribute of the JWT header will be compared with the kid attribute of the JWKs. The JWK of the matching one will be used for token verification.

When adding verify options by calling il_jwt_addVerifyOptionsFromJwks the kid will be automatically used as the id.

Note: JWT_VERIFY_OPTIONS_LOOKUP_ISSUER cannot be used in combination with il_jwt_addVerifyOptionsFromJwks as the comparing of kid and iss doesn’t make sense.

Wrap Up

Industry standards like JWT and JWK have found their way into ILEastic and are well supported. If your case is not supported just create an issue on GitHub and present your case.

Happy web serving!

Mihael