I'm trying to load the FIDO Alliance Metadata in Python using JWCrypto, but I always get a jwcrypto.jws.InvalidJWSSignature('Verification failed')
error.
FIDO Alliance provides an endpoint with authenticator metadata as they state here. The data is wrapped in a signed JWT token. They do not directly provide a public key, they however link to a GlobalSign root certificate they use.
How can I properly load and validate the JWT when I don't have their public key, only the certificate?
I tried deriving the public key from the certificate and using that to deserialize the JWT, but JWCrypto complains about invalid signature.
import requests
import jwcrypto.jwt, jwcrypto.jwk
import cryptography.x509
import cryptography.hazmat.backends
import cryptography.hazmat.primitives.serialization
import cryptography.hazmat.primitives.ciphers.algorithms
# Download certificate
root_cert = requests.get("http://secure.globalsign.com/cacert/root-r3.crt").content.strip()
# Get public key from certificate
root_cert = cryptography.x509.load_der_x509_certificate(root_cert)
public_key_pem = root_cert.public_key().public_bytes(
encoding=cryptography.hazmat.primitives.serialization.Encoding.PEM,
format=cryptography.hazmat.primitives.serialization.PublicFormat.PKCS1,
)
public_key = jwcrypto.jwk.JWK.from_pem(public_key_pem)
# Download JWT
signed_data = requests.get("https://mds3.fidoalliance.org/").content.strip()
# Deserialize
jwt = jwcrypto.jwt.JWT(jwt=signed_data.decode("ascii"), key=public_key)
print(jwt.claims)
The problem with my approach was deriving the public key from the root certificate, while the JWT is signed by the leaf certificate, which is actually included in the JWT
x5c
header (see more in rfc7515).So to verify the JWT signature, one needs to derive a public key from the leaf certificate and use that one:
That was all I needed!
Additional info: The root certificate is only useful for verifying the certificate chain: