0

I'm writing some unit and integration tests around JWT creation and validation. I'm hacking together a quick JWKS page using real (well self-signed) certs I created with openssl, and I was cooking along until I got to this very simple problem:

The "E" value is 65537, but in the JWKS it is suppose to be AQAB. After beating my head on the wall for a very long time, I finally figured it out on paper.

https://www.rfc-editor.org/rfc/rfc7518#section-6.3.1.2

65537 is 0000 0001: 0000 0000 : 0000 0001

If you shift that to 6 bits for base64 you get

000000 010000 000000 000001

Which is A Q A B

https://en.wikipedia.org/wiki/Base64#Base64_table

So I tried to whip up some quick code. but it proved very not-quick. I was thinking, there MUST be an easier way. All the base64 encoding I tried, gave me way more than 3 bytes.

So did what any self respecting coder would do... I saw that everybody uses 65537, so I hard coded that sucker and moved on, but it just keeps nagging at me. I hate problems that vex me.

The closest that I got was:

bs := make([]byte, 4)
binary.BigEndian.PutUint32(bs, 65537)
fmt.Println(bs)
fmt.Println(base64.URLEncoding.EncodeToString(bs))

Which produced:

[0 1 0 1]
AAEAAQ==

which my quick math said, that base64 output is why more than 3 bytes :D

Community
  • 1
  • 1
Cade Thacker
  • 105
  • 2
  • 9

1 Answers1

1

You are base64 encoding 4 binary bytes:

bs := make([]byte, 4) // `AAEAAQ==`

which will produce 6 (4*8/6) base64 characters - plus the == padding.

You want to encode three binary bytes - which will produce 4 (3*8/6) base64 characters.

bs := []byte{1, 0, 1}
fmt.Println(bs)
fmt.Println(base64.URLEncoding.EncodeToString(bs)) // `AQAB`

https://play.golang.org/p/gDviu06pftR


EDIT

So you only want the lower-significant 3 bytes (at the end if using BigEndian).

So you can use your original code:

bs := make([]byte, 4)
binary.BigEndian.PutUint32(bs, 65537)

bs = bs[1:] // drop most significant byte - leaving least-significant 3-bytes

fmt.Println(base64.URLEncoding.EncodeToString(bs)) // `AQAB`

Or if you look at the source of binary.BigEndian.PutUint32 you can see how it extracts 4 bytes from a uint32 - and do the same for 3 bytes:

bs := make([]byte, 3)

v := 65537
bs[0] = byte(v >> 16)
bs[1] = byte(v >> 8)
bs[2] = byte(v)

fmt.Println(base64.URLEncoding.EncodeToString(bs)) // `AQAB`

https://play.golang.org/p/xTNzZMeQWVx

colm.anseo
  • 19,337
  • 4
  • 43
  • 52