Encrypted tokens with KeyStoreSecretStore
This page shows how to validate encrypted access tokens with the StatelessAccessTokenResolver using a JwkSetSecretStore.
Set up encryption keys
-
Locate the following directories for keys, keystores, and certificates, and in a terminal create variables for them:
-
Directory where the keystore is created:
keystore_directory
-
AM keystore directory:
am_keystore_directory
-
PingGateway keystore directory:
ig_keystore_directory
-
-
Set up keys for AM:
-
Generate the encryption key:
$ keytool -genseckey \ -alias encryption-key \ -dname "CN=ig.example.com, OU=example, O=com, L=fr, ST=fr, C=fr" \ -keystore "$am_keystore_directory/AM_keystore.p12" \ -storetype PKCS12 \ -storepass "password" \ -keyalg AES \ -keysize 256
-
List the keys in the AM keystore:
$ keytool -list \ -v \ -keystore "$am_keystore_directory/AM_keystore.p12" \ -storepass "password" \ -storetype PKCS12 ... Your keystore contains 1 entry Alias name: encryption-key
-
Add a file called
keystore.pass
, with the contentpassword
:$ cd $am_keystore_directory $ echo -n 'password' > keystore.pass
Make sure the password file contains only the password, with no trailing spaces or carriage returns. The filename corresponds to the secret ID of the store password and entry password for the KeyStoreSecretStore.
-
Restart AM.
-
-
Set up keys for PingGateway:
-
Import
encryption-key
into the PingGateway keystore, with the aliasdecryption-key
:$ keytool -importkeystore \ -srcalias encryption-key \ -srckeystore "$am_keystore_directory/AM_keystore.p12" \ -srcstoretype PKCS12 \ -srcstorepass "password" \ -destkeystore "$ig_keystore_directory/IG_keystore.p12" \ -deststoretype PKCS12 \ -destalias decryption-key \ -deststorepass "password" \ -destkeypass "password"
-
List the keys in the PingGateway keystore:
$ keytool -list \ -v \ -keystore "$ig_keystore_directory/IG_keystore.p12" \ -storepass "password" \ -storetype PKCS12 ... Your keystore contains 1 entry Alias name: decryption-key
-
In the PingGateway configuration, set an environment variable for the keystore password:
$ export KEYSTORE_SECRET_ID='cGFzc3dvcmQ='
-
Restart PingGateway.
-
Validate tokens
-
Set up AM:
-
Set up AM as described in Validate tokens.
-
Add a mapping for the encryption keystore:
-
Select Secret Stores >
keystoresecretstore
. -
Select the Mappings tab, and add a mapping with the following values:
-
Secret Label :
am.services.oauth2.stateless.token.encryption
-
Alias :
encryption-key
-
-
-
Enable token encryption on the OAuth 2.0 Authorization Provider:
-
Select Services > OAuth2 Provider.
-
On the Advanced tab, select Encrypt Client-Side Tokens.
-
-
-
Set up PingGateway:
-
Set up PingGateway for HTTPS, as described in Configure PingGateway for TLS (server-side).
-
Add the following route to PingGateway, replacing ig_keystore_directory:
-
Linux
-
Windows
$HOME/.openig/config/routes/rs-stateless-encrypted.json
%appdata%\OpenIG\config\routes\rs-stateless-encrypted.json
{ "name": "rs-stateless-encrypted", "condition": "${find(request.uri.path, '/rs-stateless-encrypted')}", "heap": [ { "name": "SystemAndEnvSecretStore-1", "type": "SystemAndEnvSecretStore" }, { "name": "KeyStoreSecretStore-1", "type": "KeyStoreSecretStore", "config": { "file": "<ig_keystore_directory>/IG_keystore.p12", "storeType": "PKCS12", "storePasswordSecretId": "keystore.secret.id", "entryPasswordSecretId": "keystore.secret.id", "secretsProvider": "SystemAndEnvSecretStore-1", "mappings": [ { "secretId": "stateless.access.token.decryption.key", "aliases": [ "decryption-key" ] } ] } } ], "handler": { "type": "Chain", "capture": "all", "config": { "filters": [ { "name": "OAuth2ResourceServerFilter-1", "type": "OAuth2ResourceServerFilter", "config": { "scopes": [ "myscope" ], "requireHttps": false, "accessTokenResolver": { "type": "StatelessAccessTokenResolver", "config": { "secretsProvider": "KeyStoreSecretStore-1", "issuer": "http://am.example.com:8088/openam/oauth2", "decryptionSecretId": "stateless.access.token.decryption.key" } } } } ], "handler": { "type": "StaticResponseHandler", "config": { "status": 200, "headers": { "Content-Type": [ "text/html; charset=UTF-8" ] }, "entity": "<html><body><h2>Decoded access_token: ${contexts.oauth2.accessToken.info}</h2></body></html>" } } } } }
Notice the following features of the route compared to
rs-stateless-signed.json
from Validate tokens.-
The route matches requests to
/rs-stateless-encrypted
. -
The OAuth2ResourceServerFilter and KeyStoreSecretStore refer to the configuration for a decryption key instead of a verification key.
-
-
-
Test the setup
-
Get an access token for the demo user, using the scope
myscope
:$ mytoken=$(curl -s \ --user "client-application:password" \ --data "grant_type=password&username=demo&password=Ch4ng31t&scope=myscope" \ http://am.example.com:8088/openam/oauth2/access_token | jq -r ".access_token")
-
Display the token:
$ echo ${mytoken}
Note that the token is structured as an encrypted token.
-
Access the route by providing the token returned in the previous step:
$ curl -v \ --cacert /path/to/secrets/ig.example.com-certificate.pem \ --header "Authorization: Bearer ${mytoken}" \ https://ig.example.com:8443/rs-stateless-encrypted ... Decoded access_token: { sub=demo, cts=OAUTH2_STATELESS_GRANT, ...
-