Decoding and Encoding JWS (signed JWT)
Decoding a JWS
The most common scenario faced by developers is to be able to decode a JWS that is provided to the application (for example in an OpenID Connect authentication), this scenario is described below.Note: It is strongly recommended to make use of common libraries for JWT and JWS processing to avoid introducing implementation specific bugs.
For this example we will use the id_token described in the above section. This is a signed JWT (JWS):
eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0.eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWNfY2xpZW50IiwianRpIjoidWY5MFNLNH
dzY0ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYx
MTUzLCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNGVhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bG
M2VHFBIn0.lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIcehyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0Pia
UOmJK1U-ws_C-fCplUFSE7SK-TrCwaOow4_7FN5L4i4NAa_WqgOjZPloT8o3kKyTkBL7GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJS
ykRqzYS2MEJlncocBBI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka5OcArlo188XH7lMgPA2GnwSFGHBhccjpxh
N7S46ubGPXRBNsnrPx6RuoR2cI46d9ARQ
Step 1: Split the JWS into its Components
Split the token by periods to end up with three separate components: the JOSE header, the JWT payload, and the digital signature. These three components are base64urlencoded and therefore will need to be base64urldecoded to reveal the contents.
The JOSE header in this example contains the algorithm used to sign ("alg":"RS256") and a reference to the key that can be used to verify it ("kid":"i0wnn"):
Component | Value | Value Decoded |
---|---|---|
JWT Signature | eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0 | { "alg":"RS256", "kid":"i0wnn" } |
The second component is the JWT payload which contains the JWT claims. The payload of the above example is decoded as follows:
Component | Value | Value Decoded |
---|---|---|
JWT Payload | eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWN fY2xpZW50IiwianRpIjoidWY5MFNLNHdzY0 ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzO lwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6 MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYxMTU zLCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNG VhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X 2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bGM2 VHFBIn0 | { "sub":"joe", "aud":"im_oic_client", "jti":"uf90SK4wscFhctUT6Dtvb2", "iss":"https:\/\/localhost:9031", "iat":1394060853, "exp":1394061153, "nonce":"e957ffba-9a78-4ea9-8eca-ae8c4ef9c856", "at_hash":"wfgvmE9VxjAudsl9lc6TqA" } |
Finally, the third component is the digital signature of section 1 and 2 (period concatenated). The algorithm and key reference used to create and verify the signature is defined in the JWT Header.
Component | Value | Value Decoded |
---|---|---|
JWT Signature | lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIce hyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0PiaUOmJK1U-ws_C-f CplUFSE7SK-TrCwaOow4_7FN5L4i-4NAa_WqgOjZPloT8o3kKyTkBL7 GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJSykRqzYS2MEJlncocB BI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka 5OcArlo188XH7lMgPA2GnwSFGHBhccjpxhN7S46ubGPXRBNsnrPx6Ru oR2cI46d9ARQ | N/A |
Step 2: Validating the Digital Signature
To validate the signature, concatenate the JWT header, a period, and the JWT payload. Validate that value against the third component of the JWT using the algorithm defined in the JWT header. Using the above ID token as an example:
Signed data (JWT Header + "." + JWT Payload):
eyJhbGciOiJSUzI1NiIsImtpZCI6Imkwd25uIn0.eyJzdWIiOiJqb2UiLCJhdWQiOiJpbV9vaWNfY2xpZW50IiwianRpIjoidWY5MFNLNHdz
Y0ZoY3RVVDZEdHZiMiIsImlzcyI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6OTAzMSIsImlhdCI6MTM5NDA2MDg1MywiZXhwIjoxMzk0MDYxMTUz
LCJub25jZSI6ImU5NTdmZmJhLTlhNzgtNGVhOS04ZWNhLWFlOGM0ZWY5Yzg1NiIsImF0X2hhc2giOiJ3Zmd2bUU5VnhqQXVkc2w5bGM2VHFB
In0
Signature value to verify:
lr4L-oT7DJi7Re0eSZDstAdOKHwSvjZfR-OpdWSOmsrw0QVeI7oaIcehyKUFpPFDXDR0-RsEzqno0yek-_U-Ui5EM-yv0PiaUOmJK1U-ws_C
-fCplUFSE7SK-TrCwaOow4_7FN5L4i-4NAa_WqgOjZPloT8o3kKyTkBL7GdITL8rEe4BDK8L6mLqHJrFX4SsEduPk0CyHJSykRqzYS2MEJln
cocBBI4up5Y5g2BNEb0aV4VZwYjmrv9oOUC_yC1Fb4Js5Ry1t6P4Q8q_2ka5OcArlo188XH7lMgPA2GnwSFGHBhccjpxhN7S46ubGPXRBNsn
rPx6RuoR2cI46d9ARQ
Note: The validation of the signature will depend on the language and/or platform in use. It is recommended to use a published library to perform the signature verification.
Encoding a JWS
Another common developer function is to encode a JWS to provide to a service or API. The steps involved are basically the reverse of the decoding process, and the following example creates a JWS
Note: It is strongly recommended to make use of common libraries for JWT and JWS processing to avoid introducing implementation specific bugs.
Step 1: Generate the JOSE Header
First we create the JSON object for the JWT header. The format of the header is defined by the API.
JSON object:
{
"alg": "HS256",
"org_alias": "aaaa-1234-xyzzy-12345678",
"token": "aa01330de012031"
}
Base64urlencoded value:
ewogICJhbGciOiAiSFMyNTYiLAogICJvcmdfYWxpYXMiOiAiYWFhYS0xMjM0LXh5enp5LTEyMzQ1Njc4IiwKICAidG9rZW4iOiAi
YWEwMTMzMGRlMDEyMDMxIgp9
Step 2: Generate the JWT Payload
Step 2: Generate the JWT Payload The payload follows the same process, create the JSON object and base64urlencode the value.
JSON payload:
{
"reqHeader": {
"orgAlias": "aaaa-1234-xyzzy-12345678",
"secretKey": "aa01330de012031",
"timestamp": "2015-07-23 11:19:56.38",
"version": "4.5",
"locale": "en",
},
"reqBody": {
"getSameDeviceUsers": true,
"userName": "marcher",
"adminId": null,
"clientData": null
}
}
Base64urlencoded value:
ewogICJyZXFIZWFkZXIiOiB7CiAgICAib3JnQWxpYXMiOiAiYWFhYS0xMjM0LXh5enp5LTEyMzQ1Njc4IiwKICAgICJzZWNyZXRL
ZXkiOiAiYWEwMTMzMGRlMDEyMDMxIiwKICAgICJ0aW1lc3RhbXAiOiAiMjAxNS0wNy0yMyAxMToxOTo1Ni4zOCIsCiAgICAidmVy
c2lvbiI6ICI0LjUiLAogICAgImxvY2FsZSI6ICJlbiIsCiAgfSwKICAicmVxQm9keSI6IHsKICAgICJnZXRTYW1lRGV2aWNlVXNl
cnMiOiB0cnVlLAogICAgInVzZXJOYW1lIjogIm1hcmNoZXIiLAogICAgImFkbWluSWQiOiBudWxsLAogICAgImNsaWVudERhdGEi
OiBudWxsCiAgfQp9
Step 3: Generate the Digital Signal
To generate the digital signature, we join the base64urlencoded values for the header and the payload (using a period to separate) and generate the digital signature according to the algorithm specified. In this case we are using the HS256 alg value (which uses the HMAC algorithm with a SHA-256 hash).
BASE64URLENCODE( HMACSHA256( BASE64URLENCODE(< header >) + "." + BASE64URLENCODE(< payload >), < symmetric key > ) )
This will result in a value similar to:
Vi3JphmmikK50FMPgAkPfaSoLsoHNhwMuUDGsrQxUH4=
Step 4: Put It All Together
The resulting JWS is a concatenation of the encoded header, payload and signature:
ewogICJhbGciOiAiSFMyNTYiLAogICJvcmdfYWxpYXMiOiAiYWFhYS0xMjM0LXh5enp5LTEyMzQ1Njc4IiwKICAidG9rZW4iOiAi
YWEwMTMzMGRlMDEyMDMxIgp9.ewogICJyZXFIZWFkZXIiOiB7CiAgICAib3JnQWxpYXMiOiAiYWFhYS0xMjM0LXh5enp5LTEyMzQ
1Njc4IiwKICAgICJzZWNyZXRLZXkiOiAiYWEwMTMzMGRlMDEyMDMxIiwKICAgICJ0aW1lc3RhbXAiOiAiMjAxNS0wNy0yMyAxMTo
xOTo1Ni4zOCIsCiAgICAidmVyc2lvbiI6ICI0LjUiLAogICAgImxvY2FsZSI6ICJlbiIsCiAgfSwKICAicmVxQm9keSI6IHsKICA
gICJnZXRTYW1lRGV2aWNlVXNlcnMiOiB0cnVlLAogICAgInVzZXJOYW1lIjogIm1hcmNoZXIiLAogICAgImFkbWluSWQiOiBudWx
sLAogICAgImNsaWVudERhdGEiOiBudWxsCiAgfQp9.Vi3JphmmikK50FMPgAkPfaSoLsoHNhwMuUDGsrQxUH4=
Libraries and Tools
It is highly recommended to use a common library to work with JWT's. Below are some common libraries for various languages.
Java | JOSE4J - by Brian Campbell |
JavaScript | jsrsasign - by Kenji Urushima |
.NET | Microsoft System.IdentityModel.Tokens.JwtSecurityToken (Microsoft .NET 4.5+) |