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 "bytes" 21 "crypto" 22 "crypto/ecdsa" 23 "crypto/elliptic" 24 "crypto/rsa" 25 "crypto/sha1" 26 "crypto/sha256" 27 "crypto/x509" 28 "encoding/hex" 29 "math/big" 30 "reflect" 31 "strings" 32 "testing" 33 34 "github.com/google/go-cmp/cmp" 35 "github.com/stretchr/testify/assert" 36 "github.com/stretchr/testify/require" 37 38 "golang.org/x/crypto/ed25519" 39 40 "gopkg.in/square/go-jose.v2/json" 41) 42 43// Test chain of two X.509 certificates 44var testCertificates, _ = x509.ParseCertificates(fromBase64Bytes(` 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`)) 84 85func TestCurveSize(t *testing.T) { 86 size256 := curveSize(elliptic.P256()) 87 size384 := curveSize(elliptic.P384()) 88 size521 := curveSize(elliptic.P521()) 89 if size256 != 32 { 90 t.Error("P-256 have 32 bytes") 91 } 92 if size384 != 48 { 93 t.Error("P-384 have 48 bytes") 94 } 95 if size521 != 66 { 96 t.Error("P-521 have 66 bytes") 97 } 98} 99 100func TestRoundtripRsaPrivate(t *testing.T) { 101 jwk, err := fromRsaPrivateKey(rsaTestKey) 102 if err != nil { 103 t.Error("problem constructing JWK from rsa key", err) 104 } 105 106 rsa2, err := jwk.rsaPrivateKey() 107 if err != nil { 108 t.Error("problem converting RSA private -> JWK", err) 109 } 110 111 if rsa2.N.Cmp(rsaTestKey.N) != 0 { 112 t.Error("RSA private N mismatch") 113 } 114 if rsa2.E != rsaTestKey.E { 115 t.Error("RSA private E mismatch") 116 } 117 if rsa2.D.Cmp(rsaTestKey.D) != 0 { 118 t.Error("RSA private D mismatch") 119 } 120 if len(rsa2.Primes) != 2 { 121 t.Error("RSA private roundtrip expected two primes") 122 } 123 if rsa2.Primes[0].Cmp(rsaTestKey.Primes[0]) != 0 { 124 t.Error("RSA private P mismatch") 125 } 126 if rsa2.Primes[1].Cmp(rsaTestKey.Primes[1]) != 0 { 127 t.Error("RSA private Q mismatch") 128 } 129} 130 131func TestRoundtripRsaPrivatePrecomputed(t *testing.T) { 132 // Isolate a shallow copy of the rsaTestKey to avoid polluting it with Precompute 133 localKey := &(*rsaTestKey) 134 localKey.Precompute() 135 136 jwk, err := fromRsaPrivateKey(localKey) 137 if err != nil { 138 t.Error("problem constructing JWK from rsa key", err) 139 } 140 141 rsa2, err := jwk.rsaPrivateKey() 142 if err != nil { 143 t.Error("problem converting RSA private -> JWK", err) 144 } 145 146 if rsa2.Precomputed.Dp == nil { 147 t.Error("RSA private Dp nil") 148 } 149 if rsa2.Precomputed.Dq == nil { 150 t.Error("RSA private Dq nil") 151 } 152 if rsa2.Precomputed.Qinv == nil { 153 t.Error("RSA private Qinv nil") 154 } 155 156 if rsa2.Precomputed.Dp.Cmp(localKey.Precomputed.Dp) != 0 { 157 t.Error("RSA private Dp mismatch") 158 } 159 if rsa2.Precomputed.Dq.Cmp(localKey.Precomputed.Dq) != 0 { 160 t.Error("RSA private Dq mismatch") 161 } 162 if rsa2.Precomputed.Qinv.Cmp(localKey.Precomputed.Qinv) != 0 { 163 t.Error("RSA private Qinv mismatch") 164 } 165} 166 167func TestRsaPrivateInsufficientPrimes(t *testing.T) { 168 brokenRsaPrivateKey := rsa.PrivateKey{ 169 PublicKey: rsa.PublicKey{ 170 N: rsaTestKey.N, 171 E: rsaTestKey.E, 172 }, 173 D: rsaTestKey.D, 174 Primes: []*big.Int{rsaTestKey.Primes[0]}, 175 } 176 177 _, err := fromRsaPrivateKey(&brokenRsaPrivateKey) 178 if err != ErrUnsupportedKeyType { 179 t.Error("expected unsupported key type error, got", err) 180 } 181} 182 183func TestRsaPrivateExcessPrimes(t *testing.T) { 184 brokenRsaPrivateKey := rsa.PrivateKey{ 185 PublicKey: rsa.PublicKey{ 186 N: rsaTestKey.N, 187 E: rsaTestKey.E, 188 }, 189 D: rsaTestKey.D, 190 Primes: []*big.Int{ 191 rsaTestKey.Primes[0], 192 rsaTestKey.Primes[1], 193 big.NewInt(3), 194 }, 195 } 196 197 _, err := fromRsaPrivateKey(&brokenRsaPrivateKey) 198 if err != ErrUnsupportedKeyType { 199 t.Error("expected unsupported key type error, got", err) 200 } 201} 202 203func TestRoundtripEcPublic(t *testing.T) { 204 for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} { 205 jwk, err := fromEcPublicKey(&ecTestKey.PublicKey) 206 207 ec2, err := jwk.ecPublicKey() 208 if err != nil { 209 t.Error("problem converting ECDSA private -> JWK", i, err) 210 } 211 212 if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) { 213 t.Error("ECDSA private curve mismatch", i) 214 } 215 if ec2.X.Cmp(ecTestKey.X) != 0 { 216 t.Error("ECDSA X mismatch", i) 217 } 218 if ec2.Y.Cmp(ecTestKey.Y) != 0 { 219 t.Error("ECDSA Y mismatch", i) 220 } 221 } 222} 223 224func TestRoundtripEcPrivate(t *testing.T) { 225 for i, ecTestKey := range []*ecdsa.PrivateKey{ecTestKey256, ecTestKey384, ecTestKey521} { 226 jwk, err := fromEcPrivateKey(ecTestKey) 227 228 ec2, err := jwk.ecPrivateKey() 229 if err != nil { 230 t.Fatalf("problem converting ECDSA private -> JWK for %#v: %s", ecTestKey, err) 231 } 232 233 if !reflect.DeepEqual(ec2.Curve, ecTestKey.Curve) { 234 t.Error("ECDSA private curve mismatch", i) 235 } 236 if ec2.X.Cmp(ecTestKey.X) != 0 { 237 t.Error("ECDSA X mismatch", i) 238 } 239 if ec2.Y.Cmp(ecTestKey.Y) != 0 { 240 t.Error("ECDSA Y mismatch", i) 241 } 242 if ec2.D.Cmp(ecTestKey.D) != 0 { 243 t.Error("ECDSA D mismatch", i) 244 } 245 } 246} 247 248func TestRoundtripX509(t *testing.T) { 249 x5tSHA1 := sha1.Sum(testCertificates[0].Raw) 250 x5tSHA256 := sha256.Sum256(testCertificates[0].Raw) 251 252 cases := []struct { 253 name string 254 jwk JSONWebKey 255 }{ 256 { 257 name: "all fields", 258 jwk: JSONWebKey{ 259 Key: testCertificates[0].PublicKey, 260 KeyID: "bar", 261 Algorithm: "foo", 262 Certificates: testCertificates, 263 CertificateThumbprintSHA1: x5tSHA1[:], 264 CertificateThumbprintSHA256: x5tSHA256[:], 265 }, 266 }, 267 { 268 name: "no optional x5ts", 269 jwk: JSONWebKey{ 270 Key: testCertificates[0].PublicKey, 271 KeyID: "bar", 272 Algorithm: "foo", 273 Certificates: testCertificates, 274 }, 275 }, 276 { 277 name: "no x5t", 278 jwk: JSONWebKey{ 279 Key: testCertificates[0].PublicKey, 280 KeyID: "bar", 281 Algorithm: "foo", 282 Certificates: testCertificates, 283 CertificateThumbprintSHA256: x5tSHA256[:], 284 }, 285 }, 286 { 287 name: "no x5t#S256", 288 jwk: JSONWebKey{ 289 Key: testCertificates[0].PublicKey, 290 KeyID: "bar", 291 Algorithm: "foo", 292 Certificates: testCertificates, 293 CertificateThumbprintSHA1: x5tSHA1[:], 294 }, 295 }, 296 } 297 298 for _, c := range cases { 299 t.Run(c.name, func(t *testing.T) { 300 jsonbar, err := c.jwk.MarshalJSON() 301 require.NoError(t, err) 302 303 var jwk2 JSONWebKey 304 err = jwk2.UnmarshalJSON(jsonbar) 305 require.NoError(t, err) 306 307 if !reflect.DeepEqual(testCertificates, jwk2.Certificates) { 308 t.Error("Certificates not equal", c.jwk.Certificates, jwk2.Certificates) 309 } 310 311 jsonbar2, err := jwk2.MarshalJSON() 312 require.NoError(t, err) 313 314 require.Empty(t, cmp.Diff(jsonbar, jsonbar2)) 315 if !bytes.Equal(jsonbar, jsonbar2) { 316 t.Error("roundtrip should not lose information") 317 } 318 }) 319 } 320} 321 322func TestRoundtripX509Hex(t *testing.T) { 323 var hexJWK = `{ 324 "kty":"RSA", 325 "kid":"bar", 326 "alg":"foo", 327 "n":"u7LUr30Mhrh8N79-H4rKiHQ123q6xaBZPYbf1nV4GM19rizSnbEfyebG1kpfCv-XY6c499XiM6lOvcPL-0goTOcfW6Lg7AAR895GbnMeXEmnxICaI8rAZHK6t1WPmiWp82y_qhK2F_pYUaT3GSuiTFiMGq_GNwdpWuMlsInnnMNv1nxFbxtDPwzmCp0fEBxbH5d1EtXZwTPOHMyj8rfa-NIA5Nl4h_5RrbOWveKwBr26_CDAratJgOWh9xcd5g0ot_uDGcMoAgB6xeTuYklfaxCPptvu49kvoxw1J71fp6nKW_ZuhDRAp2F_BQ9inKpTo05sPLJg8tPTdjaeouOuJQ", 328 "e":"AQAB", 329 "x5c":[ 330 "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=", 331 "MIIDUzCCAjugAwIBAgIJAKg+LQlirffwMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1yb290MB4XDTE2MDYxMDIyMTQxMVoXDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOEoSiNjMQ8/zUFcQW89LWw+UeTXKGwNDSpGjyi8jBKZ1lWPbnMmrjI6DZ9ReevHHzqBdKZt+9NFPFEz7djDMRByIuJhRvzhfFBflaIdSeNk2+NpUaFuUUUd6IIePu0AdRveJ8ZGHXRwCeEDIVCZS4oBYPHOhX/zMWDg8vSO4pSxTjGc7I8fHxaUSkVzUBbeO9T/1eFk0m2uxs3UziUck2X/8YqRd+p/EaBED78nXvKRALAguKAzqxIgk3ccPK0SVQFNFq+eV1/qo8coueQuqMpCAvwVkfpVKhneyC2NlMrfzlcZZbfG/irlSjQn5+ExZX4Isy1pCUbOiVfSrsCdtAgMBAAGjJjAkMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4IBAQCLEJU65vTU+oLbNHLOCR6fALrbjK7xsi6SFDpSXBMm74MWsy3myDBmXpOcN8hCYgsgivUXTQz9ynXP/pzOj4b83zzlaOfPtLTAmMhKWVV4Q85mrDQz+HzG4lKXM78eTsD8PyrocA/tSE7mVEJ0Jal4E2KI/Z9/fqpYFLB6LFlx5n83ehXM/egA0l4OeCC9nBKCeNUN3sIQO85lljyzAJdtWnsdoWogJs6qjcV8n2U5xjZxN5ZFdclYLjq6g2cjEXXMQxb8b7ZhHjLWFdjHP85UvXHK3DpK3JmUg8bYS7t1DJffDQNjawhlsMycKZN+r0ND0Um4m7AjGqxbKT/M2yKF" 332 ], 333 "x5t": "MDYxMjU0ZmRmNzIwZjJjMGU0YmQzZjMzMzlhMmZlNTM1MGExNWRlMQ", 334 "x5t#S256": "MjAzMjRhNGI5MmYxMjI2OGVmOWFlMDI1ZmQ1Yzc5ZDE1OGZmNzQ1NzQwMDkyMTk2ZTgzNTNjMDAzMTUxNzUxMQ" 335}` 336 337 // json output 338 var output = `{ 339 "kty":"RSA", 340 "kid":"bar", 341 "alg":"foo", 342 "n":"u7LUr30Mhrh8N79-H4rKiHQ123q6xaBZPYbf1nV4GM19rizSnbEfyebG1kpfCv-XY6c499XiM6lOvcPL-0goTOcfW6Lg7AAR895GbnMeXEmnxICaI8rAZHK6t1WPmiWp82y_qhK2F_pYUaT3GSuiTFiMGq_GNwdpWuMlsInnnMNv1nxFbxtDPwzmCp0fEBxbH5d1EtXZwTPOHMyj8rfa-NIA5Nl4h_5RrbOWveKwBr26_CDAratJgOWh9xcd5g0ot_uDGcMoAgB6xeTuYklfaxCPptvu49kvoxw1J71fp6nKW_ZuhDRAp2F_BQ9inKpTo05sPLJg8tPTdjaeouOuJQ", 343 "e":"AQAB", 344 "x5c":[ 345 "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=", 346 "MIIDUzCCAjugAwIBAgIJAKg+LQlirffwMA0GCSqGSIb3DQEBCwUAMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UEChMHY2VydGlnbzEQMA4GA1UECxMHZXhhbXBsZTEVMBMGA1UEAxMMZXhhbXBsZS1yb290MB4XDTE2MDYxMDIyMTQxMVoXDTIzMDQxNTIyMTQxMVowVTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQKEwdjZXJ0aWdvMRAwDgYDVQQLEwdleGFtcGxlMRUwEwYDVQQDEwxleGFtcGxlLXJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKOEoSiNjMQ8/zUFcQW89LWw+UeTXKGwNDSpGjyi8jBKZ1lWPbnMmrjI6DZ9ReevHHzqBdKZt+9NFPFEz7djDMRByIuJhRvzhfFBflaIdSeNk2+NpUaFuUUUd6IIePu0AdRveJ8ZGHXRwCeEDIVCZS4oBYPHOhX/zMWDg8vSO4pSxTjGc7I8fHxaUSkVzUBbeO9T/1eFk0m2uxs3UziUck2X/8YqRd+p/EaBED78nXvKRALAguKAzqxIgk3ccPK0SVQFNFq+eV1/qo8coueQuqMpCAvwVkfpVKhneyC2NlMrfzlcZZbfG/irlSjQn5+ExZX4Isy1pCUbOiVfSrsCdtAgMBAAGjJjAkMA4GA1UdDwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMA0GCSqGSIb3DQEBCwUAA4IBAQCLEJU65vTU+oLbNHLOCR6fALrbjK7xsi6SFDpSXBMm74MWsy3myDBmXpOcN8hCYgsgivUXTQz9ynXP/pzOj4b83zzlaOfPtLTAmMhKWVV4Q85mrDQz+HzG4lKXM78eTsD8PyrocA/tSE7mVEJ0Jal4E2KI/Z9/fqpYFLB6LFlx5n83ehXM/egA0l4OeCC9nBKCeNUN3sIQO85lljyzAJdtWnsdoWogJs6qjcV8n2U5xjZxN5ZFdclYLjq6g2cjEXXMQxb8b7ZhHjLWFdjHP85UvXHK3DpK3JmUg8bYS7t1DJffDQNjawhlsMycKZN+r0ND0Um4m7AjGqxbKT/M2yKF" 347 ], 348 "x5t":"BhJU_fcg8sDkvT8zOaL-U1ChXeE", 349 "x5t#S256":"IDJKS5LxImjvmuAl_Vx50Vj_dFdACSGW6DU8ADFRdRE" 350 }` 351 352 var jwk2 JSONWebKey 353 err := jwk2.UnmarshalJSON([]byte(hexJWK)) 354 require.NoError(t, err) 355 356 js, err := jwk2.MarshalJSON() 357 require.NoError(t, err) 358 359 var j1, j2 map[string]interface{} 360 require.NoError(t, json.Unmarshal(js, &j1)) 361 require.NoError(t, json.Unmarshal([]byte(output), &j2)) 362 require.Empty(t, cmp.Diff(j1, j2)) 363} 364 365func TestInvalidThumbprintsX509(t *testing.T) { 366 // Too short 367 jwk := JSONWebKey{ 368 Key: rsaTestKey, 369 KeyID: "bar", 370 Algorithm: "foo", 371 Certificates: testCertificates, 372 CertificateThumbprintSHA1: []byte{0x01}, // invalid length 373 CertificateThumbprintSHA256: []byte{0x02}, // invalid length 374 } 375 376 _, err := jwk.MarshalJSON() 377 if err == nil { 378 t.Error("should not marshal JWK with too short thumbprints") 379 } 380 381 // Mismatched (leaf has different sum) 382 sha1sum := sha1.Sum(nil) 383 jwk.CertificateThumbprintSHA1 = sha1sum[:] 384 sha256sum := sha256.Sum256(nil) 385 jwk.CertificateThumbprintSHA256 = sha256sum[:] 386 387 _, err = jwk.MarshalJSON() 388 if err == nil { 389 t.Error("should not marshal JWK with mismatched thumbprints") 390 } 391 392 // Too short 393 shortThumbprints := []byte(`{ 394 "kty": "RSA", 395 "kid": "bar", 396 "alg": "foo", 397 "n": "wN3v274Fr7grvZdXirEGkHlhYKh72gP-46MxQilMgANi6EaX2m0mYiMC60X1UmOhQNoVW0ItthMME-CGh7haA_Jeou_L6-EVOz-7lGu_J06VRl-mgkQZO0sYSkRY8Rsu7TW-pgnWWwZjSdgxN2gq7DvjjC4RLroV94Lgrb2Qwx6J5bZNOfj3pHmEWZ_eRFEH73Ct5RxwUKtNKx_XAmodZn9oFXBlN7F2Js-4DO-8UgbS7ALkTmhDzClT1GPfdLMusmXw4BXyV72vBOWrbUTdwk4pG8ahPQ0cGQ1ubaAmro_k3Bxg06voWPhXx7ALrpzmZCzr6YY-c5Y5rku4qXh-HQ", 398 "e": "AQAB", 399 "d": "RRM3xMvZ3YVopQ5_G_0rDLNsXOH6-apUr9LS4Y9JBtAvrGEcIe7VwHApq3ny0v870a5J19Vr6boIqVXQ2Or90kwL-O9JacHDiOTamd29KKbMb9fyGtWo88OBf5fbAv9pXyvQjEcZrqArD1eOyPlV5iXM6XfWT5X2KB-HuLIcFsVJSEb0yw943dEhZKkv2fySlVtKEhji2CfkMWP438G8auxwFPNIXmuvA1xvcAepOiI9I1Wy17txLOQg8MYBl98F7mxPUMpL3jm8-CSuxpknucFuFIrqIsbGmukSNp14APcu7bN0cJW5uVW-XtGbuioJkPjU2YJgfhIeMI8kuny5QQ", 400 "p": "yRNzbsreZK9MqJgbVsbl7SX2MyyJW_JnFKGdyrXzMqCtzRS7XJ9L3SwdDG_ERgSCkT9PP2dax9fS7RghHNJIU_NlPnJqzBPvPyzbjho7hcGYGDU2UO8E3SBP1WKm1SnlYhm1uHwrHudAO0D5jhVYQfRwem-zX3QibrsxEyxSELM", 401 "q": "9Yx0zzrhSxdE8ubryZfOyOw6cSbUdyCMgY3IFmEdb_UDZOQNMDCFj2RS1g6yFGKuaFqnL9ZRnmprikf20w-mu-LtLU_l0eK4U7pbAoB--pxFP_O6Yk3ZBu_YJ3FpimyX1v4OVZT-JOU_caKiTrPnlw3P7KbEc9iu_bOc8dE-_e8", 402 "dp": "GwiFbXDS44B58vS4QDtvcCm5Zvnm4bi-SRTNbRJ3Rug5Vagi5Hn6LhsfMKvaHHvAvhxf4CtaFiIbFos28HQJC1he1T12xEct1DWIsxstw3bapu6IhesMoVoVwZ-IxIHkeALy3oG7HmWCyjSbGJIgEoX1lVBtMjkf4_lAyM4dnmc", 403 "dq": "XYtXyMbOo3PG8Z6lfxRVU9gi346CbKu6u3RPIK94rnkyBNKYb55ck2cN47yPfRKnDNxUSvYj--zg8To_PuL8iyGFZ7jDffUYcdVR7J8VQNYdz6JDhEXSA0GGIGilY3XBVsdMoK_1Lgsj41-o48DH3pUFfEuAFf4blE1D4h_sFoM", 404 "qi": "CN3q5TMZO06DlK4onrJ687bPp2GE-fynd7ADPdf1ek3cXbeaFApVNp4w-i8zr7IVugQohn01i_jnkymw4UN-acoIzX2sGYhgDm6le1jyfv28bQ_Z5hT0rFNlPfGyK0kkPYUTxZ4ZCKpCYJT9C1nH58Yu4xFk4VR1zhULegjCCd4", 405 "x5c": [ 406 "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=" 407 ], 408 "x5t": "BhJU_fcg8sDkvT8zOaL-U1C", 409 "x5t#S256": "IDJKS5LxImjvmuAl_Vx50Vj_dFdACSGW6DU8ADF" 410}`) 411 412 // Mismatched (leaf has different sum) 413 mismatchedThumbprints := []byte(`{ 414 "kty": "RSA", 415 "kid": "bar", 416 "alg": "foo", 417 "n": "wN3v274Fr7grvZdXirEGkHlhYKh72gP-46MxQilMgANi6EaX2m0mYiMC60X1UmOhQNoVW0ItthMME-CGh7haA_Jeou_L6-EVOz-7lGu_J06VRl-mgkQZO0sYSkRY8Rsu7TW-pgnWWwZjSdgxN2gq7DvjjC4RLroV94Lgrb2Qwx6J5bZNOfj3pHmEWZ_eRFEH73Ct5RxwUKtNKx_XAmodZn9oFXBlN7F2Js-4DO-8UgbS7ALkTmhDzClT1GPfdLMusmXw4BXyV72vBOWrbUTdwk4pG8ahPQ0cGQ1ubaAmro_k3Bxg06voWPhXx7ALrpzmZCzr6YY-c5Y5rku4qXh-HQ", 418 "e": "AQAB", 419 "d": "RRM3xMvZ3YVopQ5_G_0rDLNsXOH6-apUr9LS4Y9JBtAvrGEcIe7VwHApq3ny0v870a5J19Vr6boIqVXQ2Or90kwL-O9JacHDiOTamd29KKbMb9fyGtWo88OBf5fbAv9pXyvQjEcZrqArD1eOyPlV5iXM6XfWT5X2KB-HuLIcFsVJSEb0yw943dEhZKkv2fySlVtKEhji2CfkMWP438G8auxwFPNIXmuvA1xvcAepOiI9I1Wy17txLOQg8MYBl98F7mxPUMpL3jm8-CSuxpknucFuFIrqIsbGmukSNp14APcu7bN0cJW5uVW-XtGbuioJkPjU2YJgfhIeMI8kuny5QQ", 420 "p": "yRNzbsreZK9MqJgbVsbl7SX2MyyJW_JnFKGdyrXzMqCtzRS7XJ9L3SwdDG_ERgSCkT9PP2dax9fS7RghHNJIU_NlPnJqzBPvPyzbjho7hcGYGDU2UO8E3SBP1WKm1SnlYhm1uHwrHudAO0D5jhVYQfRwem-zX3QibrsxEyxSELM", 421 "q": "9Yx0zzrhSxdE8ubryZfOyOw6cSbUdyCMgY3IFmEdb_UDZOQNMDCFj2RS1g6yFGKuaFqnL9ZRnmprikf20w-mu-LtLU_l0eK4U7pbAoB--pxFP_O6Yk3ZBu_YJ3FpimyX1v4OVZT-JOU_caKiTrPnlw3P7KbEc9iu_bOc8dE-_e8", 422 "dp": "GwiFbXDS44B58vS4QDtvcCm5Zvnm4bi-SRTNbRJ3Rug5Vagi5Hn6LhsfMKvaHHvAvhxf4CtaFiIbFos28HQJC1he1T12xEct1DWIsxstw3bapu6IhesMoVoVwZ-IxIHkeALy3oG7HmWCyjSbGJIgEoX1lVBtMjkf4_lAyM4dnmc", 423 "dq": "XYtXyMbOo3PG8Z6lfxRVU9gi346CbKu6u3RPIK94rnkyBNKYb55ck2cN47yPfRKnDNxUSvYj--zg8To_PuL8iyGFZ7jDffUYcdVR7J8VQNYdz6JDhEXSA0GGIGilY3XBVsdMoK_1Lgsj41-o48DH3pUFfEuAFf4blE1D4h_sFoM", 424 "qi": "CN3q5TMZO06DlK4onrJ687bPp2GE-fynd7ADPdf1ek3cXbeaFApVNp4w-i8zr7IVugQohn01i_jnkymw4UN-acoIzX2sGYhgDm6le1jyfv28bQ_Z5hT0rFNlPfGyK0kkPYUTxZ4ZCKpCYJT9C1nH58Yu4xFk4VR1zhULegjCCd4", 425 "x5c": [ 426 "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=" 427 ], 428 "x5t": "BhJU_fcg8sDkvT8zOaL-U1ChXeX", 429 "x5t#S256": "IDJKS5LxImjvmuAl_Vx50Vj_dFdACSGW6DU8ADFRdRX" 430}`) 431 432 var jwk2 JSONWebKey 433 err = jwk2.UnmarshalJSON(mismatchedThumbprints) 434 if err == nil { 435 t.Error("should not unmarshal JWK with mismatched thumbprints") 436 } 437 438 err = jwk2.UnmarshalJSON(shortThumbprints) 439 if err == nil { 440 t.Error("should not unmarshal JWK with too short thumbprints") 441 } 442} 443 444func TestKeyMismatchX509(t *testing.T) { 445 x5tSHA1 := sha1.Sum(testCertificates[0].Raw) 446 x5tSHA256 := sha256.Sum256(testCertificates[0].Raw) 447 448 jwk := JSONWebKey{ 449 KeyID: "bar", 450 Algorithm: "foo", 451 Certificates: testCertificates, 452 CertificateThumbprintSHA1: x5tSHA1[:], 453 CertificateThumbprintSHA256: x5tSHA256[:], 454 } 455 456 for _, key := range []interface{}{ 457 // None of these keys should match what's in the cert, so parsing should always fail. 458 ecTestKey256, 459 ecTestKey256.Public(), 460 ecTestKey384, 461 ecTestKey384.Public(), 462 ecTestKey521, 463 ecTestKey521.Public(), 464 rsaTestKey, 465 rsaTestKey.Public(), 466 ed25519PrivateKey, 467 ed25519PrivateKey.Public(), 468 } { 469 jwk.Key = key 470 raw, _ := jwk.MarshalJSON() 471 472 var jwk2 JSONWebKey 473 err := jwk2.UnmarshalJSON(raw) 474 if err == nil { 475 t.Error("should not unmarshal JWK with key/cert mismatch") 476 } 477 } 478} 479 480func TestMarshalUnmarshal(t *testing.T) { 481 kid := "DEADBEEF" 482 483 for i, key := range []interface{}{ 484 ecTestKey256, 485 ecTestKey256.Public(), 486 ecTestKey384, 487 ecTestKey384.Public(), 488 ecTestKey521, 489 ecTestKey521.Public(), 490 rsaTestKey, 491 rsaTestKey.Public(), 492 ed25519PrivateKey, 493 ed25519PrivateKey.Public(), 494 } { 495 for _, use := range []string{"", "sig", "enc"} { 496 jwk := JSONWebKey{Key: key, KeyID: kid, Algorithm: "foo"} 497 if use != "" { 498 jwk.Use = use 499 } 500 501 jsonbar, err := jwk.MarshalJSON() 502 if err != nil { 503 t.Error("problem marshaling", i, err) 504 } 505 506 var jwk2 JSONWebKey 507 err = jwk2.UnmarshalJSON(jsonbar) 508 if err != nil { 509 t.Fatal("problem unmarshalling", i, err) 510 } 511 512 jsonbar2, err := jwk2.MarshalJSON() 513 if err != nil { 514 t.Fatal("problem marshaling", i, err) 515 } 516 517 if !bytes.Equal(jsonbar, jsonbar2) { 518 t.Error("roundtrip should not lose information", i) 519 } 520 if jwk2.KeyID != kid { 521 t.Error("kid did not roundtrip JSON marshalling", i) 522 } 523 524 if jwk2.Algorithm != "foo" { 525 t.Error("alg did not roundtrip JSON marshalling", i) 526 } 527 528 if jwk2.Use != use { 529 t.Error("use did not roundtrip JSON marshalling", i) 530 } 531 } 532 } 533} 534 535func TestMarshalNonPointer(t *testing.T) { 536 type EmbedsKey struct { 537 Key JSONWebKey 538 } 539 540 keyJSON := []byte(`{ 541 "e": "AQAB", 542 "kty": "RSA", 543 "n": "vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw" 544 }`) 545 var parsedKey JSONWebKey 546 err := json.Unmarshal(keyJSON, &parsedKey) 547 if err != nil { 548 t.Errorf("Error unmarshalling key: %v", err) 549 return 550 } 551 ek := EmbedsKey{ 552 Key: parsedKey, 553 } 554 out, err := json.Marshal(ek) 555 if err != nil { 556 t.Errorf("Error marshalling JSON: %v", err) 557 return 558 } 559 expected := "{\"Key\":{\"kty\":\"RSA\",\"n\":\"vd7rZIoTLEe-z1_8G1FcXSw9CQFEJgV4g9V277sER7yx5Qjz_Pkf2YVth6wwwFJEmzc0hoKY-MMYFNwBE4hQHw\",\"e\":\"AQAB\"}}" 560 if string(out) != expected { 561 t.Error("Failed to marshal embedded non-pointer JWK properly:", string(out)) 562 } 563} 564 565func TestMarshalUnmarshalInvalid(t *testing.T) { 566 // Make an invalid curve coordinate by creating a byte array that is one 567 // byte too large, and setting the first byte to 1 (otherwise it's just zero). 568 invalidCoord := make([]byte, curveSize(ecTestKey256.Curve)+1) 569 invalidCoord[0] = 1 570 571 keys := []interface{}{ 572 // Empty keys 573 &rsa.PrivateKey{}, 574 &ecdsa.PrivateKey{}, 575 // Invalid keys 576 &ecdsa.PrivateKey{ 577 PublicKey: ecdsa.PublicKey{ 578 // Missing values in pub key 579 Curve: elliptic.P256(), 580 }, 581 }, 582 &ecdsa.PrivateKey{ 583 PublicKey: ecdsa.PublicKey{ 584 // Invalid curve 585 Curve: nil, 586 X: ecTestKey256.X, 587 Y: ecTestKey256.Y, 588 }, 589 }, 590 &ecdsa.PrivateKey{ 591 // Valid pub key, but missing priv key values 592 PublicKey: ecTestKey256.PublicKey, 593 }, 594 &ecdsa.PrivateKey{ 595 // Invalid pub key, values too large 596 PublicKey: ecdsa.PublicKey{ 597 Curve: ecTestKey256.Curve, 598 X: big.NewInt(0).SetBytes(invalidCoord), 599 Y: big.NewInt(0).SetBytes(invalidCoord), 600 }, 601 D: ecTestKey256.D, 602 }, 603 nil, 604 } 605 606 for i, key := range keys { 607 jwk := JSONWebKey{Key: key} 608 _, err := jwk.MarshalJSON() 609 if err == nil { 610 t.Error("managed to serialize invalid key", i) 611 } 612 } 613} 614 615func TestWebKeyVectorsInvalid(t *testing.T) { 616 keys := []string{ 617 // Invalid JSON 618 "{X", 619 // Empty key 620 "{}", 621 // Invalid RSA keys 622 `{"kty":"RSA"}`, 623 `{"kty":"RSA","e":""}`, 624 `{"kty":"RSA","e":"XXXX"}`, 625 `{"kty":"RSA","d":"XXXX"}`, 626 // Invalid EC keys 627 `{"kty":"EC","crv":"ABC"}`, 628 `{"kty":"EC","crv":"P-256"}`, 629 `{"kty":"EC","crv":"P-256","d":"XXX"}`, 630 `{"kty":"EC","crv":"ABC","d":"dGVzdA","x":"dGVzdA"}`, 631 `{"kty":"EC","crv":"P-256","d":"dGVzdA","x":"dGVzdA"}`, 632 } 633 634 for _, key := range keys { 635 var jwk2 JSONWebKey 636 err := jwk2.UnmarshalJSON([]byte(key)) 637 if err == nil { 638 t.Error("managed to parse invalid key:", key) 639 } 640 } 641} 642 643// Test vectors from RFC 7520 644var cookbookJWKs = []string{ 645 // EC Public 646 stripWhitespace(`{ 647 "kty": "EC", 648 "kid": "bilbo.baggins@hobbiton.example", 649 "use": "sig", 650 "crv": "P-521", 651 "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 652 A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", 653 "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy 654 SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1" 655 }`), 656 657 //ED Private 658 stripWhitespace(`{ 659 "kty": "OKP", 660 "crv": "Ed25519", 661 "x": "11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo", 662 "d": "nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A" 663 }`), 664 665 // EC Private 666 stripWhitespace(`{ 667 "kty": "EC", 668 "kid": "bilbo.baggins@hobbiton.example", 669 "use": "sig", 670 "crv": "P-521", 671 "x": "AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9 672 A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt", 673 "y": "AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy 674 SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1", 675 "d": "AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb 676 KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt" 677 }`), 678 679 // RSA Public 680 stripWhitespace(`{ 681 "kty": "RSA", 682 "kid": "bilbo.baggins@hobbiton.example", 683 "use": "sig", 684 "n": "n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT 685 -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV 686 wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj- 687 oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde 688 3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC 689 LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g 690 HdrNP5zw", 691 "e": "AQAB" 692 }`), 693 694 // RSA Private 695 stripWhitespace(`{"kty":"RSA", 696 "kid":"juliet@capulet.lit", 697 "use":"enc", 698 "n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy 699 O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP 700 8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0 701 Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X 702 OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1 703 _I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q", 704 "e":"AQAB", 705 "d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS 706 NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U 707 vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu 708 ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu 709 rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a 710 hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ", 711 "p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf 712 QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8 713 UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws", 714 "q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I 715 edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK 716 rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s", 717 "dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3 718 tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w 719 Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c", 720 "dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9 721 GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy 722 mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots", 723 "qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq 724 abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o 725 Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"}`), 726 727 // X.509 Certificate Chain 728 stripWhitespace(`{"kty":"RSA", 729 "use":"sig", 730 "kid":"1b94c", 731 "n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08 732 PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q 733 u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a 734 YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH 735 MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv 736 VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ", 737 "e":"AQAB", 738 "x5c": 739 ["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB 740 gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD 741 VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1 742 wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg 743 NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV 744 QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w 745 YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH 746 YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66 747 s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6 748 SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn 749 fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq 750 PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk 751 aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA 752 QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL 753 +9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1 754 zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL 755 2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo 756 4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq 757 gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA=="]}`), 758} 759 760// SHA-256 thumbprints of the above keys, hex-encoded 761var cookbookJWKThumbprints = []string{ 762 "747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793", 763 "f6934029a341ddf81dceb753e91d17efe16664f40d9f4ed84bc5ea87e111f29d", 764 "747ae2dd2003664aeeb21e4753fe7402846170a16bc8df8f23a8cf06d3cbe793", 765 "f63838e96077ad1fc01c3f8405774dedc0641f558ebb4b40dccf5f9b6d66a932", 766 "0fc478f8579325fcee0d4cbc6d9d1ce21730a6e97e435d6008fb379b0ebe47d4", 767 "0ddb05bfedbec2070fa037324ba397396561d3425d6d69245570c261dc49dee3", 768} 769 770func TestWebKeyVectorsValid(t *testing.T) { 771 for _, key := range cookbookJWKs { 772 var jwk2 JSONWebKey 773 err := jwk2.UnmarshalJSON([]byte(key)) 774 if err != nil { 775 t.Error("unable to parse valid key:", key, err) 776 } 777 } 778} 779 780func TestEd25519Serialization(t *testing.T) { 781 jwk := JSONWebKey{ 782 Key: ed25519PrivateKey, 783 } 784 serialized, _ := json.Marshal(jwk) 785 786 var jwk2 JSONWebKey 787 json.Unmarshal(serialized, &jwk2) 788 789 assert.True(t, bytes.Equal( 790 []byte(jwk.Key.(ed25519.PrivateKey).Public().(ed25519.PublicKey)), 791 []byte(jwk2.Key.(ed25519.PrivateKey).Public().(ed25519.PublicKey)))) 792} 793 794func TestThumbprint(t *testing.T) { 795 for i, key := range cookbookJWKs { 796 var jwk2 JSONWebKey 797 err := jwk2.UnmarshalJSON([]byte(key)) 798 if err != nil { 799 t.Error("unable to parse valid key:", key, err) 800 } 801 802 tp, err := jwk2.Thumbprint(crypto.SHA256) 803 if err != nil { 804 t.Error("unable to compute thumbprint:", key, err) 805 } 806 807 tpHex := hex.EncodeToString(tp) 808 if cookbookJWKThumbprints[i] != tpHex { 809 t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex) 810 } 811 } 812} 813 814func TestMarshalUnmarshalJWKSet(t *testing.T) { 815 jwk1 := JSONWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"} 816 jwk2 := JSONWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"} 817 var set JSONWebKeySet 818 set.Keys = append(set.Keys, jwk1) 819 set.Keys = append(set.Keys, jwk2) 820 821 jsonbar, err := json.Marshal(&set) 822 if err != nil { 823 t.Error("problem marshalling set", err) 824 } 825 var set2 JSONWebKeySet 826 err = json.Unmarshal(jsonbar, &set2) 827 if err != nil { 828 t.Fatal("problem unmarshalling set", err) 829 } 830 jsonbar2, err := json.Marshal(&set2) 831 if err != nil { 832 t.Fatal("problem marshalling set", err) 833 } 834 if !bytes.Equal(jsonbar, jsonbar2) { 835 t.Error("roundtrip should not lose information") 836 } 837} 838 839func TestJWKSetKey(t *testing.T) { 840 jwk1 := JSONWebKey{Key: rsaTestKey, KeyID: "ABCDEFG", Algorithm: "foo"} 841 jwk2 := JSONWebKey{Key: rsaTestKey, KeyID: "GFEDCBA", Algorithm: "foo"} 842 var set JSONWebKeySet 843 set.Keys = append(set.Keys, jwk1) 844 set.Keys = append(set.Keys, jwk2) 845 k := set.Key("ABCDEFG") 846 if len(k) != 1 { 847 t.Errorf("method should return slice with one key not %d", len(k)) 848 } 849 if k[0].KeyID != "ABCDEFG" { 850 t.Error("method should return key with ID ABCDEFG") 851 } 852} 853 854func TestJWKSymmetricKey(t *testing.T) { 855 sample1 := `{"kty":"oct","alg":"A128KW","k":"GawgguFyGrWKav7AX4VKUg"}` 856 sample2 := `{"kty":"oct","k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow","kid":"HMAC key used in JWS spec Appendix A.1 example"}` 857 858 var jwk1 JSONWebKey 859 json.Unmarshal([]byte(sample1), &jwk1) 860 861 if jwk1.Algorithm != "A128KW" { 862 t.Errorf("expected Algorithm to be A128KW, but was '%s'", jwk1.Algorithm) 863 } 864 expected1 := fromHexBytes("19ac2082e1721ab58a6afec05f854a52") 865 if !bytes.Equal(jwk1.Key.([]byte), expected1) { 866 t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected1), hex.EncodeToString(jwk1.Key.([]byte))) 867 } 868 869 var jwk2 JSONWebKey 870 json.Unmarshal([]byte(sample2), &jwk2) 871 872 if jwk2.KeyID != "HMAC key used in JWS spec Appendix A.1 example" { 873 t.Errorf("expected KeyID to be 'HMAC key used in JWS spec Appendix A.1 example', but was '%s'", jwk2.KeyID) 874 } 875 expected2 := fromHexBytes(` 876 0323354b2b0fa5bc837e0665777ba68f5ab328e6f054c928a90f84b2d2502ebf 877 d3fb5a92d20647ef968ab4c377623d223d2e2172052e4f08c0cd9af567d080a3`) 878 if !bytes.Equal(jwk2.Key.([]byte), expected2) { 879 t.Errorf("expected Key to be '%s', but was '%s'", hex.EncodeToString(expected2), hex.EncodeToString(jwk2.Key.([]byte))) 880 } 881} 882 883func TestJWKSymmetricRoundtrip(t *testing.T) { 884 jwk1 := JSONWebKey{Key: []byte{1, 2, 3, 4}} 885 marshaled, err := jwk1.MarshalJSON() 886 if err != nil { 887 t.Error("failed to marshal valid JWK object", err) 888 } 889 890 var jwk2 JSONWebKey 891 err = jwk2.UnmarshalJSON(marshaled) 892 if err != nil { 893 t.Error("failed to unmarshal valid JWK object", err) 894 } 895 896 if !bytes.Equal(jwk1.Key.([]byte), jwk2.Key.([]byte)) { 897 t.Error("round-trip of symmetric JWK gave different raw keys") 898 } 899} 900 901func TestJWKSymmetricInvalid(t *testing.T) { 902 invalid := JSONWebKey{} 903 _, err := invalid.MarshalJSON() 904 if err == nil { 905 t.Error("excepted error on marshaling invalid symmetric JWK object") 906 } 907 908 var jwk JSONWebKey 909 err = jwk.UnmarshalJSON([]byte(`{"kty":"oct"}`)) 910 if err == nil { 911 t.Error("excepted error on unmarshaling invalid symmetric JWK object") 912 } 913} 914 915func TestJWKIsPublic(t *testing.T) { 916 bigInt := big.NewInt(0) 917 eccPub := ecdsa.PublicKey{elliptic.P256(), bigInt, bigInt} 918 rsaPub := rsa.PublicKey{bigInt, 1} 919 920 cases := []struct { 921 key interface{} 922 expectedIsPublic bool 923 }{ 924 {&eccPub, true}, 925 {&ecdsa.PrivateKey{eccPub, bigInt}, false}, 926 {&rsaPub, true}, 927 {&rsa.PrivateKey{rsaPub, bigInt, []*big.Int{bigInt, bigInt}, rsa.PrecomputedValues{}}, false}, 928 {ed25519PublicKey, true}, 929 {ed25519PrivateKey, false}, 930 } 931 932 for _, tc := range cases { 933 k := &JSONWebKey{Key: tc.key} 934 if public := k.IsPublic(); public != tc.expectedIsPublic { 935 t.Errorf("expected IsPublic to return %t, got %t", tc.expectedIsPublic, public) 936 } 937 } 938} 939 940func TestJWKValid(t *testing.T) { 941 bigInt := big.NewInt(0) 942 eccPub := ecdsa.PublicKey{elliptic.P256(), bigInt, bigInt} 943 rsaPub := rsa.PublicKey{bigInt, 1} 944 edPubEmpty := ed25519.PublicKey([]byte{}) 945 edPrivEmpty := ed25519.PublicKey([]byte{}) 946 947 cases := []struct { 948 key interface{} 949 expectedValidity bool 950 }{ 951 {nil, false}, 952 {&ecdsa.PublicKey{}, false}, 953 {&eccPub, true}, 954 {&ecdsa.PrivateKey{}, false}, 955 {&ecdsa.PrivateKey{eccPub, bigInt}, true}, 956 {&rsa.PublicKey{}, false}, 957 {&rsaPub, true}, 958 {&rsa.PrivateKey{}, false}, 959 {&rsa.PrivateKey{rsaPub, bigInt, []*big.Int{bigInt, bigInt}, rsa.PrecomputedValues{}}, true}, 960 {ed25519PublicKey, true}, 961 {ed25519PrivateKey, true}, 962 {edPubEmpty, false}, 963 {edPrivEmpty, false}, 964 } 965 966 for _, tc := range cases { 967 k := &JSONWebKey{Key: tc.key} 968 valid := k.Valid() 969 if valid != tc.expectedValidity { 970 t.Errorf("expected Valid to return %t, got %t", tc.expectedValidity, valid) 971 } 972 if valid { 973 wasPublic := k.IsPublic() 974 p := k.Public() // all aforemention keys are asymmetric 975 if !p.Valid() { 976 t.Errorf("unable to derive public key from valid asymmetric key") 977 } 978 if wasPublic != k.IsPublic() { 979 t.Errorf("original key was touched during public key derivation") 980 } 981 } 982 } 983} 984 985func TestJWKBufferSizeCheck(t *testing.T) { 986 key := `{ 987 "kty":"EC", 988 "crv":"P-256", 989 "x":"m9GSmJ5iGmAYlMlaOJGSFN_CjN9cIn8GGYExP-C0FBiIXlWTNvGN38R9WdrHcppfsKF0FXMOMyutpHIRaiMxYSA", 990 "y":"ZaPcRZ3q_7T3h-Gwz2i-T2JjJXfj6YVGgKHcFz5zqmg"}` 991 var jwk JSONWebKey 992 jwk.UnmarshalJSON([]byte(key)) 993 jwk.Valid() // true 994 // panic: square/go-jose: invalid call to newFixedSizeBuffer (len(data) > length) 995 // github.com/square/go-jose.newFixedSizeBuffer(0xc420014557, 0x41, 0x41, 0x20, 0x0) 996 jwk.Thumbprint(crypto.SHA256) 997} 998 999func TestJWKPaddingPrivateX(t *testing.T) { 1000 key := `{ 1001 "kty": "EC", 1002 "crv": "P-256", 1003 "x": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ", 1004 "y": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs", 1005 "d": "nIVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0" 1006 }` 1007 var jwk JSONWebKey 1008 err := jwk.UnmarshalJSON([]byte(key)) 1009 if err == nil { 1010 t.Errorf("Expected key with short x to fail unmarshalling") 1011 } 1012 if !strings.Contains(err.Error(), "wrong length for x") { 1013 t.Errorf("Wrong error for short x, got %q", err) 1014 } 1015 if jwk.Valid() { 1016 t.Errorf("Expected key to be invalid, but it was valid.") 1017 } 1018} 1019 1020func TestJWKPaddingPrivateY(t *testing.T) { 1021 key := `{ 1022 "kty": "EC", 1023 "crv": "P-256", 1024 "x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs", 1025 "y": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ", 1026 "d": "nIVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0" 1027 }` 1028 var jwk JSONWebKey 1029 err := jwk.UnmarshalJSON([]byte(key)) 1030 if err == nil { 1031 t.Errorf("Expected key with short x to fail unmarshalling") 1032 } 1033 if !strings.Contains(err.Error(), "wrong length for y") { 1034 t.Errorf("Wrong error for short y, got %q", err) 1035 } 1036 if jwk.Valid() { 1037 t.Errorf("Expected key to be invalid, but it was valid.") 1038 } 1039} 1040 1041func TestJWKPaddingPrivateD(t *testing.T) { 1042 key := `{ 1043 "kty": "EC", 1044 "crv": "P-256", 1045 "x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs", 1046 "y": "qnPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ", 1047 "d": "IVCvMR2wkLmeGJErOpI23VDHl2s3JwGdbzKy0odir0" 1048 }` 1049 var jwk JSONWebKey 1050 err := jwk.UnmarshalJSON([]byte(key)) 1051 if err == nil { 1052 t.Errorf("Expected key with short x to fail unmarshalling") 1053 } 1054 if !strings.Contains(err.Error(), "wrong length for d") { 1055 t.Errorf("Wrong error for short d, got %q", err) 1056 } 1057 if jwk.Valid() { 1058 t.Errorf("Expected key to be invalid, but it was valid.") 1059 } 1060} 1061 1062func TestJWKPaddingX(t *testing.T) { 1063 key := `{ 1064 "kty": "EC", 1065 "crv": "P-256", 1066 "x": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ", 1067 "y": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs" 1068 }` 1069 var jwk JSONWebKey 1070 err := jwk.UnmarshalJSON([]byte(key)) 1071 if err == nil { 1072 t.Errorf("Expected key with short x to fail unmarshalling") 1073 } 1074 if !strings.Contains(err.Error(), "wrong length for x") { 1075 t.Errorf("Wrong error for short x, got %q", err) 1076 } 1077 if jwk.Valid() { 1078 t.Errorf("Expected key to be invalid, but it was valid.") 1079 } 1080} 1081 1082func TestJWKPaddingY(t *testing.T) { 1083 key := `{ 1084 "kty": "EC", 1085 "crv": "P-256", 1086 "x": "vEEs4V0egJkNyM2Q4pp001zu14VcpQ0_Ei8xOOPxKZs", 1087 "y": "nPTIABcDASY6FNGSNfHCB51tY7qChtgzeVazOtLrwQ" 1088 }` 1089 var jwk JSONWebKey 1090 err := jwk.UnmarshalJSON([]byte(key)) 1091 if err == nil { 1092 t.Errorf("Expected key with short y to fail unmarshalling") 1093 } 1094 if !strings.Contains(err.Error(), "wrong length for y") { 1095 t.Errorf("Wrong error for short y, got %q", err) 1096 } 1097 if jwk.Valid() { 1098 t.Errorf("Expected key to be invalid, but it was valid.") 1099 } 1100} 1101