1/*- 2 * Copyright 2014 Square Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package jose 18 19import ( 20 "crypto/x509" 21 "encoding/base64" 22 "strings" 23 "testing" 24 25 "github.com/stretchr/testify/assert" 26) 27 28const trustedCA = ` 29-----BEGIN CERTIFICATE----- 30MIIE6DCCAtCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUcnVz 31dGVkQ0EwHhcNMTgwMzI4MTg0MzA0WhcNMzgwMzI4MTg0MzA0WjAUMRIwEAYDVQQD 32EwlUcnVzdGVkQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCsHcd3 33uaKBilWQUe2epNf86xvq2HZV+JDULjJlKfUQAkpG+huHDEMiPEFPSlQK17bFj7gc 34qOx/INeeCU2nBVtZDtlm3U0jfQWO2F2kZgH1JWnEArrAWWy3BP/NYv7apBLcl7nD 35hkL4USVUnXF8mtuegiSMI2YT7TVchGzYMjrj/j+oRuDm1GF1OxoIMeUuVmqyJ6jK 36Kxv9YVmCB+e/QaUltkPGwxl2dKWdBwECXDgSr7hcZhT8ANmgFR1dJjLCy0Us12yw 375eKUANDlfNP+z9urykoAwHXpBlmga1ze45aL+p+7K+8sl/PgMqKO7VdT5GBsOCzf 38xaBDG5Qy92Di34Sc27ZZz0mfaIy5kySnceBclMyWb8vdhEGkyHVsGpWc63JBmtg+ 39bKeh876m7KVLfiykfpMqHUhq/ImQwiQTwX2RonFK5gP+XU0I9V+4rE0iqucbcvCS 40HuHzhf6B+TybhalRsvOZ6GB/SokF5YCmf8ylAq4be/HSxnJQcBhpSSQp0zz4ZKOD 41ikXuwf29yhWZ0lgIyaZpT9H4QecWNcyx4UcqO3wQAGjxadTG3gzjLu/OJwPkw+bK 42RvXWSBZjlQ9+JPmrHH+oKMgHshR4TQmtmXqXLaarrAe+HXCZEiBKFOqPgeo2RMxr 43LAO+MYIsVtEz39gISRhEvqcAls01sV1l7oGicQIDAQABo0UwQzAOBgNVHQ8BAf8E 44BAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUy9Nqk0mDRwC5tcmN 45xQ1YWO5MAhgwDQYJKoZIhvcNAQELBQADggIBAHbpsqY+tPSj8BSky+acZoZjF7fZ 46Ae3MKVogwm5tecmwTKf5xDj9J99ZpGvcWCKtoxxWw0LZ+JI/aeqANSRDXZIelcIw 47yZefw06z/coQJwAIy1RSoKJPV72mkG0Es9w2HxSEoLaZ9tql0TyV8D/QseUM8Yt/ 48nNtShRoj6iMnZjhmut5pLfrLWHwQkt4fguBpL7rtydS/wAsOmnJ7lmOrU6zrBJzD 49vEER3AJtgdIt4GvKf4MupKLgKvYDB4sUQVmMyAS78B9+WZDDRTClsx+/Oc1ggkWz 508X7EmIw+3U9V2hd67qZ81EwcSB8ixV06E7ZcbhnJs7ds7swqUjwMArFWuzqO4cjW 512BnyVzCO9pymFLI7qol32xCEgaQlOVS/kFHP3meygfeaeYe902sJw6NevOA4e0AO 52AKR8FDfGRXJ9cOmYzeHeWKex8yt1Ul6+N8SXzjOhf39JM0QqTfHN7pPfFthTAFOs 539rI/buJteJqR1WxgVk/jY4wLGEOcEyO6Y/Uj5iWWTvm5G/C1yZfSg+NvWoytxZ7P 543S0qtEfmT4UwuHBsd5ZfEZoxb+GbqL/nhrKz/0B9LyKS0SJP9+mz7nSORz7t35Uc 55BhiG6T9W7P/NRW4Tqb2tEN1VwU6eP5SEf7c7C1VVaepk0fvc1p5dl67IERqPucPD 56dT2rDsCMBV7SXMUM 57-----END CERTIFICATE-----` 58 59const intermediateCA = ` 60-----BEGIN CERTIFICATE----- 61MIIEHTCCAgWgAwIBAgIQXzZsEQv0cvSRLJAkS9FmWTANBgkqhkiG9w0BAQsFADAU 62MRIwEAYDVQQDEwlUcnVzdGVkQ0EwHhcNMTgwMzI4MTg0MzMzWhcNMzgwMzI4MTg0 63MzAzWjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTCCASIwDQYJKoZIhvcNAQEB 64BQADggEPADCCAQoCggEBAN3aYpH/1yEYf/kHuHWyO3AO4tgwlYYLhCDT2GvaPdaE 65cqhe/VuYiqx3xY7IRDqsW2rau/OXgW6KzLHdRZHogK07hUj1Lfr7X+Oqbp22IV4y 66dyiL7jwK9AtVXvDuuv5ET+oRfV82j0uhyk0ueGD9r6C/h+6NTzHBD+3xo6Yuc0Vk 67BfY5zIyhaFqlm1aRYvupDRjC/63uBgAlrGxy2LyiTMVnYMuxoJM5ahDepz3sqjuN 68WVyPhfGwIezjXuXRdEvlmWX05XLnsTdP4zu4fHq9Z7c3TKWWONM3z64ECAZmGQVf 69MAcEDX7qP0gZX5PCT+0WcvTgTWE4Q+WIh5AmYyxQ04cCAwEAAaNmMGQwDgYDVR0P 70AQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMAYlq86RZzT 71WxLpYE7KTTM7DHOuMB8GA1UdIwQYMBaAFMvTapNJg0cAubXJjcUNWFjuTAIYMA0G 72CSqGSIb3DQEBCwUAA4ICAQBmYRpQoWEm5g16kwUrpwWrH7OIqqMtUhM1dcskECfk 73i3/hcsV+MQRkGHLIItucYIWqs7oOQIglsyGcohAbnvE1PVtKKojUHC0lfbjgIenD 74Pbvz15QB6A3KLDR82QbQGeGniACy924p66zlfPwHJbkMo5ZaqtNqI//EIa2YCpyy 75okhFXaSFmPWXXrTOCsEEsFJKsoSCH1KUpTcwACGkkilNseg1edZB6/lBDwybxVuY 76+dbUlHip3r5tFcP66Co3tKAaEcVY0AsZ/8GKwH+IM2AR6q7jdn9Gp2OX4E1ul9Wy 77+hW5GHMmfixkgTVwRowuKgkCPEKV2/Xy3k9rlSpnKr2NpYYq0mu6An9HYt8THQ+e 78wGZHwWufuDFDWuzlu7CxFOjpXLKv8qqVnwSFC91S3HsPAzPKLC9ZMEC+iQs2Vkes 79Os0nFLZeMaMGAO5W6xiyQ5p94oo0bqa1XbmSV1bNp1HWuNEGIiZKrEUDxfYuDc6f 80C6hJZKsjJkMkBeadlQAlLcjIx1rDV171CKLLTxy/dT5kv4p9UrJlnleyMVG6S/3d 816nX/WLSgZIMYbOwiZVVPlSrobuG38ULJMCSuxndxD0l+HahJaH8vYXuR67A0XT+b 82TEe305AI6A/9MEaRrActBnq6/OviQgBsKAvtTv1FmDbnpZsKeoFuwc3OPdTveQdC 83RA== 84-----END CERTIFICATE-----` 85 86func TestEmbeddedHMAC(t *testing.T) { 87 // protected: {"alg":"HS256", "jwk":{"kty":"oct", "k":"MTEx"}}, aka HMAC key. 88 msg := `{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ","protected":"eyJhbGciOiJIUzI1NiIsICJqd2siOnsia3R5Ijoib2N0IiwgImsiOiJNVEV4In19","signature":"lvo41ZZsuHwQvSh0uJtEXRR3vmuBJ7in6qMoD7p9jyo"}` 89 90 _, err := ParseSigned(msg) 91 if err == nil { 92 t.Error("should not allow parsing JWS with embedded JWK with HMAC key") 93 } 94} 95 96func TestCompactParseJWS(t *testing.T) { 97 // Should parse 98 msg := "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.c2lnbmF0dXJl" 99 _, err := ParseSigned(msg) 100 if err != nil { 101 t.Error("Unable to parse valid message:", err) 102 } 103 104 // Should parse (detached signature missing payload) 105 msg = "eyJhbGciOiJYWVoifQ..c2lnbmF0dXJl" 106 _, err = ParseSigned(msg) 107 if err != nil { 108 t.Error("Unable to parse valid message:", err) 109 } 110 111 // Messages that should fail to parse 112 failures := []string{ 113 // Not enough parts 114 "eyJhbGciOiJYWVoifQ.cGF5bG9hZA", 115 // Invalid signature 116 "eyJhbGciOiJYWVoifQ.cGF5bG9hZA.////", 117 // Invalid payload 118 "eyJhbGciOiJYWVoifQ.////.c2lnbmF0dXJl", 119 // Invalid header 120 "////.eyJhbGciOiJYWVoifQ.c2lnbmF0dXJl", 121 // Invalid header 122 "cGF5bG9hZA.cGF5bG9hZA.c2lnbmF0dXJl", 123 } 124 125 for i := range failures { 126 _, err = ParseSigned(failures[i]) 127 if err == nil { 128 t.Error("Able to parse invalid message") 129 } 130 } 131} 132 133func TestFullParseJWS(t *testing.T) { 134 // Messages that should succeed to parse 135 successes := []string{ 136 "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"},{\"protected\":\"e30\",\"signature\":\"CUJD\"}]}", 137 } 138 139 for i := range successes { 140 _, err := ParseSigned(successes[i]) 141 if err != nil { 142 t.Error("Unble to parse valid message", err, successes[i]) 143 } 144 } 145 146 // Messages that should fail to parse 147 failures := []string{ 148 // Empty 149 "{}", 150 // Invalid JSON 151 "{XX", 152 // Invalid protected header 153 "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", 154 // Invalid protected header 155 "{\"payload\":\"CUJD\",\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}", 156 // Invalid protected header 157 "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"###\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", 158 // Invalid payload 159 "{\"payload\":\"###\",\"signatures\":[{\"protected\":\"CUJD\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"CUJD\"}]}", 160 // Invalid payload 161 "{\"payload\":\"CUJD\",\"signatures\":[{\"protected\":\"e30\",\"header\":{\"kid\":\"XYZ\"},\"signature\":\"###\"}]}", 162 } 163 164 for i := range failures { 165 _, err := ParseSigned(failures[i]) 166 if err == nil { 167 t.Error("Able to parse invalid message", err, failures[i]) 168 } 169 } 170} 171 172func TestRejectUnprotectedJWSNonce(t *testing.T) { 173 // No need to test compact, since that's always protected 174 175 // Flattened JSON 176 input := `{ 177 "header": { "nonce": "should-cause-an-error" }, 178 "payload": "does-not-matter", 179 "signature": "does-not-matter" 180 }` 181 _, err := ParseSigned(input) 182 if err == nil { 183 t.Error("JWS with an unprotected nonce parsed as valid.") 184 } else if err != ErrUnprotectedNonce { 185 t.Errorf("Improper error for unprotected nonce: %v", err) 186 } 187 188 // Full JSON 189 input = `{ 190 "payload": "does-not-matter", 191 "signatures": [{ 192 "header": { "nonce": "should-cause-an-error" }, 193 "signature": "does-not-matter" 194 }] 195 }` 196 _, err = ParseSigned(input) 197 if err == nil { 198 t.Error("JWS with an unprotected nonce parsed as valid.") 199 } else if err != ErrUnprotectedNonce { 200 t.Errorf("Improper error for unprotected nonce: %v", err) 201 } 202} 203 204func TestVerifyFlattenedWithIncludedUnprotectedKey(t *testing.T) { 205 input := `{ 206 "header": { 207 "alg": "RS256", 208 "jwk": { 209 "e": "AQAB", 210 "kty": "RSA", 211 "n": "tSwgy3ORGvc7YJI9B2qqkelZRUC6F1S5NwXFvM4w5-M0TsxbFsH5UH6adigV0jzsDJ5imAechcSoOhAh9POceCbPN1sTNwLpNbOLiQQ7RD5mY_pSUHWXNmS9R4NZ3t2fQAzPeW7jOfF0LKuJRGkekx6tXP1uSnNibgpJULNc4208dgBaCHo3mvaE2HV2GmVl1yxwWX5QZZkGQGjNDZYnjFfa2DKVvFs0QbAk21ROm594kAxlRlMMrvqlf24Eq4ERO0ptzpZgm_3j_e4hGRD39gJS7kAzK-j2cacFQ5Qi2Y6wZI2p-FCq_wiYsfEAIkATPBiLKl_6d_Jfcvs_impcXQ" 212 } 213 }, 214 "payload": "Zm9vCg", 215 "signature": "hRt2eYqBd_MyMRNIh8PEIACoFtmBi7BHTLBaAhpSU6zyDAFdEBaX7us4VB9Vo1afOL03Q8iuoRA0AT4akdV_mQTAQ_jhTcVOAeXPr0tB8b8Q11UPQ0tXJYmU4spAW2SapJIvO50ntUaqU05kZd0qw8-noH1Lja-aNnU-tQII4iYVvlTiRJ5g8_CADsvJqOk6FcHuo2mG643TRnhkAxUtazvHyIHeXMxydMMSrpwUwzMtln4ZJYBNx4QGEq6OhpAD_VSp-w8Lq5HOwGQoNs0bPxH1SGrArt67LFQBfjlVr94E1sn26p4vigXm83nJdNhWAMHHE9iV67xN-r29LT-FjA" 216 }` 217 218 jws, err := ParseSigned(input) 219 if err != nil { 220 t.Fatal("Unable to parse valid message", err) 221 } 222 if len(jws.Signatures) != 1 { 223 t.Error("Too many or too few signatures.") 224 } 225 sig := jws.Signatures[0] 226 if sig.Header.JSONWebKey == nil { 227 t.Error("No JWK in signature header.") 228 } 229 payload, err := jws.Verify(sig.Header.JSONWebKey) 230 if err != nil { 231 t.Errorf("Signature did not validate: %v", err) 232 } 233 if string(payload) != "foo\n" { 234 t.Errorf("Payload was incorrect: '%s' should have been 'foo\\n'", string(payload)) 235 } 236} 237 238// Test verification of a detached signature 239func TestDetachedVerifyJWS(t *testing.T) { 240 rsaPublicKey, err := x509.ParsePKIXPublicKey(fromBase64Bytes(` 241 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO 242 91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ 243 t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd 244 NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6 245 CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G 246 zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`)) 247 if err != nil { 248 t.Fatal(err) 249 } 250 251 sampleMessages := []string{ 252 "eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA", 253 "eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg", 254 } 255 256 for _, msg := range sampleMessages { 257 obj, err := ParseSigned(msg) 258 if err != nil { 259 t.Error("unable to parse message", msg, err) 260 continue 261 } 262 payload := obj.payload 263 obj.payload = nil 264 err = obj.DetachedVerify(payload, rsaPublicKey) 265 if err != nil { 266 t.Error("unable to verify message", msg, err) 267 continue 268 } 269 idx, _, err := obj.DetachedVerifyMulti(payload, rsaPublicKey) 270 if idx != 0 || err != nil { 271 t.Error("unable to verify message", msg, err) 272 continue 273 } 274 } 275} 276 277func TestVerifyFlattenedWithPrivateProtected(t *testing.T) { 278 // The protected field contains a Private Header Parameter name, per 279 // https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4 280 // Base64-decoded, it's '{"nonce":"8HIepUNFZUa-exKTrXVf4g"}' 281 input := `{"header":{"alg":"RS256","jwk":{"kty":"RSA","n":"7ixeydcbxxppzxrBphrW1atUiEZqTpiHDpI-79olav5XxAgWolHmVsJyxzoZXRxmtED8PF9-EICZWBGdSAL9ZTD0hLUCIsPcpdgT_LqNW3Sh2b2caPL2hbMF7vsXvnCGg9varpnHWuYTyRrCLUF9vM7ES-V3VCYTa7LcCSRm56Gg9r19qar43Z9kIKBBxpgt723v2cC4bmLmoAX2s217ou3uCpCXGLOeV_BesG4--Nl3pso1VhCfO85wEWjmW6lbv7Kg4d7Jdkv5DjDZfJ086fkEAYZVYGRpIgAvJBH3d3yKDCrSByUEud1bWuFjQBmMaeYOrVDXO_mbYg5PwUDMhw","e":"AQAB"}},"protected":"eyJub25jZSI6IjhISWVwVU5GWlVhLWV4S1RyWFZmNGcifQ","payload":"eyJjb250YWN0IjpbIm1haWx0bzpmb29AYmFyLmNvbSJdfQ","signature":"AyvVGMgXsQ1zTdXrZxE_gyO63pQgotL1KbI7gv6Wi8I7NRy0iAOkDAkWcTQT9pcCYApJ04lXfEDZfP5i0XgcFUm_6spxi5mFBZU-NemKcvK9dUiAbXvb4hB3GnaZtZiuVnMQUb_ku4DOaFFKbteA6gOYCnED_x7v0kAPHIYrQnvIa-KZ6pTajbV9348zgh9TL7NgGIIsTcMHd-Jatr4z1LQ0ubGa8tS300hoDhVzfoDQaEetYjCo1drR1RmdEN1SIzXdHOHfubjA3ZZRbrF_AJnNKpRRoIwzu1VayOhRmdy1qVSQZq_tENF4VrQFycEL7DhG7JLoXC4T2p1urwMlsw"}` 282 283 jws, err := ParseSigned(input) 284 if err != nil { 285 t.Error("Unable to parse valid message.") 286 } 287 if len(jws.Signatures) != 1 { 288 t.Error("Too many or too few signatures.") 289 } 290 sig := jws.Signatures[0] 291 if sig.Header.JSONWebKey == nil { 292 t.Error("No JWK in signature header.") 293 } 294 payload, err := jws.Verify(sig.Header.JSONWebKey) 295 if err != nil { 296 t.Errorf("Signature did not validate: %v", err) 297 } 298 expected := "{\"contact\":[\"mailto:foo@bar.com\"]}" 299 if string(payload) != expected { 300 t.Errorf("Payload was incorrect: '%s' should have been '%s'", string(payload), expected) 301 } 302} 303 304// Test vectors generated with nimbus-jose-jwt 305func TestSampleNimbusJWSMessagesRSA(t *testing.T) { 306 rsaPublicKey, err := x509.ParsePKIXPublicKey(fromBase64Bytes(` 307 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3aLSGwbeX0ZA2Ha+EvELaIFGzO 308 91+Q15JQc/tdGdCgGW3XAbrh7ZUhDh1XKzbs+UOQxqn3Eq4YOx18IG0WsJSuCaHQIxnDlZ 309 t/GP8WLwjMC0izlJLm2SyfM/EEoNpmTC3w6MQ2dHK7SZ9Zoq+sKijQd+V7CYdr8zHMpDrd 310 NKoEcR0HjmvzzdMoUChhkGH5TaNbZyollULTggepaYUKS8QphqdSDMWiSetKG+g6V87lv6 311 CVYyK1FF6g7Esp5OOj5pNn3/bmF+7V+b7TvK91NCIlURCjE9toRgNoIP4TDnWRn/vvfZ3G 312 zNrtWmlizqz3r5KdvIs71ahWgMUSD4wfazrwIDAQAB`)) 313 if err != nil { 314 panic(err) 315 } 316 317 rsaSampleMessages := []string{ 318 "eyJhbGciOiJSUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.YHX849fvekz6wJGeyqnQhFqyHFcUXNJKj3o2w3ddR46YLlsCopUJrlifRU_ZuTWzpYxt5oC--T2eoqMhlCvltSWrE5_1_EumqiMfAYsZULx9E6Jns7q3w7mttonYFSIh7aR3-yg2HMMfTCgoAY1y_AZ4VjXwHDcZ5gu1oZDYgvZF4uXtCmwT6e5YtR1m8abiWPF8BgoTG_BD3KV6ClLj_QQiNFdfdxAMDw7vKVOKG1T7BFtz6cDs2Q3ILS4To5E2IjcVSSYS8mi77EitCrWmrqbK_G3WCdKeUFGnMnyuKXaCDy_7FLpAZ6Z5RomRr5iskXeJZdZqIKcJV8zl4fpsPA", 319 "eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg", 320 "eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA", 321 "eyJhbGciOiJQUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.UTtxjsv_6x4CdlAmZfAW6Lun3byMjJbcwRp_OlPH2W4MZaZar7aql052mIB_ddK45O9VUz2aphYVRvKPZY8WHmvlTUU30bk0z_cDJRYB9eIJVMOiRCYj0oNkz1iEZqsP0YgngxwuUDv4Q4A6aJ0Bo5E_rZo3AnrVHMHUjPp_ZRRSBFs30tQma1qQ0ApK4Gxk0XYCYAcxIv99e78vldVRaGzjEZmQeAVZx4tGcqZP20vG1L84nlhSGnOuZ0FhR8UjRFLXuob6M7EqtMRoqPgRYw47EI3fYBdeSivAg98E5S8R7R1NJc7ef-l03RvfUSY0S3_zBq_4PlHK6A-2kHb__w", 322 "eyJhbGciOiJSUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.meyfoOTjAAjXHFYiNlU7EEnsYtbeUYeEglK6BL_cxISEr2YAGLr1Gwnn2HnucTnH6YilyRio7ZC1ohy_ZojzmaljPHqpr8kn1iqNFu9nFE2M16ZPgJi38-PGzppcDNliyzOQO-c7L-eA-v8Gfww5uyRaOJdiWg-hUJmeGBIngPIeLtSVmhJtz8oTeqeNdUOqQv7f7VRCuvagLhW1PcEM91VUS-gS0WEUXoXWZ2lp91No0v1O24izgX3__FKiX_16XhrOfAgJ82F61vjbTIQYwhexHPZyYTlXYt_scNRzFGhSKeGFin4zVdFLOXWJqKWdUd5IrDP5Nya3FSoWbWDXAg", 323 "eyJhbGciOiJSUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.rQPz0PDh8KyE2AX6JorgI0MLwv-qi1tcWlz6tuZuWQG1hdrlzq5tR1tQg1evYNc_SDDX87DWTSKXT7JEqhKoFixLfZa13IJrOc7FB8r5ZLx7OwOBC4F--OWrvxMA9Y3MTJjPN3FemQePUo-na2vNUZv-YgkcbuOgbO3hTxwQ7j1JGuqy-YutXOFnccdXvntp3t8zYZ4Mg1It_IyL9pzgGqHIEmMV1pCFGHsDa-wStB4ffmdhrADdYZc0q_SvxUdobyC_XzZCz9ENzGIhgwYxyyrqg7kjqUGoKmCLmoSlUFW7goTk9IC5SXdUyLPuESxOWNfHoRClGav230GYjPFQFA", 324 } 325 326 for _, msg := range rsaSampleMessages { 327 obj, err := ParseSigned(msg) 328 if err != nil { 329 t.Error("unable to parse message", msg, err) 330 continue 331 } 332 payload, err := obj.Verify(rsaPublicKey) 333 if err != nil { 334 t.Error("unable to verify message", msg, err) 335 continue 336 } 337 if string(payload) != "Lorem ipsum dolor sit amet" { 338 t.Error("payload is not what we expected for msg", msg) 339 } 340 } 341} 342 343// Test vectors generated with nimbus-jose-jwt 344func TestSampleNimbusJWSMessagesEC(t *testing.T) { 345 ecPublicKeyP256, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIg62jq6FyL1otEj9Up7S35BUrwGF9TVrAzrrY1rHUKZqYIGEg67u/imjgadVcr7y9Q32I0gB8W8FHqbqt696rA==")) 346 if err != nil { 347 panic(err) 348 } 349 ecPublicKeyP384, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEPXsVlqCtN2oTY+F+hFZm3M0ldYpb7IeeJM5wYmT0k1RaqzBFDhDMNnYK5Q5x+OyssZrAtHgYDFw02AVJhhng/eHRp7mqmL/vI3wbxJtrLKYldIbBA+9fYBQcKeibjlu5")) 350 if err != nil { 351 panic(err) 352 } 353 ecPublicKeyP521, err := x509.ParsePKIXPublicKey(fromBase64Bytes("MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAa2w3MMJ5FWD6tSf68G+Wy5jIhWXOD3IA7pE5IC/myQzo1lWcD8KS57SM6nm4POtPcxyLmDhL7FLuh8DKoIZyvtAAdK8+tOQP7XXRlT2bkvzIuazp05It3TAPu00YzTIpKfDlc19Y1lvf7etrbFqhShD92B+hHmhT4ddrdbPCBDW8hvU=")) 354 if err != nil { 355 panic(err) 356 } 357 358 ecPublicKeys := []interface{}{ecPublicKeyP256, ecPublicKeyP384, ecPublicKeyP521} 359 360 ecSampleMessages := []string{ 361 "eyJhbGciOiJFUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.MEWJVlvGRQyzMEGOYm4rwuiwxrX-6LjnlbaRDAuhwmnBm2Gtn7pRpGXRTMFZUXsSGDz2L1p-Hz1qn8j9bFIBtQ", 362 "eyJhbGciOiJFUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.nbdjPnJPYQtVNNdBIx8-KbFKplTxrz-hnW5UNhYUY7SBkwHK4NZnqc2Lv4DXoA0aWHq9eiypgOh1kmyPWGEmqKAHUx0xdIEkBoHk3ZsbmhOQuq2jL_wcMUG6nTWNhLrB", 363 "eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g", 364 } 365 366 for i, msg := range ecSampleMessages { 367 obj, err := ParseSigned(msg) 368 if err != nil { 369 t.Error("unable to parse message", msg, err) 370 continue 371 } 372 payload, err := obj.Verify(ecPublicKeys[i]) 373 if err != nil { 374 t.Error("unable to verify message", msg, err) 375 continue 376 } 377 if string(payload) != "Lorem ipsum dolor sit amet" { 378 t.Error("payload is not what we expected for msg", msg) 379 } 380 } 381} 382 383// Test vectors generated with nimbus-jose-jwt 384func TestSampleNimbusJWSMessagesHMAC(t *testing.T) { 385 hmacTestKey := fromHexBytes("DF1FA4F36FFA7FC42C81D4B3C033928D") 386 387 hmacSampleMessages := []string{ 388 "eyJhbGciOiJIUzI1NiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM", 389 "eyJhbGciOiJIUzM4NCJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.sBu44lXOJa4Nd10oqOdYH2uz3lxlZ6o32QSGHaoGdPtYTDG5zvSja6N48CXKqdAh", 390 "eyJhbGciOiJIUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.M0yR4tmipsORIix-BitIbxEPGaxPchDfj8UNOpKuhDEfnb7URjGvCKn4nOlyQ1z9mG1FKbwnqR1hOVAWSzAU_w", 391 } 392 393 for _, msg := range hmacSampleMessages { 394 obj, err := ParseSigned(msg) 395 if err != nil { 396 t.Error("unable to parse message", msg, err) 397 continue 398 } 399 payload, err := obj.Verify(hmacTestKey) 400 if err != nil { 401 t.Error("unable to verify message", msg, err) 402 continue 403 } 404 if string(payload) != "Lorem ipsum dolor sit amet" { 405 t.Error("payload is not what we expected for msg", msg) 406 } 407 } 408} 409 410func TestHeaderFieldsCompact(t *testing.T) { 411 msg := "eyJhbGciOiJFUzUxMiJ9.TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ.AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g" 412 413 obj, err := ParseSigned(msg) 414 if err != nil { 415 t.Fatal("unable to parse message", msg, err) 416 } 417 if obj.Signatures[0].Header.Algorithm != "ES512" { 418 t.Error("merged header did not contain expected alg value") 419 } 420 if obj.Signatures[0].Protected.Algorithm != "ES512" { 421 t.Error("protected header did not contain expected alg value") 422 } 423 if obj.Signatures[0].Unprotected.Algorithm != "" { 424 t.Error("unprotected header contained an alg value") 425 } 426} 427 428func TestHeaderFieldsFull(t *testing.T) { 429 msg := `{"payload":"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQ","protected":"eyJhbGciOiJFUzUxMiJ9","header":{"custom":"test"},"signature":"AeYNFC1rwIgQv-5fwd8iRyYzvTaSCYTEICepgu9gRId-IW99kbSVY7yH0MvrQnqI-a0L8zwKWDR35fW5dukPAYRkADp3Y1lzqdShFcEFziUVGo46vqbiSajmKFrjBktJcCsfjKSaLHwxErF-T10YYPCQFHWb2nXJOOI3CZfACYqgO84g"}` 430 431 obj, err := ParseSigned(msg) 432 if err != nil { 433 t.Fatal("unable to parse message", msg, err) 434 } 435 if obj.Signatures[0].Header.Algorithm != "ES512" { 436 t.Error("merged header did not contain expected alg value") 437 } 438 if obj.Signatures[0].Protected.Algorithm != "ES512" { 439 t.Error("protected header did not contain expected alg value") 440 } 441 if obj.Signatures[0].Unprotected.Algorithm != "" { 442 t.Error("unprotected header contained an alg value") 443 } 444 if obj.Signatures[0].Unprotected.ExtraHeaders["custom"] != "test" { 445 t.Error("unprotected header did not contain custom header value") 446 } 447} 448 449// Test vectors generated with nimbus-jose-jwt 450func TestErrorMissingPayloadJWS(t *testing.T) { 451 _, err := (&rawJSONWebSignature{}).sanitized() 452 if err == nil { 453 t.Error("was able to parse message with missing payload") 454 } 455 if !strings.Contains(err.Error(), "missing payload") { 456 t.Errorf("unexpected error message, should contain 'missing payload': %s", err) 457 } 458} 459 460// Test that a null value in the header doesn't panic 461func TestNullHeaderValue(t *testing.T) { 462 msg := `{ 463 "payload": 464 "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGF 465 tcGxlLmNvbS9pc19yb290Ijp0cnVlfQ", 466 "protected":"eyJhbGciOiJFUzI1NiIsIm5vbmNlIjpudWxsfQ", 467 "header": 468 {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}, 469 "signature": 470 "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS 471 lSApmWQxfKTUJqPP3-Kg6NU1Q" 472 }` 473 474 defer func() { 475 if r := recover(); r != nil { 476 t.Errorf("ParseSigned panic'd when parsing a message with a null protected header value") 477 } 478 }() 479 ParseSigned(msg) 480} 481 482// Test for bug: 483// https://github.com/square/go-jose/issues/157 484func TestEmbedJWKBug(t *testing.T) { 485 signerKey := SigningKey{ 486 Key: &JSONWebKey{ 487 Key: rsaTestKey, 488 KeyID: "rsa-test-key", 489 }, 490 Algorithm: RS256, 491 } 492 493 signer, err := NewSigner(signerKey, &SignerOptions{EmbedJWK: true}) 494 if err != nil { 495 t.Fatal(err) 496 } 497 498 signerNoEmbed, err := NewSigner(signerKey, &SignerOptions{EmbedJWK: false}) 499 if err != nil { 500 t.Fatal(err) 501 } 502 503 jws, err := signer.Sign([]byte("Lorem ipsum dolor sit amet")) 504 if err != nil { 505 t.Fatal(err) 506 } 507 508 jwsNoEmbed, err := signerNoEmbed.Sign([]byte("Lorem ipsum dolor sit amet")) 509 if err != nil { 510 t.Fatal(err) 511 } 512 513 // This used to panic with: 514 // json: error calling MarshalJSON for type *jose.JSONWebKey: square/go-jose: unknown key type '%!s(<nil>)' 515 output := jws.FullSerialize() 516 outputNoEmbed := jwsNoEmbed.FullSerialize() 517 518 // Expected output with embed set to true is a JWS with the public JWK embedded, with kid header empty. 519 // Expected output with embed set to false is that we set the kid header for key identification instead. 520 parsed, err := ParseSigned(output) 521 if err != nil { 522 t.Fatal(err) 523 } 524 525 parsedNoEmbed, err := ParseSigned(outputNoEmbed) 526 if err != nil { 527 t.Fatal(err) 528 } 529 530 if parsed.Signatures[0].Header.KeyID != "" { 531 t.Error("expected kid field in protected header to be empty") 532 } 533 if parsed.Signatures[0].Header.JSONWebKey.KeyID != "rsa-test-key" { 534 t.Error("expected rsa-test-key to be kid in embedded JWK in protected header") 535 } 536 if parsedNoEmbed.Signatures[0].Header.KeyID != "rsa-test-key" { 537 t.Error("expected kid field in protected header to be rsa-test-key") 538 } 539 if parsedNoEmbed.Signatures[0].Header.JSONWebKey != nil { 540 t.Error("expected no embedded JWK to be present") 541 } 542} 543 544func TestJWSWithCertificateChain(t *testing.T) { 545 signerKey := SigningKey{ 546 Key: rsaTestKey, 547 Algorithm: RS256, 548 } 549 550 certs := []string{ 551 // CN=TrustedSigner, signed by IntermediateCA 552 "MIIDLDCCAhSgAwIBAgIQNsV1i7m3kXGugqOQuuC7FzANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTAeFw0xODAzMjgxODQzNDlaFw0zODAzMjgxODQzMDJaMBgxFjAUBgNVBAMTDVRydXN0ZWRTaWduZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLpvmOEDRxzQJUKHLkLQSsFDo9eGnolSERa6fz1E1F4wmk6nieHqssPd28C6Vb1sHJFne/j93DXNrx7W9Gy9fQvWa+VNHfGuYAodaS2pyV4VUPWMXI2a+qjxW85orq34XtcHzU+qm+ekR5W06ypW+xewbXJW//P9ulrsv3bDoDFaiggHY/u3p5CRSB9mg+Pbpf6E/k/N85sFJUsRE9hzgwg27Kqhh6p3hP3QnA+0WZRcWhwG0gykoD6layRLCPVcxlTSUdpyStDiK8w2whLJQfixCBGLS3/tB/GKb726bxTQK72OLzIMtOo4ZMtTva7bcA2PRgwfRz7bJg4DXz7oHTAgMBAAGjcTBvMA4GA1UdDwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0OBBYEFCpZEyJGAyK//NsYSSC4xkOqNnh3MB8GA1UdIwQYMBaAFMAYlq86RZzTWxLpYE7KTTM7DHOuMA0GCSqGSIb3DQEBCwUAA4IBAQBSIln6jPFkctPC17le0O+wkCStFOuqUM9cjwPuj4xBQ47RxmC0Pjv52N3TuVH7slmMykITQO/vVqQZguf+N5u4BCh223qWiu1muYBTfBPXCPgJjJ79bUL/dy9QEocOfPiIqTFC6xHKeSUCu6qi5jCPFynOaoVvlNPZEb2MR+QrkKVzg09aDEfk6J+wE6eH9+kNOtwvd/z2a2t2hterURtJEnYt7AQGviEpUf1gbHxCE9f3FW5iJGdgcshrk5ZwUfxvND2x4qFq2fYQRxNBnkO+TSYzwYoAItcGAUvlZFH+rdsq3N+UpRptXRkj5iMq59VlcXFOT675EkkNREgromWn", 553 // CN=IntermediateCA, signed by TrustedCA 554 "MIIEHTCCAgWgAwIBAgIQXzZsEQv0cvSRLJAkS9FmWTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDEwlUcnVzdGVkQ0EwHhcNMTgwMzI4MTg0MzMzWhcNMzgwMzI4MTg0MzAzWjAZMRcwFQYDVQQDEw5JbnRlcm1lZGlhdGVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3aYpH/1yEYf/kHuHWyO3AO4tgwlYYLhCDT2GvaPdaEcqhe/VuYiqx3xY7IRDqsW2rau/OXgW6KzLHdRZHogK07hUj1Lfr7X+Oqbp22IV4ydyiL7jwK9AtVXvDuuv5ET+oRfV82j0uhyk0ueGD9r6C/h+6NTzHBD+3xo6Yuc0VkBfY5zIyhaFqlm1aRYvupDRjC/63uBgAlrGxy2LyiTMVnYMuxoJM5ahDepz3sqjuNWVyPhfGwIezjXuXRdEvlmWX05XLnsTdP4zu4fHq9Z7c3TKWWONM3z64ECAZmGQVfMAcEDX7qP0gZX5PCT+0WcvTgTWE4Q+WIh5AmYyxQ04cCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFMAYlq86RZzTWxLpYE7KTTM7DHOuMB8GA1UdIwQYMBaAFMvTapNJg0cAubXJjcUNWFjuTAIYMA0GCSqGSIb3DQEBCwUAA4ICAQBmYRpQoWEm5g16kwUrpwWrH7OIqqMtUhM1dcskECfki3/hcsV+MQRkGHLIItucYIWqs7oOQIglsyGcohAbnvE1PVtKKojUHC0lfbjgIenDPbvz15QB6A3KLDR82QbQGeGniACy924p66zlfPwHJbkMo5ZaqtNqI//EIa2YCpyyokhFXaSFmPWXXrTOCsEEsFJKsoSCH1KUpTcwACGkkilNseg1edZB6/lBDwybxVuY+dbUlHip3r5tFcP66Co3tKAaEcVY0AsZ/8GKwH+IM2AR6q7jdn9Gp2OX4E1ul9Wy+hW5GHMmfixkgTVwRowuKgkCPEKV2/Xy3k9rlSpnKr2NpYYq0mu6An9HYt8THQ+ewGZHwWufuDFDWuzlu7CxFOjpXLKv8qqVnwSFC91S3HsPAzPKLC9ZMEC+iQs2VkesOs0nFLZeMaMGAO5W6xiyQ5p94oo0bqa1XbmSV1bNp1HWuNEGIiZKrEUDxfYuDc6fC6hJZKsjJkMkBeadlQAlLcjIx1rDV171CKLLTxy/dT5kv4p9UrJlnleyMVG6S/3d6nX/WLSgZIMYbOwiZVVPlSrobuG38ULJMCSuxndxD0l+HahJaH8vYXuR67A0XT+bTEe305AI6A/9MEaRrActBnq6/OviQgBsKAvtTv1FmDbnpZsKeoFuwc3OPdTveQdCRA==", 555 } 556 557 testCases := []struct { 558 // Cert chain to embed in message 559 chain []string 560 // Intermediates & root certificate to verify against 561 intermediates []string 562 root string 563 // Should this test case verify? 564 success bool 565 }{ 566 {certs, nil, trustedCA, true}, 567 {certs, []string{intermediateCA}, trustedCA, true}, 568 {certs[0:1], nil, intermediateCA, true}, 569 {certs[0:1], nil, trustedCA, false}, 570 {[]string{}, nil, trustedCA, false}, 571 } 572 573 for i, testCase := range testCases { 574 signer, err := NewSigner(signerKey, &SignerOptions{ 575 ExtraHeaders: map[HeaderKey]interface{}{HeaderKey("x5c"): testCase.chain}, 576 }) 577 if err != nil { 578 t.Fatal(err) 579 } 580 581 signed, err := signer.Sign([]byte("Lorem ipsum dolor sit amet")) 582 if err != nil { 583 t.Fatal(err) 584 } 585 586 parsed, err := ParseSigned(signed.FullSerialize()) 587 if err != nil { 588 t.Fatal(err) 589 } 590 591 opts := x509.VerifyOptions{ 592 DNSName: "TrustedSigner", 593 Roots: x509.NewCertPool(), 594 } 595 596 ok := opts.Roots.AppendCertsFromPEM([]byte(testCase.root)) 597 if !ok { 598 t.Fatal("failed to parse trusted root certificate") 599 } 600 601 if len(testCase.intermediates) > 0 { 602 opts.Intermediates = x509.NewCertPool() 603 for _, intermediate := range testCase.intermediates { 604 ok := opts.Intermediates.AppendCertsFromPEM([]byte(intermediate)) 605 if !ok { 606 t.Fatal("failed to parse trusted root certificate") 607 } 608 } 609 } 610 611 chains, err := parsed.Signatures[0].Protected.Certificates(opts) 612 if testCase.success && (len(chains) == 0 || err != nil) { 613 t.Fatalf("failed to verify certificate chain for test case %d: %s", i, err) 614 } 615 if !testCase.success && (len(chains) != 0 && err == nil) { 616 t.Fatalf("incorrectly verified certificate chain for test case %d (should fail)", i) 617 } 618 } 619} 620 621func TestDetachedCompactSerialization(t *testing.T) { 622 msg := "eyJhbGciOiJSUzI1NiJ9.JC4wMg.W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM" 623 exp := "eyJhbGciOiJSUzI1NiJ9..W5tc_EUhxexcvLYEEOckyyvdb__M5DQIVpg6Nmk1XGM" 624 625 obj, err := ParseSigned(msg) 626 if err != nil { 627 t.Fatal(err) 628 } 629 630 ser, err := obj.DetachedCompactSerialize() 631 if err != nil { 632 t.Fatal(err) 633 } 634 635 if ser != exp { 636 t.Fatalf("got '%s', expected '%s'", ser, exp) 637 } 638 639 obj, err = ParseDetached(ser, []byte("$.02")) 640 if err != nil { 641 t.Fatal(err) 642 } 643 644 ser, err = obj.CompactSerialize() 645 if err != nil { 646 t.Fatal(err) 647 } 648 649 if ser != msg { 650 t.Fatalf("got '%s', expected '%s'", ser, msg) 651 } 652} 653 654func TestJWSComputeAuthDataBase64(t *testing.T) { 655 jws := JSONWebSignature{} 656 657 _, err := jws.computeAuthData([]byte{0x01}, &Signature{ 658 original: &rawSignatureInfo{ 659 Protected: newBuffer([]byte("{!invalid-json}")), 660 }, 661 }) 662 // Invalid header, should return error 663 assert.NotNil(t, err) 664 665 payload := []byte{0x01} 666 encodedPayload := base64.RawURLEncoding.EncodeToString(payload) 667 668 b64TrueHeader := newBuffer([]byte(`{"alg":"RSA-OAEP","enc":"A256GCM","b64":true}`)) 669 b64FalseHeader := newBuffer([]byte(`{"alg":"RSA-OAEP","enc":"A256GCM","b64":false}`)) 670 671 data, err := jws.computeAuthData(payload, &Signature{ 672 original: &rawSignatureInfo{ 673 Protected: b64TrueHeader, 674 }, 675 }) 676 assert.Nil(t, err) 677 // Payload should be b64 encoded 678 assert.Len(t, data, len(b64TrueHeader.base64())+len(encodedPayload)+1) 679 680 data, err = jws.computeAuthData(payload, &Signature{ 681 original: &rawSignatureInfo{ 682 Protected: b64FalseHeader, 683 }, 684 }) 685 assert.Nil(t, err) 686 // Payload should *not* be b64 encoded 687 assert.Len(t, data, len(b64FalseHeader.base64())+len(payload)+1) 688} 689