JavaScript Object Signing and Encryption (JOSE) consists of a set of specifications for encryption and signatures based on the popular JSON format. This is work in progress, the IETF jose workgroup usually has the latest information.

The jose package implements some of these specifications, in particular for working with JSON web tokens and keys.

JSON Web Token: HMAC tagging

The most common use of JSON Web Tokens is combining a small payload (the ‘claim’) with a HMAC tag or RSA/ECDSA signature. See also https://jwt.io for short introduction.

library(openssl)
library(jose)

# Example payload
claim <- jwt_claim(user = "jeroen", session_key = 123456)

# Encode with hmac
key <- charToRaw("SuperSecret")
(jwt <- jwt_encode_hmac(claim, secret = key))
[1] "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1NDgwMDYzMTIsInVzZXIiOiJqZXJvZW4iLCJzZXNzaW9uX2tleSI6MTIzNDU2fQ.Ijj0X5ZS0qEEzM1UF7CC-cIVW5uKEahvrH1FH8-NHoY"
# Decode 
jwt_decode_hmac(jwt, secret = key)
$iat
[1] 1548006312

$user
[1] "jeroen"

$session_key
[1] 123456

The decoding errors if the tag verification fails.

# What happens if we decode with the wrong key
jwt_decode_hmac(jwt, secret = raw())
Error: HMAC signature verification failed!

JSON Web Token: RSA/ECDSA signature

Similarly, we can use an RSA or ECDSA key pair we to verify a signature from someone’s public key.

# Generate ECDSA keypair
key <- ec_keygen()
pubkey <- as.list(key)$pubkey

# Sign with the private key
(jwt <- jwt_encode_sig(claim, key = key))
[1] "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJpYXQiOjE1NDgwMDYzMTIsInVzZXIiOiJqZXJvZW4iLCJzZXNzaW9uX2tleSI6MTIzNDU2fQ.c9cBVHZ5itBzk5GKt50Q1novutBBJchxt5QqE7e8n1HJx-LT-u9BeKUZxbucIMAkatUI8PMOLbjxyJoWwS2sKQ"
# Decode and verify using the public key
jwt_decode_sig(jwt, pubkey = pubkey)
$iat
[1] 1548006312

$user
[1] "jeroen"

$session_key
[1] 123456

Again decoding will error if the signature verification fails.

wrong_key <- ec_keygen()
jwt_decode_sig(jwt, pubkey = wrong_key)
Error in hash_verify(md, sig, pk): Verification failed: incorrect signature

The spec also describes methods for encrypting the payload, but this is currently not widely in use yet.

Where is the JSON

The jwt payloads consists of a head, body and signature which are separated with a dot into a single string. Both the header and body are actually base64url encoded JSON objects.

(strings <- strsplit(jwt, ".", fixed = TRUE)[[1]])
[1] "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9"                                                  
[2] "eyJpYXQiOjE1NDgwMDYzMTIsInVzZXIiOiJqZXJvZW4iLCJzZXNzaW9uX2tleSI6MTIzNDU2fQ"            
[3] "c9cBVHZ5itBzk5GKt50Q1novutBBJchxt5QqE7e8n1HJx-LT-u9BeKUZxbucIMAkatUI8PMOLbjxyJoWwS2sKQ"
cat(rawToChar(base64url_decode(strings[1])))
{"typ":"JWT","alg":"ES256"}
cat(rawToChar(base64url_decode(strings[2])))
{"iat":1548006312,"user":"jeroen","session_key":123456}

However you should never trust this information without verifying the signature. This is what the jwt_decode functions do for you.