I'm trying to sign a JWT using the private key (ECCP256) embedded in my Yubikey5.
Note that the ECCP384 is not working either.
However, the signature verification is not working (i'm trying on jwt.io)
The signature is valid if i'm checking it using openssl (and i'm signing the right data, i.e base64url(header)+"."+base64url(data) :
$ openssl dgst -sha256 -verify pubkey.pem -signature data.sig data.jwt
Verified OK
My script is :
echo -n '{"alg":"ES256","typ":"JWT"}' | base64 | tr -d "\n" | tr '+/' '-_' | tr -d '=' > token.jwt
printf "." >> token.jwt
cat data.json | base64 | tr -d "\n" | tr '+/' '-_' | tr -d '=' >> token.jwt
yubico-piv-tool -a verify-pin --sign -s 9c -H SHA256 -A ECCP256 -i token.jwt -o data.sig
cp token.jwt data.jwt #this is just to check that i signed the right data
printf "." >> token.jwt
cat data.sig | base64 | tr -d '\n=' | tr -- '+/' '-_' >> token.jwt
And one output example :
Enter PIN:
Successfully verified PIN.
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJpYXQiOiAxNTE2MjM5MDIyCn0K.MEUCIGI9uVtz_r14rgJw19YdPR5sNbezKB3vOa3bApcFWJg2AiEAzf9ITwL-6fsapZt0pBzNYA5zaHBcmjP-HtCOA5uo870
Am I doing someting wrong ? Note that the exact same script using RSA 2048 keys is working (RS256).
Note : using openssl for key generation is not working either for jwt validation (despite the signature being validated by OpenSSL)
$ openssl ecparam -genkey -name prime256v1 -noout -out private.pem
$ openssl ec -in private.pem -pubout -out public.pem
$ echo -n '{"alg":"ES256","typ":"JWT"}' | base64 | tr -d "\n" | tr '+/' '-_' | tr -d '=' > token.jwt
$ echo -n "." >> token.jwt
$ cat ../data.json | base64 | tr -d "\n" | tr '+/' '-_' | tr -d '=' >> token.jwt
$ openssl dgst -sha256 -sign private.pem token.jwt > signature.bin
$ openssl dgst -sha256 -verify public.pem -signature signature.bin token.jwt
Verified OK
$ echo -n "." >> token.jwt
$ cat signature.bin | base64 | tr -d '\n=' | tr -- '+/' '-_' >> token.jwt
$ cat token.jwt
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.ewogICJzdWIiOiAiMTIzNDU2Nzg5MCIsCiAgIm5hbWUiOiAiSm9obiBEb2UiLAogICJpYXQiOiAxNTE2MjM5MDIyCn0K.MEUCIHY5OUbyovQX4jXFQVqF0ZIpoIBBjhQ6KMwnjHpp66JMAiEApIK2YspuRTXkzquunG-385QMFSACOKeKuQDGZC2mZcM
Both openssl and yubico-piv tools generate signatures that are wrapped in an ASN.1 SEQUENCE. For example, here is a ECDSA signature using the p256 curve:
JWT however uses raw signatures. In above example this means it should just contains the (r,s) pair of integers. To fix this, you can just concatenate the binary integers into a single file (containing 64 bytes in this example).
Parsing the ASN.1 structure is a bit tricky, as the encoding differs for different values of the integers, but a simple hack would be to use openssl for this:
For a complete example of signing a JWT using a YubiKey, see this gist.