JOSE.JWS (JOSE v1.11.2) View Source

JWS stands for JSON Web Signature which is defined in RFC 7515.

Unsecured Signing Vulnerability

The "none" signing algorithm is disabled by default to prevent accidental verification of empty signatures (read about the vulnerability here).

You may also enable the "none" algorithm as an application environment variable for :jose or by using JOSE.unsecured_signing/1.

JOSE.JWS.verify_strict/3 is recommended over JOSE.JWS.verify/2 so that signing algorithms may be whitelisted during verification of signed input.

Algorithms

The following algorithms are currently supported by JOSE.JWS (some may need the JOSE.crypto_fallback/1 option to be enabled):

  • "Ed25519"
  • "Ed25519ph"
  • "Ed448"
  • "Ed448ph"
  • "EdDSA"
  • "ES256"
  • "ES384"
  • "ES512"
  • "HS256"
  • "HS384"
  • "HS512"
  • "Poly1305"
  • "PS256"
  • "PS384"
  • "PS512"
  • "RS256"
  • "RS384"
  • "RS512"
  • "none" (disabled by default, enable with JOSE.unsecured_signing/1)

Examples

All of the example keys generated below can be found here: https://gist.github.com/potatosalad/925a8b74d85835e285b9

Ed25519 and Ed25519ph

# let's generate the 2 keys we'll use below
jwk_ed25519   = JOSE.JWK.generate_key({:okp, :Ed25519})
jwk_ed25519ph = JOSE.JWK.generate_key({:okp, :Ed25519ph})

# Ed25519
iex> signed_ed25519 = JOSE.JWS.sign(jwk_ed25519, "{}", %{ "alg" => "Ed25519" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFZDI1NTE5In0.e30.xyg2LTblm75KbLFJtROZRhEgAFJdlqH9bhx8a9LO1yvLxNLhO9fLqnFuU3ojOdbObr8bsubPkPqUfZlPkGHXCQ"
iex> JOSE.JWS.verify(jwk_ed25519, signed_ed25519) |> elem(0)
true

# Ed25519ph
iex> signed_ed25519ph = JOSE.JWS.sign(jwk_ed25519ph, "{}", %{ "alg" => "Ed25519ph" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFZDI1NTE5cGgifQ.e30.R3je4TTxQvoBOupIKkel_b8eW-G8KaWmXuC14NMGSCcHCTalURtMmVqX2KbcIpFBeI-OKP3BLHNIpt1keKveDg"
iex> JOSE.JWS.verify(jwk_ed25519ph, signed_ed25519ph) |> elem(0)
true

Ed448 and Ed448ph

# let's generate the 2 keys we'll use below
jwk_ed448   = JOSE.JWK.generate_key({:okp, :Ed448})
jwk_ed448ph = JOSE.JWK.generate_key({:okp, :Ed448ph})

# Ed448
iex> signed_ed448 = JOSE.JWS.sign(jwk_ed448, "{}", %{ "alg" => "Ed448" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFZDQ0OCJ9.e30.UlqTx962FvZP1G5pZOrScRXlAB0DJI5dtZkknNTm1E70AapkONi8vzpvKd355czflQdc7uyOzTeAz0-eLvffCKgWm_zebLly7L3DLBliynQk14qgJgz0si-60mBFYOIxRghk95kk5hCsFpxpVE45jRIA"
iex> JOSE.JWS.verify(jwk_ed448, signed_ed448) |> elem(0)
true

# Ed448ph
iex> signed_ed448ph = JOSE.JWS.sign(jwk_ed448ph, "{}", %{ "alg" => "Ed448ph" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFZDQ0OHBoIn0.e30._7wxQF8Am-Fg3E-KgREXBv3Gr2vqLM6ja_7hs6kA5EakCrJVQ2QiAHrr4NriLABmiPbVd7F7IiaAApyR3Ud4ak3lGcHVxSyksjJjvBUbKnSB_xkT6v_QMmx27hV08JlxskUkfvjAG0-yKGC8BXoT9R0A"
iex> JOSE.JWS.verify(jwk_ed448ph, signed_ed448ph) |> elem(0)
true

EdDSA

# EdDSA works with Ed25519, Ed25519ph, Ed448, and Ed448ph keys.
# However, it defaults to Ed25519 for key generation.
jwk_eddsa = JOSE.JWS.generate_key(%{ "alg" => "EdDSA" })

# EdDSA
iex> signed_eddsa = JOSE.JWS.sign(jwk_eddsa, "{}", %{ "alg" => "EdDSA" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFZERTQSJ9.e30.rhb5ZY7MllNbW9q-SCn_NglhYtaRGMXEUDj6BvJjltOt19tEI_1wFrVK__jL91i9hO7WtVqRH_OfHiilnO1CAQ"
iex> JOSE.JWS.verify(jwk_eddsa, signed_eddsa) |> elem(0)
true

ES256, ES384, and ES512

# let's generate the 3 keys we'll use below
jwk_es256 = JOSE.JWK.generate_key({:ec, :secp256r1})
jwk_es384 = JOSE.JWK.generate_key({:ec, :secp384r1})
jwk_es512 = JOSE.JWK.generate_key({:ec, :secp521r1})

# ES256
iex> signed_es256 = JOSE.JWS.sign(jwk_es256, "{}", %{ "alg" => "ES256" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFUzI1NiJ9.e30.nb7cEQQuIi2NgcP5A468FHGG8UZg8gWZjloISyVIwNh3X6FiTTFZsvc0mL3RnulWoNJzKF6xwhae3botI1LbRg"
iex> JOSE.JWS.verify(jwk_es256, signed_es256) |> elem(0)
true

# ES384
iex> signed_es384 = JOSE.JWS.sign(jwk_es384, "{}", %{ "alg" => "ES384" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFUzM4NCJ9.e30.-2kZkNe66y2SprhgvvtMa0qBrSb2imPhMYkbi_a7vx-vpEHuVKsxCpUyNVLe5_CXaHWhHyc2rNi4uEfU73c8XQB3e03rg_JOj0H5XGIGS5G9f4RmNMSCiYGwqshLSDFI"
iex> JOSE.JWS.verify(jwk_es384, signed_es384) |> elem(0)
true

# ES512
iex> signed_es512 = JOSE.JWS.sign(jwk_es512, "{}", %{ "alg" => "ES512" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJFUzUxMiJ9.e30.AOIw4KTq5YDu6QNrAYKtFP8R5IljAbhqXuPK1dUARPqlfc5F3mM0kmSh5KOVNHDmdCdapBv0F3b6Hl6glFDPlxpiASuSWtvvs9K8_CRfSkEzvToj8wf3WLGOarQHDwYXtlZoki1zMPGeWABwafTZNQaItNSpqYd_P9GtN0XM3AALdua0"
iex> JOSE.JWS.verify(jwk_es512, signed_es512) |> elem(0)
true

HS256, HS384, and HS512

# let's generate the 3 keys we'll use below
jwk_hs256 = JOSE.JWK.generate_key({:oct, 16})
jwk_hs384 = JOSE.JWK.generate_key({:oct, 24})
jwk_hs512 = JOSE.JWK.generate_key({:oct, 32})

# HS256
iex> signed_hs256 = JOSE.JWS.sign(jwk_hs256, "{}", %{ "alg" => "HS256" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJIUzI1NiJ9.e30.r2JwwMFHECoDZlrETLT-sgFT4qN3w0MLee9MrgkDwXs"
iex> JOSE.JWS.verify(jwk_hs256, signed_hs256) |> elem(0)
true

# HS384
iex> signed_hs384 = JOSE.JWS.sign(jwk_hs384, "{}", %{ "alg" => "HS384" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJIUzM4NCJ9.e30.brqQFXXM0XtMWDdKf0foEQcvK18swcoDkxBqCPeed_IO317_tisr60H2mz79SlNR"
iex> JOSE.JWS.verify(jwk_hs384, signed_hs384) |> elem(0)
true

# HS512
iex> signed_hs512 = JOSE.JWS.sign(jwk_hs512, "{}", %{ "alg" => "HS512" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJIUzUxMiJ9.e30.ge1JYomO8Fyl6sgxLbc4g3AMPbaMHLmeTl0jrUYAJZSloN9j4VyhjucX8d-RWIlMjzdG0xyklw53k1-kaTlRVQ"
iex> JOSE.JWS.verify(jwk_hs512, signed_hs512) |> elem(0)
true

Poly1305

This is highly experimental and based on RFC 7539.

Every signed message has a new 96-bit nonce generated which is used to generate a one-time key from the secret.

# let's generate the key we'll use below
jwk_poly1305 = JOSE.JWK.generate_key({:oct, 32})

# Poly1305
iex> signed_poly1305 = JOSE.JWS.sign(jwk_poly1305, "{}", %{ "alg" => "Poly1305" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJQb2x5MTMwNSIsIm5vbmNlIjoiTjhiR3A1QXdob0Y3Yk1YUiJ9.e30.XWcCkV1WU72cTO-XuiNRAQ"
iex> JOSE.JWS.verify(jwk_poly1305, signed_poly1305) |> elem(0)
true

# let's inspect the protected header to see the generated nonce
iex> JOSE.JWS.peek_protected(signed_poly1305)
"{\"alg\":\"Poly1305\",\"nonce\":\"N8bGp5AwhoF7bMXR\"}"

PS256, PS384, and PS512

# let's generate the 3 keys we'll use below (cutkey must be installed as a dependency)
jwk_ps256 = JOSE.JWK.generate_key({:rsa, 2048})
jwk_ps384 = JOSE.JWK.generate_key({:rsa, 4096})
jwk_ps512 = JOSE.JWK.generate_key({:rsa, 8192}) # this may take a few seconds

# PS256
iex> signed_ps256 = JOSE.JWS.sign(jwk_ps256, "{}", %{ "alg" => "PS256" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJQUzI1NiJ9.e30.RY5A3rG2TjmdlARE57eSSSFE6plkuQPKLKsyqz3WrqKRWZgSrvROACRTzoGyrx1sNvQEZJLZ-xVhrFvP-80Q14XzQbPfYLubvn-2wcMNCmih3OVQNVtFdFjA5U2NG-sF-SWAUmm9V_DvMShFGG0qHxLX7LqT83lAIgEulgsytb0xgOjtJObBru5jLjN_uEnc7fCfnxi3my1GAtnrs9NiKvMfuIVlttvOORDFBTO2aFiCv1F-S6Xgj16rc0FGImG0x3amQcmFAD9g41KY0_KsCXgUfoiVpC6CqO6saRC4UDykks91B7Nuoxjsm3nKWa_4vKh9QJy-V8Sf0gHxK58j8Q"
iex> JOSE.JWS.verify(jwk_ps256, signed_ps256) |> elem(0)
true

# PS384
iex> signed_ps384 = JOSE.JWS.sign(jwk_ps384, "{}", %{ "alg" => "PS384" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJQUzM4NCJ9.e30.xmYVenIhi75hDMy3bnL6WVpVlTzYmO1ejOZeq9AkSjkp_STrdIp6uUEs9H_y7CLD9LrGYYHDNDl9WmoH6cn95WZT9KJgAVNFFYd8owY6JUHGKU1jUbLkptAgvdphVpWZ1C5fVCRt4vmp8K9f6jy3er9jCBNjl9gSBdmToFwYdXI26ZKSBjfoVm2tFFQIOThye4YQWCWHbzSho6J7d5ATje72L30zDvWXavJ-XNvof5Tkju4WQQB-ukFoqTw4yV8RVwCa-DX61I1hNrq-Zr75_iWmHak3GqNkg5ACBEjDtvtyxJizqy9KINKSlbB9jGztiWoEiXZ6wJ5sSJ6ZrSFJuQVEmns_dLqzpSHEFkWfczEV_gj9Eu_EXwMp9YQlQ3GktfXaz-mzH_jUaLmudEUskQGCiR92gK9KR6_ROQPJfD54Tkqdh6snwg6y17k8GdlTc5qMM3V84q3R6zllmhrRhV1Dlduc0MEqKcsQSX_IX21-sfiVMIcUsW73dIPXVZI2jsNlEHKqwMjWdSfjYUf3YApxSGERU3u4lRS3F0yRrZur8KWS3ToilApjg0cNg9jKas8g8C8ZPgGFYM6StVxUnXRmsJILDnsZMIPjbUDAPHhB0DwLwOB7OqGUBcItX-zwur1OVnHR7aIh1DbfWfyTIml8VIhYfGfazgXfgQVcGEM"
iex> JOSE.JWS.verify(jwk_ps384, signed_ps384) |> elem(0)
true

# PS512
iex> signed_ps512 = JOSE.JWS.sign(jwk_ps512, "{}", %{ "alg" => "PS512" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJQUzUxMiJ9.e30.fJe52-PF3I7UrpQamLCnmVAGkBhP0HVeJi48qZqaFc1-_tQEiYTfxuwQBDlt01GQWpjTZRb097bZF6RcrKWwRHyAo3otOZdR32emWfOHddWLL3qotj_fTaDR2-OhLixwce6mFjnHqppHH1zjCmgbKPG8S2cAadNd5w10VR-IS6LdnFRhNZOahuuB7dzCEJaSjkGfm3_9xdj3I0ZRl4fauR_LO9NQIyvMMeCFevowz1sVGG1G-I2njPrEXvxhAMp7y2mao5Yik8UUORXRjcn2Wai3umy8Yh4nHYU5qqruHjLjDwudCPNDjxjg294z1uAUpt7S0v7VbrkgUvgutTFAT-bcHywFODiycajQuqIpFp1TCUAq3Xe2yk4DTRduvPIKcPkJQnFrVkClJAU9A4D4602xpdK-z2uCgWsBVHVokf5-9ba5EqVb8BJx2xYZUIA5CdrIiTBfoe_cI5Jh92uprcWC_llio2ZJvGdQpPgwCgca7-RQ94LAmIA4u3mAndrZj_z48T2GjHbaKzl18FOPQH0XEvK_W5oypUe5NOGlz9mMGZigbFqBY2lM-7oVVYc4ZA3VFy8Dv1nWhU6DGb2NnDnQUyChllyBREuZbwrkOTQEvqqdV-6lM6VwXNu1gqc3YHly9W6u5CmsnxtvlIxsUVg679HiqdtdWxLSaIJObd9Xji56-eEkWMEA08SNy9p-F9AgHOxzoZqgrAQDEwqyEwqoAW681xLc5Vck580AQDxO9Ha4IqLIPirpO5EODQjOd8-S_SlAP5o_wz1Oh38MC5T5V13PqPuZ70dbggB4bUgVaHYC4FE4XHCqP7W3xethaPc68cY9-g9f1RUvthmnEYXSRpvyaMY3iX0txZazWIS_Jg7pNTCEaWr9JCLTZd1MiLbFowPvKYGM-z-39K31OUbq5PIScy0I9OOz9joecm8KsCesA2ysPph1E7cL7Etiw5tGhCFzcdQwm8Gm6SDwj8vCEcZUkXeZJfhlS1cJtZk1sNu3KZNndevtZjRWaXi2m4WNKVxVE-nuaF7V3GWfDemh9RXxyFK8OC8aYLIqcc2pAKJM47ANVty2ll1xaCIB3q3CKdnk5fmsnzKkQI9SjKy70p9TWT-NNoYU682KG_mZo-ByEs5CvJ8w7qysmX8Xpb2I6oSJf7S3qjbqkqtXQcV5MuQ232vk7-g42CcQGL82xvRc09TuvwnmykpKHmjUaJ4U9k9zTN3g2iTdpkvl6vbnND9uG1SBaieVeFYWCT-6VdhovEiD9bvIdA7D_R7NZO8YHBt_lfBQRle_jDyLzHSlkP6kt9dYRhrc2SNMzF_4i3iEUAihbaQYvbNsGwWrHqyGofnva20pRXwc4GxOlw"
iex> JOSE.JWS.verify(jwk_ps512, signed_ps512) |> elem(0)
true

RS256, RS384, and RS512

# let's generate the 3 keys we'll use below
jwk_rs256 = JOSE.JWK.generate_key({:rsa, 1024})
jwk_rs384 = JOSE.JWK.generate_key({:rsa, 2048})
jwk_rs512 = JOSE.JWK.generate_key({:rsa, 4096})

# RS256
iex> signed_rs256 = JOSE.JWS.sign(jwk_rs256, "{}", %{ "alg" => "RS256" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJSUzI1NiJ9.e30.C0J8v5R-sEe9-g_s0SMgPorCh8VDdaZ9gLpWNm1Tn1Cv2xRph1Xn9Rzm10ZCEs84sj7kxA4v28fVShQ_P1AHN83yQ2mvstkKwsuwXxr-cludx_NLQL5CKKQtTR0ITD_pxUowjfAkBYuJv0677jUj-8lGKs1P5e2dbwW9IqFe4uE"
iex> JOSE.JWS.verify(jwk_rs256, signed_rs256) |> elem(0)
true

# RS384
iex> signed_rs384 = JOSE.JWS.sign(jwk_rs384, "{}", %{ "alg" => "RS384" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJSUzM4NCJ9.e30.fvPxeNhO0oitOsdqFmrBgpGE7Gn_NdJ1J8F5ArKon54pdHB2v30hua9wbG4V2Hr-hNAyflaBJtoGAwIpKVkfHn-IW7d06hKw_Hv0ecG-VvZr60cK2IJnHS149Htz_652egThZh1GIKRZN1IrRVlraLMozFcWP0Ojc-L-g5XjcTFafesmV0GFGfFubAiQWEiWIgNV3822L-wPe7ZGeFe5yYsZ70WMHQQ1tSuNsm5QUOUVInOThAhJ30FRTCNFgv46l4TEF9aaI9443cKAbwzd_EavD0FpvgpwEhGyNTVx0sxiCZIYUE_jN53aSaHXB82d0xwIr2-GXlr3Y-dLwERIMw"
iex> JOSE.JWS.verify(jwk_rs384, signed_rs384) |> elem(0)
true

# RS512
iex> signed_rs512 = JOSE.JWS.sign(jwk_rs512, "{}", %{ "alg" => "RS512" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJSUzUxMiJ9.e30.le2_kCnmj6Y02bl16Hh5EPqmLsFkB3YZpiEfvmA6xfdg9I3QJ5uSgOejs_HpuIbItuMFUdcqtkfW45_6YKlI7plB49iWiNnWY0PLxsvbiZaSmT4R4dOUWx9KlO_Ui5SE94XkigUoFanDTHTr9bh4NpvoIaNdi_xLdC7FYA-AqZspegRcgY-QZQv4kbD3NQJtxsEiAXk8-C8CX3lF6haRlh7s4pyAmgj7SJeElsPjhPNVZ7EduhTLZfVwiLrRmzLKQ6dJ_PrZDig1lgl9jf2NjzcsFpt6lvfrMsDdIQEGyJoh53-zXiD_ltyAZGS3pX-_tHRxoAZ1SyAPkkC4cCra6wc-03sBQPoUa26xyyhrgf4h7E2l-JqhKPXT7pJv6AbRPgKUH4prEH636gpoWQrRc-JxbDIJHR0ShdL8ssf5e-rKpcVVAZKnRI64NbSKXTg-JtDxhU9QG8JVEkHqOxSeo-VSXOoExdmm8lCfqylrw7qmDxjEwOq7TGjhINyjVaK1Op_64BWVuCzgooea6G2ZvCTIEl0-k8wY8s9VC7hxSrsgCAnpWeKpIcbLQoDIoyasG-6Qb5OuSLR367eg9NAQ8WMTbrrQkm-KLNCYvMFaxmlWzBFST2JDmIr0VH9BzXRAdfG81SymuyFA7_FdpiVYwAwEGR4Q5HYEpequ38tHu3Y"
iex> JOSE.JWS.verify(jwk_rs512, signed_rs512) |> elem(0)
true

Link to this section Summary

Functions

Compacts an expanded signed map or signed list into a binary.

Expands a compacted signed binary or list of signed binaries into a map.

Converts a binary or map into a JOSE.JWS.

Converts a binary into a JOSE.JWS.

Reads file and calls from_binary/1 to convert into a JOSE.JWS.

Converts a map into a JOSE.JWS.

Converts a :jose_jws record into a JOSE.JWS.

Generates a new JOSE.JWK based on the algorithms of the specified JOSE.JWS.

Merges map on right into map on left.

Returns the decoded payload portion of a signed binary or map without verifying the signature.

Returns the decoded protected portion of a signed binary or map without verifying the signature.

Returns the decoded signature portion of a signed binary or map without verifying the signature.

Signs the plain_text using the jwk and algorithm specified by the jws.

Signs the plain_text using the jwk and algorithm specified by the jws and adds the header to the signed map.

Converts the jws to the protected argument used by signing_input/3.

Combines payload and protected based on the "b64" setting on the jws for the signing input used by sign/3 and sign/4.

Converts a JOSE.JWS into a binary.

Calls to_binary/1 on a JOSE.JWS and then writes the binary to file.

Converts a JOSE.JWS into a map.

Converts a JOSE.JWS struct to a :jose_jws record.

Verifies the signed using the jwk.

Same as verify/2, but uses allow as a whitelist for "alg" which are allowed to verify against.

Link to this section Types

Specs

t() :: %JOSE.JWS{alg: term(), b64: term(), fields: term()}

Link to this section Functions

Compacts an expanded signed map or signed list into a binary.

iex> JOSE.JWS.compact(%{"payload" => "e30",
 "protected" => "eyJhbGciOiJIUzI1NiJ9",
 "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"})
{%{},
 "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}
iex> JOSE.JWS.compact(%{"payload" => "e30",
 "signatures" => [
  %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
    "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"},
  %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
    "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]})
{%{},
 ["eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU",
  "eyJhbGciOiJIUzI1NiJ9.e30.himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"]}}

See expand/1.

Expands a compacted signed binary or list of signed binaries into a map.

iex> JOSE.JWS.expand("eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
{%{},
 %{"payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9",
   "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}}
iex> JOSE.JWS.expand([
 "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU",
 "eyJhbGciOiJIUzI1NiJ9.e30.himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"])
{%{},
 %{"payload" => "e30",
   "signatures" => [
    %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
      "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"},
    %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
      "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}}

See compact/1.

Converts a binary or map into a JOSE.JWS.

iex> JOSE.JWS.from(%{ "alg" => "HS256" })
%JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}
iex> JOSE.JWS.from("{"alg":"HS256"}")
%JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}

Support for custom algorithms may be added by specifying a map tuple:

iex> JOSE.JWS.from({%{ alg: MyCustomAlgorithm }, %{ "alg" => "custom" }})
%JOSE.JWS{alg: {MyCustomAlgorithm, :state}, b64: :undefined, fields: %{}}

Note: MyCustomAlgorithm must implement the :jose_jws and :jose_jws_alg behaviours.

Converts a binary into a JOSE.JWS.

Reads file and calls from_binary/1 to convert into a JOSE.JWS.

Converts a map into a JOSE.JWS.

Converts a :jose_jws record into a JOSE.JWS.

Generates a new JOSE.JWK based on the algorithms of the specified JOSE.JWS.

iex> JOSE.JWS.generate_key(%{"alg" => "HS256"})
%JOSE.JWK{fields: %{"alg" => "HS256", "use" => "sig"},
 keys: :undefined,
 kty: {:jose_jwk_kty_oct,
  <<150, 71, 29, 79, 228, 32, 218, 4, 111, 250, 212, 129, 226, 173, 86, 205, 72, 48, 98, 100, 66, 68, 113, 13, 43, 60, 122, 248, 179, 44, 140, 24>>}}

Merges map on right into map on left.

See peek_payload/1.

Returns the decoded payload portion of a signed binary or map without verifying the signature.

iex> JOSE.JWS.peek_payload("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
"{}"

Returns the decoded protected portion of a signed binary or map without verifying the signature.

iex> JOSE.JWS.peek_protected("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
"{"alg":"HS256","typ":"JWT"}"

Returns the decoded signature portion of a signed binary or map without verifying the signature.

iex> JOSE.JWS.peek_signature("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
<<116, 192, 40, 140, 243, 21, 108, 91, 239, 146, 139, 152, 81, 34, 61, 3, 18, 17, 7, 24, 42, 173, 235, 80, 48, 43, 205, 23, 178, 166, 76, 117>>
Link to this function

sign(jwk, plain_text, jws)

View Source

Signs the plain_text using the jwk and algorithm specified by the jws.

iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS256" })
{%{alg: :jose_jws_alg_hmac},
 %{"payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9",
   "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}}

If the jwk has a "kid" assigned, it will be added to the "header" on the signed map:

iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw", "kty" => "oct"})
%JOSE.JWK{fields: %{"kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"},
 keys: :undefined, kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> JOSE.JWS.sign(jwk, "test", %{ "alg" => "HS256" })
{%{alg: :jose_jws_alg_hmac},
 %{"header" => %{"kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"},
   "payload" => "e30", "protected" => "eyJhbGciOiJIUzI1NiJ9",
   "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}}

A list of jwk keys can also be specified to produce a signed list:

iex> jwk1 = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> jwk2 = JOSE.JWK.from_map(%{"k" => "H-v_Nw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}}
iex> JOSE.JWS.sign([jwk1, jwk2], "{}", %{ "alg" => "HS256" })
{%{alg: :jose_jws_alg_hmac},
 %{"payload" => "e30",
   "signatures" => [
    %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
      "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"},
    %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
      "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]}}

Note: Signed maps with a "header" or other fields will have data loss when used with compact/1.

Link to this function

sign(jwk, plain_text, header, jws)

View Source

Signs the plain_text using the jwk and algorithm specified by the jws and adds the header to the signed map.

iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> JOSE.JWS.sign(jwk, "{}", %{ "test" => true }, %{ "alg" => "HS256" })
{%{alg: :jose_jws_alg_hmac},
 %{"header" => %{"test" => true}, "payload" => "e30",
   "protected" => "eyJhbGciOiJIUzI1NiJ9",
   "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"}}

If the jwk has a "kid" assigned, it will be added to the "header" on the signed map. See sign/3.

Link to this function

signing_input(payload, jws)

View Source

Converts the jws to the protected argument used by signing_input/3.

Link to this function

signing_input(payload, protected, jws)

View Source

Combines payload and protected based on the "b64" setting on the jws for the signing input used by sign/3 and sign/4.

If "b64" is set to false on the jws, the raw payload will be used:

iex> JOSE.JWS.signing_input("{}", %{ "alg" => "HS256" })
"eyJhbGciOiJIUzI1NiJ9.e30"
iex> JOSE.JWS.signing_input("{}", %{ "alg" => "HS256", "b64" => false })
"eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9.{}"

See JWS Unencoded Payload Option for more information.

Converts a JOSE.JWS into a binary.

Calls to_binary/1 on a JOSE.JWS and then writes the binary to file.

Converts a JOSE.JWS into a map.

Converts a JOSE.JWS struct to a :jose_jws record.

Verifies the signed using the jwk.

iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> JOSE.JWS.verify(jwk, "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
{true, "{}",
 %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}}

A list of jwk keys can also be specified where each key will be used to verify every entry in a signed list:

iex> jwk1 = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> jwk2 = JOSE.JWK.from_map(%{"k" => "H-v_Nw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}}
iex> JOSE.JWS.verify([jwk1, jwk2], %{"payload" => "e30",
 "signatures" => [
  %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
    "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"},
  %{"protected" => "eyJhbGciOiJIUzI1NiJ9",
    "signature" => "himAUXqVJnW2ZWOD8zaOZr0YzsA61lo48wu6-WP-Ks0"}]})
[{%JOSE.JWK{fields: %{}, keys: :undefined,
   kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}},
  [{true, "{}",
    %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}},
   {false, "{}",
    %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined,
     fields: %{}}}]},
 {%JOSE.JWK{fields: %{}, keys: :undefined,
   kty: {:jose_jwk_kty_oct, <<31, 235, 255, 55>>}},
  [{false, "{}",
    %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined, fields: %{}}},
   {true, "{}",
    %JOSE.JWS{alg: {:jose_jws_alg_hmac, :HS256}, b64: :undefined,
     fields: %{}}}]}]
Link to this function

verify_strict(jwk, allow, signed)

View Source

Same as verify/2, but uses allow as a whitelist for "alg" which are allowed to verify against.

If the detected algorithm is not present in allow, then false is returned.

iex> jwk = JOSE.JWK.from(%{"k" => "qUg4Yw", "kty" => "oct"})
%JOSE.JWK{fields: %{}, keys: :undefined,
 kty: {:jose_jwk_kty_oct, <<169, 72, 56, 99>>}}
iex> signed_hs256 = JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS256" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"
iex> signed_hs512 = JOSE.JWS.sign(jwk, "{}", %{ "alg" => "HS512" }) |> JOSE.JWS.compact |> elem(1)
"eyJhbGciOiJIUzUxMiJ9.e30.DN_JCks5rzQiDJJ15E6uJFskAMw-KcasGINKK_4S8xKo7W6tZ-a00ZL8UWOWgE7oHpcFrYnvSpNRldAMp19iyw"
iex> JOSE.JWS.verify_strict(jwk, ["HS256"], signed_hs256) |> elem(0)
true
iex> JOSE.JWS.verify_strict(jwk, ["HS256"], signed_hs512) |> elem(0)
false
iex> JOSE.JWS.verify_strict(jwk, ["HS256", "HS512"], signed_hs512) |> elem(0)
true