MicahParks / keyfunc

Create a jwt.Keyfunc for JWT parsing with a JWK Set or given cryptographic keys (like HMAC) in Golang.
Apache License 2.0
265 stars 46 forks source link

keyfunc: failed to create JWK from JWK Marshal: failed to validate JSON Web Key: failed to validate JWK: X5T in marshal does not match X5T in marshalled #128

Closed ksegun closed 3 days ago

ksegun commented 3 days ago

We encountered the following issue when migrating to v3. When the JWK Set contains a JWK with x5t and no x5c it fails with the error below. The same payload did not fail prior to v3.

Failed to refresh HTTP JWK Set from remote HTTP resource. error="failed to create JWK from JWK Marshal: failed to validate JSON Web Key: failed to validate JWK: X5T in marshal does not match X5T in marshalled" url=http://127.0.0.1:62343

Here is a sample that demonstrates the issue.

package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httptest"

    "github.com/MicahParks/keyfunc/v3"
)

func main() {
    key1 := `{"keys":[
      {"kty":"RSA",
      "use":"sig",
      "kid":"1b94c",
      "n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ",
      "e":"AQAB"      
     }]}`

    key2 := `{"keys":[
      {"kty":"RSA",
      "use":"sig",
      "kid":"1b94c",
      "n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ",
      "e":"AQAB",
      "x5c":["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDVQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1wYmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnHYMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpnfajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPqPvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVkaZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTqgawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA=="],
      "x5t":"4pNenEBLv0JpLIdugWxQkOsZcK0"
     }]}`

    key3 := `{"keys":[
      {"kty":"RSA",
      "use":"sig",
      "kid":"1b94c",
      "n":"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Qu2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4aYWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwHMTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMvVfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ",
      "e":"AQAB",
      "x5t":"4pNenEBLv0JpLIdugWxQkOsZcK0"
     }]}`

    jwksData := key1

    srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        //log.Println(jwksData)
        fmt.Fprintf(w, jwksData)
    }))
    defer srv.Close()

    log.Println("Key without x5t and x5c")

    _, err := keyfunc.NewDefault([]string{srv.URL})
    if err != nil {
        log.Fatalf("Failed to create a keyfunc.Keyfunc.\nError: %s", err)
    }

    log.Println("Key with x5t and x5c")

    jwksData = key2

    _, err = keyfunc.NewDefault([]string{srv.URL})
    if err != nil {
        log.Fatalf("Failed to create a keyfunc.Keyfunc.\nError: %s", err)
    }

    log.Println("Key with x5t and no x5c")

    jwksData = key3

    _, err = keyfunc.NewDefault([]string{srv.URL})
    if err != nil {
        log.Fatalf("Failed to create a keyfunc.Keyfunc.\nError: %s", err)
    }
}
MicahParks commented 3 days ago

@ksegun thank you for opening this issue. If you are comfortable sharing, is the JWK Set from a custom written application or a SaaS provider like AWS or Azure?

ksegun commented 3 days ago

@MicahParks the JWK Set is from a custom written application.

MicahParks commented 3 days ago

@ksegun in that case I'll let you know it seems a bit odd that a certificate thumbprint x5t is present but the certificate itself x5c or x5u is not. It may possibly be a bug in the service that creates the JWK Set.

Regardless, it seems the RFC does not specifically mention that a certificate must be present for a thumbprint to be included. Therefore, I'm writing a patch for the jwkset project and will increment the version this project, keyfunc, uses and notify you of a release. I'm hoping to get that out in the next hour or so.

MicahParks commented 3 days ago

@ksegun the newest version of the keyfunc project v3.3.5 should have the bug fix you need.

go get github.com/MicahParks/keyfunc/v3@v3.3.5

Please let me know if this resolves your issue or not.

ksegun commented 3 days ago

@MicahParks thanks for the rapid response, I can confirm the latest version fixes the issue. I will follow up with the team that owns the application about this issue as well so they can do something about it. Thank you so much, I am simply blown away!