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.
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!
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.
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.