Skip to content
This repository was archived by the owner on Feb 27, 2023. It is now read-only.

Commit aba61eb

Browse files
authored
Merge pull request #242 from square/cs/x5u-x5t
Support x5u, x5t, and x5t#S256 headers (for JWK)
2 parents 7be7aa3 + 402696e commit aba61eb

File tree

5 files changed

+274
-18
lines changed

5 files changed

+274
-18
lines changed

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ module github.com/square/go-jose
22

33
go 1.12
44

5-
require golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
5+
require (
6+
github.com/stretchr/testify v1.3.0
7+
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f
8+
)

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
6+
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
7+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
18
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
29
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
310
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=

jwk.go

Lines changed: 123 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717
package jose
1818

1919
import (
20+
"bytes"
2021
"crypto"
2122
"crypto/ecdsa"
2223
"crypto/elliptic"
2324
"crypto/rsa"
25+
"crypto/sha1"
26+
"crypto/sha256"
2427
"crypto/x509"
2528
"encoding/base64"
2629
"errors"
2730
"fmt"
2831
"math/big"
32+
"net/url"
2933
"reflect"
3034
"strings"
3135

@@ -57,16 +61,31 @@ type rawJSONWebKey struct {
5761
Dq *byteBuffer `json:"dq,omitempty"`
5862
Qi *byteBuffer `json:"qi,omitempty"`
5963
// Certificates
60-
X5c []string `json:"x5c,omitempty"`
64+
X5c []string `json:"x5c,omitempty"`
65+
X5u *url.URL `json:"x5u,omitempty"`
66+
X5tSHA1 *byteBuffer `json:"x5t,omitempty"`
67+
X5tSHA256 *byteBuffer `json:"x5t#S256,omitempty"`
6168
}
6269

6370
// JSONWebKey represents a public or private key in JWK format.
6471
type JSONWebKey struct {
65-
Key interface{}
72+
// Cryptographic key, can be a symmetric or asymmetric key.
73+
Key interface{}
74+
// Key identifier, parsed from `kid` header.
75+
KeyID string
76+
// Key algorithm, parsed from `alg` header.
77+
Algorithm string
78+
// Key use, parsed from `use` header.
79+
Use string
80+
81+
// X.509 certificate chain, parsed from `x5c` header.
6682
Certificates []*x509.Certificate
67-
KeyID string
68-
Algorithm string
69-
Use string
83+
// X.509 certificate URL, parsed from `x5u` header.
84+
CertificatesURL *url.URL
85+
// X.509 certificate thumbprint (SHA-1), parsed from `x5t` header.
86+
CertificateThumbprintSHA1 []byte
87+
// X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header.
88+
CertificateThumbprintSHA256 []byte
7089
}
7190

7291
// MarshalJSON serializes the given key to its JSON representation.
@@ -105,6 +124,36 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
105124
raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw))
106125
}
107126

127+
x5tSHA1Len := len(k.CertificateThumbprintSHA1)
128+
x5tSHA256Len := len(k.CertificateThumbprintSHA256)
129+
if x5tSHA1Len > 0 {
130+
if x5tSHA1Len != sha1.Size {
131+
return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len)
132+
}
133+
raw.X5tSHA1 = newFixedSizeBuffer(k.CertificateThumbprintSHA1, sha1.Size)
134+
}
135+
if x5tSHA256Len > 0 {
136+
if x5tSHA256Len != sha256.Size {
137+
return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len)
138+
}
139+
raw.X5tSHA256 = newFixedSizeBuffer(k.CertificateThumbprintSHA256, sha256.Size)
140+
}
141+
142+
// If cert chain is attached (as opposed to being behind a URL), check the
143+
// keys thumbprints to make sure they match what is expected. This is to
144+
// ensure we don't accidentally produce a JWK with semantically inconsistent
145+
// data in the headers.
146+
if len(k.Certificates) > 0 {
147+
expectedSHA1 := sha1.Sum(k.Certificates[0].Raw)
148+
expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw)
149+
if !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) ||
150+
!bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) {
151+
return nil, errors.New("square/go-jose: invalid SHA-1 or SHA-256 thumbprint, does not match cert chain")
152+
}
153+
}
154+
155+
raw.X5u = k.CertificatesURL
156+
108157
return json.Marshal(raw)
109158
}
110159

@@ -116,28 +165,61 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
116165
return err
117166
}
118167

168+
certs, err := parseCertificateChain(raw.X5c)
169+
if err != nil {
170+
return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err)
171+
}
172+
119173
var key interface{}
174+
var certPub interface{}
175+
var keyPub interface{}
176+
177+
if len(certs) > 0 {
178+
// We need to check that leaf public key matches the key embedded in this
179+
// JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise
180+
// the JWK parsed could be semantically invalid. Technically, should also
181+
// check key usage fields and other extensions on the cert here, but the
182+
// standard doesn't exactly explain how they're supposed to map from the
183+
// JWK representation to the X.509 extensions.
184+
certPub = certs[0].PublicKey
185+
}
186+
120187
switch raw.Kty {
121188
case "EC":
122189
if raw.D != nil {
123190
key, err = raw.ecPrivateKey()
191+
if err == nil {
192+
keyPub = key.(*ecdsa.PrivateKey).Public()
193+
}
124194
} else {
125195
key, err = raw.ecPublicKey()
196+
keyPub = key
126197
}
127198
case "RSA":
128199
if raw.D != nil {
129200
key, err = raw.rsaPrivateKey()
201+
if err == nil {
202+
keyPub = key.(*rsa.PrivateKey).Public()
203+
}
130204
} else {
131205
key, err = raw.rsaPublicKey()
206+
keyPub = key
132207
}
133208
case "oct":
209+
if certPub != nil {
210+
return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain")
211+
}
134212
key, err = raw.symmetricKey()
135213
case "OKP":
136214
if raw.Crv == "Ed25519" && raw.X != nil {
137215
if raw.D != nil {
138216
key, err = raw.edPrivateKey()
217+
if err == nil {
218+
keyPub = key.(ed25519.PrivateKey).Public()
219+
}
139220
} else {
140221
key, err = raw.edPublicKey()
222+
keyPub = key
141223
}
142224
} else {
143225
err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv)
@@ -146,12 +228,43 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
146228
err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty)
147229
}
148230

149-
if err == nil {
150-
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
231+
if err != nil {
232+
return
233+
}
234+
235+
if certPub != nil && keyPub != nil {
236+
if !reflect.DeepEqual(certPub, keyPub) {
237+
return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields to not match")
238+
}
239+
}
240+
241+
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs}
242+
243+
k.CertificatesURL = raw.X5u
244+
k.CertificateThumbprintSHA1 = raw.X5tSHA1.bytes()
245+
k.CertificateThumbprintSHA256 = raw.X5tSHA256.bytes()
246+
247+
x5tSHA1Len := len(k.CertificateThumbprintSHA1)
248+
x5tSHA256Len := len(k.CertificateThumbprintSHA256)
249+
if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size {
250+
return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size")
251+
}
252+
if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size {
253+
return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size")
254+
}
255+
256+
// If certificate chain *and* thumbprints are set, verify correctness.
257+
if len(k.Certificates) > 0 {
258+
leaf := k.Certificates[0]
259+
sha1sum := sha1.Sum(leaf.Raw)
260+
sha256sum := sha256.Sum256(leaf.Raw)
261+
262+
if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) {
263+
return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value")
264+
}
151265

152-
k.Certificates, err = parseCertificateChain(raw.X5c)
153-
if err != nil {
154-
return fmt.Errorf("failed to unmarshal x5c field: %s", err)
266+
if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) {
267+
return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value")
155268
}
156269
}
157270

jwk_test.go

Lines changed: 139 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import (
2222
"crypto/ecdsa"
2323
"crypto/elliptic"
2424
"crypto/rsa"
25+
"crypto/sha1"
26+
"crypto/sha256"
2527
"crypto/x509"
2628
"encoding/hex"
2729
"math/big"
@@ -240,12 +242,17 @@ func TestRoundtripEcPrivate(t *testing.T) {
240242
}
241243
}
242244

243-
func TestRoundtripX5C(t *testing.T) {
245+
func TestRoundtripX509(t *testing.T) {
246+
x5tSHA1 := sha1.Sum(testCertificates[0].Raw)
247+
x5tSHA256 := sha256.Sum256(testCertificates[0].Raw)
248+
244249
jwk := JSONWebKey{
245-
Key: rsaTestKey,
246-
KeyID: "bar",
247-
Algorithm: "foo",
248-
Certificates: testCertificates,
250+
Key: testCertificates[0].PublicKey,
251+
KeyID: "bar",
252+
Algorithm: "foo",
253+
Certificates: testCertificates,
254+
CertificateThumbprintSHA1: x5tSHA1[:],
255+
CertificateThumbprintSHA256: x5tSHA256[:],
249256
}
250257

251258
jsonbar, err := jwk.MarshalJSON()
@@ -272,10 +279,136 @@ func TestRoundtripX5C(t *testing.T) {
272279
}
273280
}
274281

282+
func TestInvalidThumbprintsX509(t *testing.T) {
283+
// Too short
284+
jwk := JSONWebKey{
285+
Key: rsaTestKey,
286+
KeyID: "bar",
287+
Algorithm: "foo",
288+
Certificates: testCertificates,
289+
CertificateThumbprintSHA1: []byte{0x01}, // invalid length
290+
CertificateThumbprintSHA256: []byte{0x02}, // invalid length
291+
}
292+
293+
_, err := jwk.MarshalJSON()
294+
if err == nil {
295+
t.Error("should not marshal JWK with too short thumbprints")
296+
}
297+
298+
// Mismatched (leaf has different sum)
299+
sha1sum := sha1.Sum(nil)
300+
jwk.CertificateThumbprintSHA1 = sha1sum[:]
301+
sha256sum := sha256.Sum256(nil)
302+
jwk.CertificateThumbprintSHA256 = sha256sum[:]
303+
304+
_, err = jwk.MarshalJSON()
305+
if err == nil {
306+
t.Error("should not marshal JWK with mismatched thumbprints")
307+
}
308+
309+
// Too short
310+
shortThumbprints := []byte(`{
311+
"kty": "RSA",
312+
"kid": "bar",
313+
"alg": "foo",
314+
"n": "wN3v274Fr7grvZdXirEGkHlhYKh72gP-46MxQilMgANi6EaX2m0mYiMC60X1UmOhQNoVW0ItthMME-CGh7haA_Jeou_L6-EVOz-7lGu_J06VRl-mgkQZO0sYSkRY8Rsu7TW-pgnWWwZjSdgxN2gq7DvjjC4RLroV94Lgrb2Qwx6J5bZNOfj3pHmEWZ_eRFEH73Ct5RxwUKtNKx_XAmodZn9oFXBlN7F2Js-4DO-8UgbS7ALkTmhDzClT1GPfdLMusmXw4BXyV72vBOWrbUTdwk4pG8ahPQ0cGQ1ubaAmro_k3Bxg06voWPhXx7ALrpzmZCzr6YY-c5Y5rku4qXh-HQ",
315+
"e": "AQAB",
316+
"d": "RRM3xMvZ3YVopQ5_G_0rDLNsXOH6-apUr9LS4Y9JBtAvrGEcIe7VwHApq3ny0v870a5J19Vr6boIqVXQ2Or90kwL-O9JacHDiOTamd29KKbMb9fyGtWo88OBf5fbAv9pXyvQjEcZrqArD1eOyPlV5iXM6XfWT5X2KB-HuLIcFsVJSEb0yw943dEhZKkv2fySlVtKEhji2CfkMWP438G8auxwFPNIXmuvA1xvcAepOiI9I1Wy17txLOQg8MYBl98F7mxPUMpL3jm8-CSuxpknucFuFIrqIsbGmukSNp14APcu7bN0cJW5uVW-XtGbuioJkPjU2YJgfhIeMI8kuny5QQ",
317+
"p": "yRNzbsreZK9MqJgbVsbl7SX2MyyJW_JnFKGdyrXzMqCtzRS7XJ9L3SwdDG_ERgSCkT9PP2dax9fS7RghHNJIU_NlPnJqzBPvPyzbjho7hcGYGDU2UO8E3SBP1WKm1SnlYhm1uHwrHudAO0D5jhVYQfRwem-zX3QibrsxEyxSELM",
318+
"q": "9Yx0zzrhSxdE8ubryZfOyOw6cSbUdyCMgY3IFmEdb_UDZOQNMDCFj2RS1g6yFGKuaFqnL9ZRnmprikf20w-mu-LtLU_l0eK4U7pbAoB--pxFP_O6Yk3ZBu_YJ3FpimyX1v4OVZT-JOU_caKiTrPnlw3P7KbEc9iu_bOc8dE-_e8",
319+
"dp": "GwiFbXDS44B58vS4QDtvcCm5Zvnm4bi-SRTNbRJ3Rug5Vagi5Hn6LhsfMKvaHHvAvhxf4CtaFiIbFos28HQJC1he1T12xEct1DWIsxstw3bapu6IhesMoVoVwZ-IxIHkeALy3oG7HmWCyjSbGJIgEoX1lVBtMjkf4_lAyM4dnmc",
320+
"dq": "XYtXyMbOo3PG8Z6lfxRVU9gi346CbKu6u3RPIK94rnkyBNKYb55ck2cN47yPfRKnDNxUSvYj--zg8To_PuL8iyGFZ7jDffUYcdVR7J8VQNYdz6JDhEXSA0GGIGilY3XBVsdMoK_1Lgsj41-o48DH3pUFfEuAFf4blE1D4h_sFoM",
321+
"qi": "CN3q5TMZO06DlK4onrJ687bPp2GE-fynd7ADPdf1ek3cXbeaFApVNp4w-i8zr7IVugQohn01i_jnkymw4UN-acoIzX2sGYhgDm6le1jyfv28bQ_Z5hT0rFNlPfGyK0kkPYUTxZ4ZCKpCYJT9C1nH58Yu4xFk4VR1zhULegjCCd4",
322+
"x5c": [
323+
"MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoXDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAsBgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sMlM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7dvBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeLFj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2XliJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU="
324+
],
325+
"x5t": "BhJU_fcg8sDkvT8zOaL-U1C",
326+
"x5t#S256": "IDJKS5LxImjvmuAl_Vx50Vj_dFdACSGW6DU8ADF"
327+
}`)
328+
329+
// Mismatched (leaf has different sum)
330+
mismatchedThumbprints := []byte(`{
331+
"kty": "RSA",
332+
"kid": "bar",
333+
"alg": "foo",
334+
"n": "wN3v274Fr7grvZdXirEGkHlhYKh72gP-46MxQilMgANi6EaX2m0mYiMC60X1UmOhQNoVW0ItthMME-CGh7haA_Jeou_L6-EVOz-7lGu_J06VRl-mgkQZO0sYSkRY8Rsu7TW-pgnWWwZjSdgxN2gq7DvjjC4RLroV94Lgrb2Qwx6J5bZNOfj3pHmEWZ_eRFEH73Ct5RxwUKtNKx_XAmodZn9oFXBlN7F2Js-4DO-8UgbS7ALkTmhDzClT1GPfdLMusmXw4BXyV72vBOWrbUTdwk4pG8ahPQ0cGQ1ubaAmro_k3Bxg06voWPhXx7ALrpzmZCzr6YY-c5Y5rku4qXh-HQ",
335+
"e": "AQAB",
336+
"d": "RRM3xMvZ3YVopQ5_G_0rDLNsXOH6-apUr9LS4Y9JBtAvrGEcIe7VwHApq3ny0v870a5J19Vr6boIqVXQ2Or90kwL-O9JacHDiOTamd29KKbMb9fyGtWo88OBf5fbAv9pXyvQjEcZrqArD1eOyPlV5iXM6XfWT5X2KB-HuLIcFsVJSEb0yw943dEhZKkv2fySlVtKEhji2CfkMWP438G8auxwFPNIXmuvA1xvcAepOiI9I1Wy17txLOQg8MYBl98F7mxPUMpL3jm8-CSuxpknucFuFIrqIsbGmukSNp14APcu7bN0cJW5uVW-XtGbuioJkPjU2YJgfhIeMI8kuny5QQ",
337+
"p": "yRNzbsreZK9MqJgbVsbl7SX2MyyJW_JnFKGdyrXzMqCtzRS7XJ9L3SwdDG_ERgSCkT9PP2dax9fS7RghHNJIU_NlPnJqzBPvPyzbjho7hcGYGDU2UO8E3SBP1WKm1SnlYhm1uHwrHudAO0D5jhVYQfRwem-zX3QibrsxEyxSELM",
338+
"q": "9Yx0zzrhSxdE8ubryZfOyOw6cSbUdyCMgY3IFmEdb_UDZOQNMDCFj2RS1g6yFGKuaFqnL9ZRnmprikf20w-mu-LtLU_l0eK4U7pbAoB--pxFP_O6Yk3ZBu_YJ3FpimyX1v4OVZT-JOU_caKiTrPnlw3P7KbEc9iu_bOc8dE-_e8",
339+
"dp": "GwiFbXDS44B58vS4QDtvcCm5Zvnm4bi-SRTNbRJ3Rug5Vagi5Hn6LhsfMKvaHHvAvhxf4CtaFiIbFos28HQJC1he1T12xEct1DWIsxstw3bapu6IhesMoVoVwZ-IxIHkeALy3oG7HmWCyjSbGJIgEoX1lVBtMjkf4_lAyM4dnmc",
340+
"dq": "XYtXyMbOo3PG8Z6lfxRVU9gi346CbKu6u3RPIK94rnkyBNKYb55ck2cN47yPfRKnDNxUSvYj--zg8To_PuL8iyGFZ7jDffUYcdVR7J8VQNYdz6JDhEXSA0GGIGilY3XBVsdMoK_1Lgsj41-o48DH3pUFfEuAFf4blE1D4h_sFoM",
341+
"qi": "CN3q5TMZO06DlK4onrJ687bPp2GE-fynd7ADPdf1ek3cXbeaFApVNp4w-i8zr7IVugQohn01i_jnkymw4UN-acoIzX2sGYhgDm6le1jyfv28bQ_Z5hT0rFNlPfGyK0kkPYUTxZ4ZCKpCYJT9C1nH58Yu4xFk4VR1zhULegjCCd4",
342+
"x5c": [
343+
"MIIDfDCCAmSgAwIBAgIJANWAkzF7PA8/MA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1sZWFmMB4XDTE2MDYxMDIyMTQxMVoXDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLWxlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7stSvfQyGuHw3v34fisqIdDXberrFoFk9ht/WdXgYzX2uLNKdsR/J5sbWSl8K/5djpzj31eIzqU69w8v7SChM5x9bouDsABHz3kZucx5cSafEgJojysBkcrq3VY+aJanzbL+qErYX+lhRpPcZK6JMWIwar8Y3B2la4yWwieecw2/WfEVvG0M/DOYKnR8QHFsfl3US1dnBM84czKPyt9r40gDk2XiH/lGts5a94rAGvbr8IMCtq0mA5aH3Fx3mDSi3+4MZwygCAHrF5O5iSV9rEI+m2+7j2S+jHDUnvV+nqcpb9m6ENECnYX8FD2KcqlOjTmw8smDy09N2Np6i464lAgMBAAGjTzBNMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAsBgNVHREEJTAjhwR/AAABhxAAAAAAAAAAAAAAAAAAAAABgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggEBAGM4aa/qrURUweZBIwZYv8O9b2+r4l0HjGAh982/B9sMlM05kojyDCUGvj86z18Lm8mKr4/y+i0nJ+vDIksEvfDuzw5ALAXGcBzPJKtICUf7LstA/n9NNpshWz0kld9ylnB5mbUzSFDncVyeXkEf5sGQXdIIZT9ChRBoiloSaa7dvBVCcsX1LGP2LWqKtD+7nUnw5qCwtyAVT8pthEUxFTpywoiJS5ZdzeEx8MNGvUeLFj2kleqPF78EioEQlSOxViCuctEtnQuPcDLHNFr10byTZY9roObiqdsJLMVvb2XliJjAqaPa9AkYwGE6xHw2ispwg64Rse0+AtKups19WIU="
344+
],
345+
"x5t": "BhJU_fcg8sDkvT8zOaL-U1ChXeX",
346+
"x5t#S256": "IDJKS5LxImjvmuAl_Vx50Vj_dFdACSGW6DU8ADFRdRX"
347+
}`)
348+
349+
var jwk2 JSONWebKey
350+
err = jwk2.UnmarshalJSON(mismatchedThumbprints)
351+
if err == nil {
352+
t.Error("should not unmarshal JWK with mismatched thumbprints")
353+
}
354+
355+
err = jwk2.UnmarshalJSON(shortThumbprints)
356+
if err == nil {
357+
t.Error("should not unmarshal JWK with too short thumbprints")
358+
}
359+
}
360+
361+
func TestKeyMismatchX509(t *testing.T) {
362+
x5tSHA1 := sha1.Sum(testCertificates[0].Raw)
363+
x5tSHA256 := sha256.Sum256(testCertificates[0].Raw)
364+
365+
jwk := JSONWebKey{
366+
KeyID: "bar",
367+
Algorithm: "foo",
368+
Certificates: testCertificates,
369+
CertificateThumbprintSHA1: x5tSHA1[:],
370+
CertificateThumbprintSHA256: x5tSHA256[:],
371+
}
372+
373+
for _, key := range []interface{}{
374+
// None of these keys should match what's in the cert, so parsing should always fail.
375+
ecTestKey256,
376+
ecTestKey256.Public(),
377+
ecTestKey384,
378+
ecTestKey384.Public(),
379+
ecTestKey521,
380+
ecTestKey521.Public(),
381+
rsaTestKey,
382+
rsaTestKey.Public(),
383+
ed25519PrivateKey,
384+
ed25519PrivateKey.Public(),
385+
} {
386+
jwk.Key = key
387+
raw, _ := jwk.MarshalJSON()
388+
389+
var jwk2 JSONWebKey
390+
err := jwk2.UnmarshalJSON(raw)
391+
if err == nil {
392+
t.Error("should not unmarshal JWK with key/cert mismatch")
393+
}
394+
}
395+
}
396+
275397
func TestMarshalUnmarshal(t *testing.T) {
276398
kid := "DEADBEEF"
277399

278-
for i, key := range []interface{}{ecTestKey256, ecTestKey384, ecTestKey521, rsaTestKey, ed25519PrivateKey} {
400+
for i, key := range []interface{}{
401+
ecTestKey256,
402+
ecTestKey256.Public(),
403+
ecTestKey384,
404+
ecTestKey384.Public(),
405+
ecTestKey521,
406+
ecTestKey521.Public(),
407+
rsaTestKey,
408+
rsaTestKey.Public(),
409+
ed25519PrivateKey,
410+
ed25519PrivateKey.Public(),
411+
} {
279412
for _, use := range []string{"", "sig", "enc"} {
280413
jwk := JSONWebKey{Key: key, KeyID: kid, Algorithm: "foo"}
281414
if use != "" {

0 commit comments

Comments
 (0)