# PEM -> JWK for a P-256 key: the SPKI DER ends with the point 04 ‖ x ‖ yb64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; }spki=$(openssl pkey -pubin -in pub.pem -outform DER | xxd -p | tr -d '\n')x=$(printf '%s' "${spki: -128:64}" | xxd -r -p | b64url)y=$(printf '%s' "${spki: -64}" | xxd -r -p | b64url)printf '{"kty":"EC","crv":"P-256","x":"%s","y":"%s"}\n' "$x" "$y" | tee out.jwk.json# JWK -> PEM for a P-256 key: the fixed 26-byte SPKI header, then 04 ‖ x ‖ yb64url_decode() { local v; v=$(tr '_-' '/+' <<< "$1"); while (( ${#v} % 4 )); do v+="="; done; openssl base64 -d -A <<< "$v"; }json_field() { grep -o "\"$1\" *: *\"[^\"]*\"" pub.jwk.json | sed 's/.*: *"\(.*\)"/\1/'; }{ printf '3059301306072a8648ce3d020106082a8648ce3d03010703420004' | xxd -r -p b64url_decode "$(json_field x)" b64url_decode "$(json_field y)"} > pub.deropenssl pkey -pubin -inform DER -in pub.der -out out.pem # also checks that the point is on the curvecat out.pemHow it works
openssl pkey -pubin -outform DERgives the 91-byte SPKI of a P-256 key: a 26-byte header, the byte04, and the 32-byte coordinates x and y.xxd -r -pturns the hex back into bytes, and theb64urlhelper encodes them in the unpadded base64url form JWK uses.- For the reverse, the header
3059…420004is the same for every P-256 public key; the decodedxandycomplete the DER. openssl pkey -pubin -inform DERparses that DER — which checks that the point is on the curve — and writes the standard PEM.
Gotchas
- The offsets and the header are specific to P-256 public keys. P-384 keys have a different header and 48-byte coordinates, RSA and Ed25519 keys a different structure altogether.
- A point that is not on the curve fails with the misleading
Could not find private key of Public Key from pub.der(andSTORE routines … unsupported): the problem is the point, not a missing private key. - Base64url has no padding and uses
-_;openssl base64 -dexpects standard base64 with padding, whichb64url_decoderestores first. json_fieldis agrepover the JSON, good enough for a single well-formed JWK; for JWKS files or arbitrary JSON, usejqor one of the language recipes.