Closed frasertweedale closed 7 years ago
Stuff that I know. Maybe this simplifies the implementation:
Currently I'm using this implementation in my ACME library:
import Crypto.Hash.SHA256 (hash)
jwkThumbprint :: KeyMaterial -> String
jwkThumbprint = sha256 . L.unpack . encodingToLazyByteString . pairs . toEnc
where
toEnc (ECKeyMaterial ECKeyParameters {..}) =
"crv" .= ecCrv <> "kty" .= ecKty <> "x" .= ecX <> "y" .= ecY
toEnc (RSAKeyMaterial keyParam) =
"e" .= (keyParam ^?! rsaE) <> "kty" .= (keyParam ^?! rsaKty) <> "n" .=
(keyParam ^?! rsaN)
toEnc (OctKeyMaterial OctKeyParameters {..}) = undefined
sha256 :: String -> String
sha256 x = filter (/= '=') $ B.unpack $ Base64.encode (hash $ B.pack x)
Looks pretty messy. Would be great to have this in jose! (Also, converting something to the JWS Base64 format and the SHA256 function would be great library features!)
I'm testing this against the RFC example (only RSA). The test looks something like this
let file = $(embedStringFile "test/rfc7638-jwk.json") :: L.ByteString
jwk = fromJust (decode file) :: KeyMaterial
in jwkThumbprint jwk @?= "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs"
@sophie-h thanks for the info! Your approach is sound; I'll add this soon (within the next week or so), and include it in the next release (maybe a bit after I add the new function, unless more fixes/features are needed).
For the base64 stuff, I think it would be best to add it base64-bytestring (or, if they that project is not receptive, a small, separate library).
I don't see a need to export a SHA-256 function from jose (once JWK Thumbprint is implemented).
Thanks again for your input; glad to hear someone is working on a Haskell ACME implementation and finding my library useful.
@sophie-h hi again. Would you mind reviewing f7cca79ed0d3a87332767363368344ad466ba700 to determine if it meets your needs?
Note that I re-export Crypto.Hash
via Crypto.JOSE.JWK
so it is easy to select the
desired digest algorithm.
Great! Without jose it would be really painful to work with ACME. Implementation looks good to me!
Maybe I overlooked something, but currently I don't see a way to avoid the filter (/= '=')
. If I need the thumbprint inside of JWS I can use Base64Octets
/Base64SHA256
, but ACME also needs the thumbprint as Base64url without =
as DNS record etc. Maybe, the easiest way would be to add a function that converts Base64Octets
/Base64SHA256
to String or Bytestring. base64-bytestring does not remove the =
, so imho the logic of this format belongs to JWS.
Also, at some point Base64SHA256
fooled me. I thought that my Bytestring would be hashed when converted to JSON. Don't know if it's worth documenting that the constructor takes as hashed value.
@sophie-h how do you like this prism? https://github.com/frasertweedale/hs-jose/commit/48efff65638a49db7f416dea6198e627f1399371
Update: added a new subcommand to the example
program that computes the fingerprint and uses the prism to print the base64url encoding of it: https://github.com/frasertweedale/hs-jose/commit/8a600ba8482bb97e5211df00ed24d95e33b0268a
Looks good and very flexible.
Fixed in f7cca79.
https://tools.ietf.org/html/rfc7638