Algorithm selection guide
JavaScript Object Signing and Encryption (JOSE) has adopted an array of standard cryptographic algorithms, including new Edwards-curve algorithms (added in 2017). Which algorithm should you pick to secure JWTs or other objects in your application? This guide will provide you with the basic criteria for making an informed choice, but do make sure to also consult with reputable literature and experts in the field so that nothing critical is missed.
The common data security objectives
Our needs to secure data, such as tokens, typically stem from one or more concerns:
Objective: Verify data has not been tampered with |
Objective: The origin of the data can be verified |
Objective: The origin of the data must be verifiable by others |
Objective: Data is kept secret from unauthorised parties and processes |
Understanding which security aspect(s) we’re after is the first step in selecting an appropriate JOSE algorithm.
Available JOSE algorithm classes
JOSE provides three distinct classes of cryptographic algorithms to cater for the four security objectives, with partially overlapping properties:
Integrity | Authentication | Non-repudiation | Confidentiality | |
---|---|---|---|---|
HMAC |
✔ | ✔ | ||
Digital signatures | ✔ | ✔ | ✔ | |
Authenticated encryption | ✔ | ✔ | ✔ |
HMAC algorithms: A special super efficient hash (HMAC) for ensuring the integrity and authenticity of data. In order to compute an HMAC you need a secret key.
Digital signatures: Offer the properties of HMAC, plus cryptographic non-repudiation (enabling others than the signer to check the signature’s validity). Digital signatures are based on public / private key cryptography. Require a public / private key pair (of type RSA, elliptic curve (EC), or Edwards-curve Octet Key Pair (OKP)).
Authenticated encryption: Encrypt data, while also ensuring its integrity and authenticity (like HMAC). JOSE offers encryption with public / private keys, with secret (shared) keys and with passwords.
1. Hash-based Message Authentication Code (HMAC)
Provide | Integrity, Authentication |
---|---|
JOSE format | JSON Web Signature (JWS) |
Key type | Secret key |
Algorithms |
HMAC with SHA-2:
|
Use cases |
|
Notes |
|
The HMAC algorithms (with JOSE alg identifiers HS256, HS384 and HS512) are ideal for securing tokens and other information that needs to be sent out or stored externally, in order to be eventually consumed by the issuing application. The key concerns here are ensuring 1) the integrity of the data when we get it back, and 2) that the data was actually produced by us.
A good example is employing HMAC-secured JWTs for generating stateless session cookies. The JWT can include the ID and other information about the logged-in user. When a cookie is received by the web app the HMAC is recomputed and compared to that of the JWT. If the two HMACs don’t match we can assume that the cookie has been tampered with, or isn’t ours.
Example JWT claims for a stateless session cookie:
{
"sub" : "user-12345",
"email" : "alice@wonderland.net",
"login_ip" : "172.16.254.1",
"exp" : 1471102267
}
A common misconception is that message authentication codes qualify as digital signatures. They don’t! That’s because verification requires access to the original secret key used to compute the HMAC, and by its nature you cannot share that key with others without also giving them the ability to generate their own HMACs. If you want non-repudiation use RSA, EC or EdDSA signatures.
2. Digital signatures
Provide | Integrity, Authentication, Non-repudiation |
---|---|
JOSE format | JSON Web Signature (JWS) |
Key type | Public / private key pair:
|
Algorithms |
RSA signature with PKCS #1 and SHA-2:
|
Digital signatures are suited for issuing of tokens, statements, assertions and documents which integrity and authenticity must be verifiable by other parties.
Example uses:
-
Identity tokens issued by an OpenID provider, which a relying party needs to verify in order to sign in a user.
-
Access tokens issued by an OAuth 2.0 server, which the resource servers / web API must verify before serving the request.
Digital signatures work only with public / private keys:
-
The issuer needs to generate a public / private key pair before it can sign JWTs or other objects.
-
The signature is computed with the private key, which must be kept secure at all times, otherwise there is risk of impersonation.
-
The signature of a JWT or JWS object is validated with the public key. The method by which recipients can discover and download the public key are application specific. OpenID Connect servers for example publishe their public keys at a URL in JWK format.
Which digital signature algorithm to choose?
-
If you’re looking for broad support, choose RS256. This algorithm is based on RSA PKCS #1, which is still the most widely used standard for public / private key cryptography. Any decent JWT library should support it. RSxxx signatures also take very little CPU time to verify (good for ensuring quick processing of access tokens at resource servers). 2048 bits is the recommended RSA key length.
-
The ESxxx signature algorithms use Elliptic Curve (EC) cryptography. They require shorter keys and produce mush smaller signatures (of equivalent to RSA strength). EC signatures have one disadvantage though: their verification is significantly slower.
-
The new Edxxx algorithms offer the best sign / validate performance mix. Signing in particular is boosted 62x over 2048-bit RSA and 14x over EC DSA with P-265 curve.
We have examples for RSA, EC and EdDSA signed JWTs.
3. Authenticated encryption
Provide | Confidentiality, Integrity, Authentication |
---|---|
JOSE format | JSON Web Encryption (JWE) |
Key type |
|
Algorithms |
Public / private key encryption:
|
Use cases |
|
Notes |
|
JOSE provides encryption with the following:
-
A secret key in case you want to encrypt data for yourself. If the secret key is shared with other parties (by some out-of-band mean), they can also encrypt data / decrypt ciphertext with it. Check out the table above for the available secret key encryption algorithms.
-
A public key (RSA, EC or OKP) provided by recipient. OpenID Connect providers for example publish their public keys in JWK format at a discoverable URL. The recipient can then decrypt the JWT / JOSE object with its matching private key. Public / private key cryptography is ideal in situations where communicating a secret key out-of-band is not possible or feasible.
-
A password if you want to encrypt data with some phrase that you can remember.
Encryption in JOSE is always authenticated, meaning that the ciphertext’s integrity is protected from tampering. Authenticated encryption thus makes nesting an HMAC JWT inside a JSON Web Encryption (JWE) redundant; use just JWE encryption.
JWE encryption is a “two step” process:
-
The data, or content, is always encrypted with an AES key (called the Content Encryption Key, or CEK), and each JWT / JWE object uses a different CEK. The Nimbus library will automatically generate this AES key for you, and its length will depend on the enc (encryption method) header parameter (e.g. an
"enc":"A128GCM"
will cause a 128-bit AES key to be generated). Besides the choice of three AES key lengths (128, 198 and 256), JOSE supports two modes for content encryption: AxxxCBC-HSxxx and AxxxGCM. The AxxxCBC-HSxxx mode is typically the more widely supported. -
The second step is encrypting, also called wrapping, the generated AES CEK with the input key, i.e. the secret key, the public key or the password that you provided. This is specified by the alg JWE header parameter. In case you want to skip the second step, and provide the AES CEK directly, choose direct encryption and specify an
"alg":"dir"
JWE header parameter.
Example JWE header where the content is encrypted with 128-bit AES in GCM mode, while the AES CEK itself is encrypted with a public RSA key (using RSA OAEP encryption):
{
"alg" : "RSA-OAEP",
"enc" : "A128GCM"
}
You can check out the example for encrypting a JWT with an RSA public key.
Nested signing and encryption
A signed JWT / JWS object can be additionally encrypted, thus providing integrity, authenticity, non-repudiation and confidentiality to data.
This is achieved by simple nesting (see example):
-
The JWT is signed with a private RSA, EC or OKP key.
-
The signed JWT then becomes the payload (plaintext) of a JWE object, which is encrypted with either the public key (RSA, EC, OKP) of the recipient, or with a secret key that has been shared between the two parties.
Processing a nested JWT works backwards:
-
The JWE object is decrypted with the appropriate key (private key for RSA, EC or OKP, or established secret key).
-
The extracted payload (plain text) is then parsed as a signed JWT, and verified with the issuer’s public key (RSA, EC or OKP).
The Nimbus JOSE+JWT library provides a complete framework for handling nested JWTs.
Algorithm choices in OpenID Connect
The following rules can give you an idea of which ID token algorithms are possible, given a client registration:
-
A client with a client_secret can receive ID tokens secured with HMAC (where the client_secret serves as the HMAC secret key) or with a signature (RSA, EC or EdDSA). In order to verify an RSA, EC or EdDSA signed ID token the client simply needs to retrieve the matching public key of the OpenID provider (from its JWK set URL).
-
A client with a client_secret can also receive encrypted ID tokens, where the client_secret is used to derive a secret AES key from it.
-
Issued client_secrets must be sufficiently long to fit the required secret key length if HMAC is used. For example, a 256 bit client_secret permits HMAC with HS256.
-
In order for a client to receive RSA or ECDH encrypted ID tokens, it must have a public RSA, EC or OKP key registered with the OpenID provider.
-
OpenID Connect also permits clients without a client_secret. Such clients need to register a public RSA, EC or OKP key with the OpenID provider, and use that key to authenticate (via JWT) at the token endpoint. The ID token may be encrypted to that public key as explained above.