How do I make a JWT from an EC private key?
ericpashman opened this issue · 4 comments
There are several Haskell libraries that will parse a PEM-encoded EC private key into one of various datatypes; e.g., the pemToKey
function from the x509-store
library will produce a representation of an EC private key in its PrivKey
type. But as far as I can tell, hs-jose
doesn't provide functionality directly to create a JWK
from any representation of an EC private key.
It seems straightforward to create a JWK
by extracting the parameters of the EC private key from a third-party datatype, then to use those parameters manually to construct an ECKeyParameters
value, then a KeyMaterial
value, and finally to use fromKeyMaterial
to make a JWK.
For the first issue: I will address this by:
- Exporting the
ECKeyParameters
constructor, so that you can construct the key material yourself then usefromKeyMaterial
. - Also add convenience functions for converting from the crypton-x509
PubKey
andPrivKey
types (i.e., what the crypton-x509-store package produces).
For the JWT with custom claims, the preferred approach is described in the Crypto.JWT module doc
. Define a record type that includes a ClaimsSet
value, and provide instances of HasClaimsSet
and (To|From)JSON
.
@ericpashman do you want to take a look at branch https://github.com/frasertweedale/hs-jose/tree/feature/121-key-conversion and see if it meets your needs? You can use Crypto.JOSE.JWK.fromX509PrivKey
to construct the JWK from a Data.X509.PrivKey
.
Hi, Fraser, thank you for these changes.
Your addition of fromX509PrivKey
does what I need to create a JWK. This solves my issue (1). Providing this function makes it unnecessary (for my purposes) to expose the ECKeyParameters
constructor, so you can revert that commit if you want.
On my issue (2): the hang-up is that there isn't an "obvious" way to create a SignedJWT
with custom headers. (The API I'm working with requires a nonce header in the JWT, which I think is somewhat common.) That is, the signJWT
function from Crypto.JWT
explicitly takes a JWSHeader ()
value, so it is not possible to pass custom headers created as documented here.
It's possible to get around this by using the signJWS
function directly, to create a JWS
that meets the definition of a JWT (i.e., a JWS with a payload of JSON-encoded claims), but IMO it'd be kinder to users not to make them figure out how to do this. It looks to me like signJWT
can be generalized to accept custom header types in a backwards-compatible way simply by generalizing its type signature:
signJWT
:: ( MonadRandom m, MonadError e m, AsError e
, HasJWSHeader header, HasParams header, ProtectionIndicator p
, ToJSON payload )
=> JWK
-> header p --^ Header with protection indicator p
-> payload
-> m SignedJWT
signJWT k h c = signJWS (encode c) (Identity (h, k))
Thanks again.
@ericpashman thanks for the feedback, and thank you for the clarifications. I will extract (2) as a separate issue and continue discussion there.