Skip to content

Instantly share code, notes, and snippets.

@monmohan
Created November 7, 2019 10:06
Show Gist options
  • Save monmohan/f06914e527e032ec9512787ca9187eae to your computer and use it in GitHub Desktop.
Save monmohan/f06914e527e032ec9512787ca9187eae to your computer and use it in GitHub Desktop.
Sample JWS Validation with RSA Key
package jws
import (
"bytes"
"crypto"
"crypto/rsa"
_ "crypto/sha256"
"encoding/base64"
"encoding/binary"
"math/big"
"strings"
)
type JWKS struct {
Keys []JWK
}
type JWK struct {
Alg string
Kty string
X5c []string
N string
E string
Kid string
X5t string
}
func verifySignature(jwtToken string, key JWK) error {
parts := strings.Split(jwtToken, ".")
message := []byte(strings.Join(parts[0:2], "."))
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
if err != nil {
return err
}
n, _ := base64.RawURLEncoding.DecodeString(key.N)
e, _ := base64.RawURLEncoding.DecodeString(key.E)
z := new(big.Int)
z.SetBytes(n)
//decoding key.E returns a three byte slice, https://golang.org/pkg/encoding/binary/#Read and other conversions fail
//since they are expecting to read as many bytes as the size of int being returned (4 bytes for uint32 for example)
var buffer bytes.Buffer
buffer.WriteByte(0)
buffer.Write(e)
exponent := binary.BigEndian.Uint32(buffer.Bytes())
publicKey := &rsa.PublicKey{N: z, E: int(exponent)}
// Only small messages can be signed directly; thus the hash of a
// message, rather than the message itself, is signed.
hasher := crypto.SHA256.New()
hasher.Write(message)
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hasher.Sum(nil), signature)
return err
}
package jws
import (
"encoding/json"
"log"
"os"
"testing"
)
func TestPublicKey(t *testing.T) {
jwtToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FTXlNRUZDTXpVd01URTFRVE5CT1VGRE1FUTFPRGN6UmprNU56QkdRelk0UVRrMVEwWkVPUSJ9.eyJpc3MiOiJodHRwczovL2Rldi1lanRsOTg4dy5hdXRoMC5jb20vIiwic3ViIjoiZ1pTeXNwQ1k1ZEk0aDFaM3Fwd3BkYjlUNFVQZEdENWtAY2xpZW50cyIsImF1ZCI6Imh0dHA6Ly9oZWxsb3dvcmxkIiwiaWF0IjoxNTcyNDA2NDQ3LCJleHAiOjE1NzI0OTI4NDcsImF6cCI6ImdaU3lzcENZNWRJNGgxWjNxcHdwZGI5VDRVUGRHRDVrIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.nupgm7iFqSnERq9GxszwBrsYrYfMuSfUGj8tGQlkY3Ksh3o_IDfq1GO5ngHQLZuYPD-8qPIovPBEVomGZCo_jYvsbjmYkalAStmF01TvSoXQgJd09ygZstH0liKsmINStiRE8fTA-yfEIuBYttROizx-cDoxiindbKNIGOsqf6yOxf7ww8DrTBJKYRnHVkAfIK8wm9LRpsaOVzWdC7S3cbhCKvANjT0RTRpAx8b_AOr_UCpOr8paj-xMT9Zc9HVCMZLBfj6OZ6yVvnC9g6q_SlTa--fY9SL5eqy6-q1JGoyK_-BQ_YrCwrRdrjoJsJ8j-XFRFWJX09W3oDuZ990nGA"
jwksFile, err := os.Open("./jwks.json")
if err != nil {
t.Fatalf("Error in reading jwks %s", err.Error())
}
dec := json.NewDecoder(jwksFile)
var jwks JWKS
if err := dec.Decode(&jwks); err != nil {
t.Fatalf("Unable to read key %s", err)
}
log.Printf("%v", jwks)
err = verifySignature(jwtToken, jwks.Keys[0])
if err != nil {
t.Fatalf("Failed signature verification : %s", err)
}
}
{
"keys": [
{
"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"
}
]
}
@ianaz
Copy link

ianaz commented Aug 7, 2020

How can I generate the x5t? I need this for my unit tests and still no clue how I get to that

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment