Digital Signature : Image courtesy : clereporting.com

Validating RSA signature for a JWS

msingh
4 min readNov 7, 2019

--

This article discusses validation of RSA signatures for a JWS. A golang sample code is also provided at the end

Pre-requisite

JWT, JWS and Signature

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object.

Lets break it down -

  1. Compact and self-contained : JWT consists of three parts separated by dot (.) — Header, Payload and Signature which are themselves JSON objects with standard well-defined fields.
  2. Secure Information Exchange : JWTs can be digitally signed which is used to verify the integrity of claims (payload of the JWT). Additionally, public-private key based signing also asserts the identification of the party which signed the JWT.

An example JWT is shown below, returned from an Auth0 Authorization Server

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FTXlNRUZDTXpVd01URTFRVE5CT1VGRE1FUTFPRGN6UmprNU56QkdRelk0UVRrMVEwWkVPUSJ9.eyJpc3MiOiJodHRwczovL2Rldi1lanRsOTg4dy5hdXRoMC5jb20vIiwic3ViIjoiZ1pTeXNwQ1k1ZEk0aDFaM3Fwd3BkYjlUNFVQZEdENWtAY2xpZW50cyIsImF1ZCI6Imh0dHA6Ly9oZWxsb3dvcmxkIiwiaWF0IjoxNTcyNDA2NDQ3LCJleHAiOjE1NzI0OTI4NDcsImF6cCI6ImdaU3lzcENZNWRJNGgxWjNxcHdwZGI5VDRVUGRHRDVrIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.nupgm7iFqSnERq9GxszwBrsYrYfMuSfUGj8tGQlkY3Ksh3o_IDfq1GO5ngHQLZuYPD-8qPIovPBEVomGZCo_jYvsbjmYkalAStmF01TvSoXQgJd09ygZstH0liKsmINStiRE8fTA-yfEIuBYttROizx-cDoxiindbKNIGOsqf6yOxf7ww8DrTBJKYRnHVkAfIK8wm9LRpsaOVzWdC7S3cbhCKvANjT0RTRpAx8b_AOr_UCpOr8paj-xMT9Zc9HVCMZLBfj6OZ6yVvnC9g6q_SlTa — fY9SL5eqy6-q1JGoyK_-BQ_YrCwrRdrjoJsJ8j-XFRFWJX09W3oDuZ990nGA

JWS

What we showed above is essentially a JWS, a signed representation of the JWT. The representation is in following format -

Base64URLEncoded(header).Base64URLEncoded(payload).Base64URLEncoded(Signature of Base64URLEncoded(header).Base64URLEncoded(payload))

If we were to decode the above JWT, here is how the different parts look

Header

which defines the algorithm and token type.

{
“typ”: “JWT”,
“alg”: “RS256”,
“kid”: “NEMyMEFCMzUwMTE1QTNBOUFDMEQ1ODczRjk5NzBGQzY4QTk1Q0ZEOQ”
}

Payload

Its the data contained with the JWT (also called claims)

{
"iss": "https://dev-ejtl988w.auth0.com/",
"sub": "gZSyspCY5dI4h1Z3qpwpdb9T4UPdGD5k@clients",
"aud": "http://helloworld",
"iat": 1572406447,
"exp": 1572492847,
"azp": "gZSyspCY5dI4h1Z3qpwpdb9T4UPdGD5k",
"gty": "client-credentials"
}

Signature

The signature created using the private key of the party that created the JWT

nupgm7iFqSnERq9GxszwBrsYrYfMuSfUGj8tGQlkY3Ksh3o_IDfq1GO5ngHQLZuYPD-8qPIovPBEVomGZCo_jYvsbjmYkalAStmF01TvSoXQgJd09ygZstH0liKsmINStiRE8fTA-yfEIuBYttROizx-cDoxiindbKNIGOsqf6yOxf7ww8DrTBJKYRnHVkAfIK8wm9LRpsaOVzWdC7S3cbhCKvANjT0RTRpAx8b_AOr_UCpOr8paj-xMT9Zc9HVCMZLBfj6OZ6yVvnC9g6q_SlTa--fY9SL5eqy6-q1JGoyK_-BQ_YrCwrRdrjoJsJ8j-XFRFWJX09W3oDuZ990nGA

Let’s deep dive in the signature. As you can see, the header defined the JSON as type JWT and is signed using RS256 which essentially is RSA Signature generation with SHA-256 hashing.

What does signing mean?

💡 The key thing to understand is, that, RSA signing is not the same as RSA encryption.

Given that the payload size can be arbitrarily large, it can’t be signed directly using RSA private key. With encryption schemes, the large message problem is solved with block cipher modes. In case of digital signature, which is what we want to do, this is solved by first applying a cryptographic hash to the message (SHA-256 in our case) and then the hash is signed using the private key to generate the digital signature.

Validating the Signature

So now we have a signed JWT (i.e. JWS), it has base64 encoded header, payload, and signature separated by dot (.) . Since the signature is created by using the private key, its validation requires access to the corresponding public key.

Public Key

Public key, as the name suggests is public, and meant to be shared. There are many ways to get hold of public keys like getting access to the certificate but in this article I would like to highlight another common publishing standard — JWK

JWK and JKWS

A JSON Web Key (JWK) is a JSON data structure that represents a cryptographic key. A JWK Set JSON data structure as the name suggests, represents a set of JWKs.

JWK will have members that are key type specific , the idea being that it provides enough information to the consumer to verify signature.

{"alg": "RS256","kty": "RSA","use": "sig","x5c": ["MIIDBzCCAe+gAwIBAgIJakoPho0MJr56MA0GCSqGSIb3DQEBCwUAMCExHzAdBgNVBAMTFmRldi1lanRsOTg4dy5hdXRoMC5jb20wHhcNMTkxMDI5MjIwNzIyWhcNMzMwNzA3MjIwNzIyWjAhMR8wHQYDVQQDExZkZXYtZWp0bDk4OHcuYXV0aDAuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzkM1QHcP0v8bmwQ2fd3Pj6unCTx5k8LsW9cuLtUhAjjzRGpSEwGCKEgi1ej2+0Cxcs1t0wzhO+zSv1TJbsDI0x862PIFEs3xkGqPZU6rfQMzvCmncAcMjuW7r/Zewm0s58oRGyic1Oyp8xiy78czlBG03jk/+/vdttJkie8pUc9AHBuMxAaV4iPN3zSi/J5OVSlovk607H3AUiL3Bfg4ssS1bsJvaFG0kuNscoiP+qLRTjFK6LzZS99VxegeNzttqGbtj5BwNgbtuzrIyfLmYB/9VgEw+QdaQHvxoAvD0f7aYsaJ1R6rrqxo+1Pun7j1/h7kOCGB0UcHDLDw7gaP/wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQwIoo6QzzUL/TcNVpLGrLdd3DAIzAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADggEBALb8QycRmauyC/HRWRxTbl0w231HTAVYizQqhFQFl3beSQIhexGik+H+B4ve2rv94QRD3LlraUp+J26wLG89EnSCuCo/OxPAq+lxO6hNf6oKJ+Y2f48awIOxolO0f89qX3KMIkABXwKbYUcd+SBHX5ZP1V9cvJEyH0s3Fq9ObysPCH2j2Hjgz3WMIffSFMaO0DIfh3eNnv9hKQwavUO7fL/jqhBl4QxI2gMySi0Ni7PgAlBgxBx6YUp59q/lzMgAf19GOEOvI7l4dA0bc9pdsm7OhimskvOUSZYi5Pz3n/i/cTVKKhlj6NyINkMXlXGgyM9vEBpdcIpOWn/1H5QVy8Q="],"n": "zkM1QHcP0v8bmwQ2fd3Pj6unCTx5k8LsW9cuLtUhAjjzRGpSEwGCKEgi1ej2-0Cxcs1t0wzhO-zSv1TJbsDI0x862PIFEs3xkGqPZU6rfQMzvCmncAcMjuW7r_Zewm0s58oRGyic1Oyp8xiy78czlBG03jk_-_vdttJkie8pUc9AHBuMxAaV4iPN3zSi_J5OVSlovk607H3AUiL3Bfg4ssS1bsJvaFG0kuNscoiP-qLRTjFK6LzZS99VxegeNzttqGbtj5BwNgbtuzrIyfLmYB_9VgEw-QdaQHvxoAvD0f7aYsaJ1R6rrqxo-1Pun7j1_h7kOCGB0UcHDLDw7gaP_w","e": "AQAB","kid": "NEMyMEFCMzUwMTE1QTNBOUFDMEQ1ODczRjk5NzBGQzY4QTk1Q0ZEOQ","x5t": "NEMyMEFCMzUwMTE1QTNBOUFDMEQ1ODczRjk5NzBGQzY4QTk1Q0ZEOQ"}

Some members of the above JSON are self explanatory e.g. kty which defines the key type being RSA and alg which defines the algorithm being RSA256. The rest of members define the public key (redundantly).

At its core, the definition of an RSA Key as defined by RFC 8017 is very simple

RSAPublicKey ::= SEQUENCE {
modulus INTEGER, -- n
publicExponent INTEGER -- e
}

Now if you look at the JWK we obtained earlier, you realize that both n (also called modulus) and e (called exponent) are present as string. This string is a base64url encoding of the value’s big endian representation.

So, now we have everything we need to validate the signature and the process is very simple

  1. Read the parts of JWT which were signed. It would be Base64(header).(Base64(payload)
  2. Read n and e from JWK and base64 decode them. Reconstruct a BigInt and int respectively to represent these numbers
  3. Create SHA256 hash of the value obtained in 1)
  4. Use a verifier method like https://golang.org/pkg/crypto/rsa/#VerifyPKCS1v15 with the inputs gathered from first 3 steps.

For a complete golang sample code with a signed JWT, JWK and verification method see below gist -

Validation code with sample JWK from and a JWS

--

--