Background

Issue Summary

The reason for producing this wiki is to document configuration of AM and IG token handling, with regards to the necessary key creation, keystore handling and AM and IG configuration required to secure them. This follows issues with finding the correct keystore configuration, procedures and keys in completing feature (pertaining to stateless access-tokens). The PR associated with this JIRA is also very informative on how to configure the environments.

KeyStores Basics

keystore is a repository for securely storing:

This repository may be a specifically formatted text file or backed by some other mechanism. keystore access is available through the java.security.KeyStore class.

Keys are stored in a keystore with an associated "alias" that is used for identifying the key. The keystore may be password protected (with a storepass) and each private or secret key may be individually protected with its own password (keypass). There is no need to password-protect public keys.

A truststore is a keystore specifically for managing remote authentication (see JSSE Ref Guide). It's use is not important in the context of this discussion, so it won't be covered any further.

keytool is a java supplied command-line tool for performing operations on a keystore, such as creating keys, listing keys and importing and exporting.

More information on KeyStores can be found here:

KeyStore Types

A KeyStore, as mentioned, may be formatted in a specific way, according to the its type:

When using the keytool, it is necessary to specify the -storetype if not using JKS, otherwise you will get unhelpful messages about keystore formats and tampering.

More useful information on keystore types:

AM Configuration

Tokens

AM 6.0 supports the following client requests (Response Type):

KeyStores

AM comes with two pre-configured KeyStoreskeystore.jks and keystore.jceks. Which is used can be configured through the AM console:

Keys (and Related Issues)

AM KeyStores come complete with provided test keys, as documented in the AM Setup and Maintenance Guide (specifically Table 5.1):

Supplied sample keys are 

Other notes of interest:

It is necessary to restart AM after any key change - as AM caches keystore content on load.

IG KeyStores

IG doesn't expressly need the configuration of any KeyStores, except if required by a particular Filter or component (e.g. the new StatelessAccessTokenResolver).

In the case that IG must use AM provided keys (e.g. the above example) then the correct keystore type must be created and AM-generated keys must be made available to it. In the case of then either a signing key (for verification) or an encryption key (for decryption) must be provided.

Sharing Keys between AM and IG (KeyStores)

Using the example of the StatelessAccessTokenResolver, AM would be configured to produce access tokens that are either Encrypted or Signed JWTs (Encrypted takes precedence in AM config). As such, depending on how the JWT is secured, we would need to generate an encryption key or a signature key in the AM KeyStore. Again, depending on how the JWT is secured, we would then need to import the respective key into the IG keystore:

Some points to note - or emphasise - here:

With this in mind, the following snippets show how to configure a signature or encryption key in an AM keystore (JCEKS) and import the corresponding key into an IG keystore (JCEKS or PKCS12).

Configuring Signature keys

The following code-block illustrates how to export a signature certificate from AM (keystore.jks) to an IG PKCS12 keystore (or a JCEKS keystore would be applicable):

# create verify-key03 (RSA 2048) in AM JKS keystore
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -genkey -alias verify-key03 \
    -dname "CN=openig.example.com, OU=example, O=com, L=fr, ST=fr, C=fr" \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
    -storetype JCEKS \
    -storepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8" \
    -keypass changeit \
    -keyalg RSA -keysize 2048

# export verify-key03 to .pem
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -exportcert -rfc -alias verify-key03 \
    -file "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/openig-container/apache-tomcat-8.0.46/conf/verify-key03-cert.pem" \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
    -storetype JCEKS \
    -storepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8" \
    -keypass changeit

# import verify-key03 .pem to IG PKCS12 keystore
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -import -trustcacerts -rfc -alias verify-key03 \
    -file "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/openig-container/apache-tomcat-8.0.46/conf/verify-key03-cert.pem" \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/ig_instance_dir/config/IG_keystore.p12" \
    -storetype PKCS12 \
    -storepass "keystore"

# list content of IG PKCS12 to confirm key present
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -list \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/ig_instance_dir/config/IG_keystore.p12" \
    -storetype PKCS12 \
    -storepass keystore

Configuring Encryption key

The following code-block illustrates how to export an encryption SecretKey from AM (keystore.jks) to an IG PKCS12 keystore (or a JCEKS keystore would be applicable):

# create secret key enckey07 (RSA 2048) in AM JKS keystore
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -genseckey -alias enckey07 \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
    -storetype JCEKS \
    -storepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8" \
    -keypass changeit \
    -keyalg AES \
    -keysize 256

# export enckey07 to .pem - using keytool exportseckey --> !!!doesn't work!!!
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -exportseckey -alias enckey07 \
   -file "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/openig-container/apache-tomcat-8.0.46/conf/enckey03-secretkey.pem" \
   -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
   -storetype JCEKS \
   -storepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8"  \
   -keypass changeit

# list content of AM keystore.jceks to confirm key present
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -list  -v \
   -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
   -storetype JCEKS \
   -storepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8"

# import enckey07 key (direct from keystore) to IG PKCS12 keystore
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -importkeystore \
   -srcalias enckey07 \
   -srckeystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openam/openam-embedded-DJ/openam/keystore.jceks" \
   -srcstoretype JCEKS \
   -srcstorepass "qWPzxXdIF0IaD/6Q9Bp7vr32oUK0H8h8" \
   -destalias enckey07 \
   -destkeystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/ig_instance_dir/config/IG_keystore.p12" \
   -deststoretype PKCS12 \
   -deststorepass "keystore" \
   -destkeypass "keystore"

# list content of IG PKCS12 to confirm key present
/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/keytool -list  -v \
    -keystore "/Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/ig_instance_dir/config/IG_keystore.p12" \
    -storetype PKCS12 \
    -storepass "keystore"

Further Steps

Configuring AM

This assumes the keystore is configured correctly:

Additionally:

Securing Access Tokens

OAuth2 Provider Configuration

OAuth2 Provider configuration: Realm → Configure OAuth2 Provider

Use Stateless Access & Refresh Tokens = true

Access Token Lifetime (seconds) = <sensible lifetime for testing - 10secs>

Issue Refresh Tokens = False (unimportant but does not need to be set)

Issue Refresh Tokens on Refreshing Access Tokens = False (unimportant but does not need to be set)

Issue Refresh Tokens = False (unimportant but does not need to be set)

It is also necessary to add appropriate scopes. For example:

"email|Your email address", "openid|", "address|Your postal address", "phone|Your telephone number(s)", "profile|Your personal information"

Securing an Access Token with a Signature

OAuth2 Token Signing Algorithm = RS256 → as per verify-key03 config

Token Signing RSA Public/Private Key Pair = verify-key03 → Asymmetric key configured earlier.

Stateless Token Compression = can be either True or False

Enable Stateless Token Encryption = False → essential to ensure signing configuration is used

Securing an Access Token with Encryption

Enable Stateless Token Encryption = True

Token Encryption Secret Key Alias = enckey07 → Symmetric key as you configured earlier

Subject Identifier Hash Salt = changeme

OAuth2 Client Configuration

OAuth2 Client configuration: Realm → Applications → OAuth2

Advanced (tab)

Grant Types = should include "Resource Owner Password Credentials"

Response Types = should include "code token" for access-tokens.

Signing and Encryption (tab)

Public key selector  = X509

Additional notes:

Configuring IG

IG configuration KeyStore heaplet would reference the above IG keystore and keystore type. For example:

    "heap": [
        {
            ...
        },
        {
            "config": {
                "type": "PKCS12",
                "password": "keystore",
                "url": "file:///Users/wayne.morrison/dev/pyforge/results/20180723-114228/Filters/openig/ig_instance_dir/config/IG_keystore.p12"
            },
            "name": "IgP12KeyStore",
            "type": "KeyStore"
        },
        ...

Specific key configuration would be added in the Filter or other component configuration. For example, for StatelessAccessTokenResolver:

{
    "type" : "StatelessAccessTokenResolver",
    "config" : {
        "issuer" : "http://openam.example.com:8082/openam/oauth2/realms/root/realms/filters_realm",
        "tokenName" : "access_token",
        "signature" : {
             "alias" : "verify-key03",
             "password" : "keystore",
             "keystore" : "IgP12KeyStore"
        },
        "_encryption" : {
            "alias" : "enckey07",
            "password" : "keystore",
            "keystore" : "IgP12KeyStore"
        }
    }
}

Demonstration and Testing

You can obtain a token from AM (oauth2/access_token endpoint) using the following:

http -a test_OAuth2ResourceServerFilter:password POST openam.example.com:8082/openam/oauth2/realms/root/realms/filters_realm/access_token username=demo password=changeit grant_type=password scope=openid --form  | jq  '.["access_token"]'


# capture it in $TOKEN for later use
TOKEN=`http -a test_OAuth2ResourceServerFilter:password POST openam.example.com:8082/openam/oauth2/realms/root/realms/filters_realm/access_token username=demo password=changeit grant_type=password scope=openid --form  | jq  '.["access_token"]' | tr -d '"'`

Please refer to Sample StatelessAccessTokenResolver setup and configuration (PR) for more thorough instructions on testing (upon which this document was based).

Documentation

Troubleshooting

  1. Key configured in AM not found:
    1. Confirm the correct configured keystore is the same as the one where the keys were added.
    2. Restart AM after keys are added.
  2. Issue creating a specific key type in a keystore:
    1. The keystore type does not support the type of key being created:
      1. For instance JKS does not support SecretKey
  3. Issue importing a specific key type into a new keystore:
    1. The new keystore type does not support the type of key being imported:
      1. For instance JKS does not support SecretKey
  4. keytool operation fails with error indicating incorrect format or that tampering has occurred:
    1. java.io.IOException: Invalid keystore format
    2. Use the -storetype setting to ensure you're performing the operation on an expected keystore type.
  5. Issue importing keys from one keystore to another:
    1. Ensure the -srcstoretype and -deststoretype settings are correct for the source and target keystores.
    2. Ensure that the destination keystore can accommodate the algorithm used in the source key.
      1. If it cannot then... ????‍??
      2. This is the issue that occurred while trying to import the AM test key "directenctest". It wasn't clear what algorithm was used.
  6. AM fails to encrypt a key, complaining of a "Invalid offset/length combination":
    1. The key specified has a keysize that cannot be used in combination with the key type. Try a larger key size
    2. I found that an AES key of 256b was fine.
  7. AM issue when creating an access token - complains of no signature even though encryption is being used:
    1. This actually relates to the id-token configuration
      1. Ensure that if ID Token Signing Algorithm is not being used then it is set to "none" (not left empty).