Class: JOSE::JWS
- Inherits:
-
Struct
- Object
- Struct
- JOSE::JWS
- Defined in:
- lib/jose/jws.rb
Overview
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 by setting the JOSE_UNSECURED_SIGNING
environment variable or by using JOSE.unsecured_signing=.
Strict Verification Recommended
JOSE::JWS.verify_strict is recommended over JOSE::JWS.verify 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= option to be enabled):
"Ed25519"
"Ed25519ph"
"Ed448"
"Ed448ph"
"EdDSA"
"ES256"
"ES384"
"ES512"
"HS256"
"HS384"
"HS512"
"PS256"
"PS384"
"PS512"
"RS256"
"RS384"
"RS512"
"none"
(disabled by default, enable with JOSE.unsecured_signing=)
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
signed_ed25519 = JOSE::JWS.sign(jwk_ed25519, "{}", { "alg" => "Ed25519" }).compact
# => "eyJhbGciOiJFZDI1NTE5In0.e30.xyg2LTblm75KbLFJtROZRhEgAFJdlqH9bhx8a9LO1yvLxNLhO9fLqnFuU3ojOdbObr8bsubPkPqUfZlPkGHXCQ"
JOSE::JWS.verify(jwk_ed25519, signed_ed25519).first
# => true
# Ed25519ph
signed_ed25519ph = JOSE::JWS.sign(jwk_ed25519ph, "{}", { "alg" => "Ed25519ph" }).compact
# => "eyJhbGciOiJFZDI1NTE5cGgifQ.e30.R3je4TTxQvoBOupIKkel_b8eW-G8KaWmXuC14NMGSCcHCTalURtMmVqX2KbcIpFBeI-OKP3BLHNIpt1keKveDg"
JOSE::JWS.verify(jwk_ed25519ph, signed_ed25519ph).first
# => 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
signed_ed448 = JOSE::JWS.sign(jwk_ed448, "{}", { "alg" => "Ed448" }).compact
# => "eyJhbGciOiJFZDQ0OCJ9.e30.UlqTx962FvZP1G5pZOrScRXlAB0DJI5dtZkknNTm1E70AapkONi8vzpvKd355czflQdc7uyOzTeAz0-eLvffCKgWm_zebLly7L3DLBliynQk14qgJgz0si-60mBFYOIxRghk95kk5hCsFpxpVE45jRIA"
JOSE::JWS.verify(jwk_ed448, signed_ed448).first
# => true
# Ed448ph
signed_ed448ph = JOSE::JWS.sign(jwk_ed448ph, "{}", { "alg" => "Ed448ph" }).compact
# => "eyJhbGciOiJFZDQ0OHBoIn0.e30._7wxQF8Am-Fg3E-KgREXBv3Gr2vqLM6ja_7hs6kA5EakCrJVQ2QiAHrr4NriLABmiPbVd7F7IiaAApyR3Ud4ak3lGcHVxSyksjJjvBUbKnSB_xkT6v_QMmx27hV08JlxskUkfvjAG0-yKGC8BXoT9R0A"
JOSE::JWS.verify(jwk_ed448ph, signed_ed448ph).first
# => 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
signed_eddsa = JOSE::JWS.sign(jwk_eddsa, "{}", { "alg" => "EdDSA" }).compact
# => "eyJhbGciOiJFZERTQSJ9.e30.rhb5ZY7MllNbW9q-SCn_NglhYtaRGMXEUDj6BvJjltOt19tEI_1wFrVK__jL91i9hO7WtVqRH_OfHiilnO1CAQ"
JOSE::JWS.verify(jwk_eddsa, signed_eddsa).first
# => true
ES256, ES384, and ES512
# let's generate the 3 keys we'll use below
jwk_es256 = JOSE::JWK.generate_key([:ec, "P-256"])
jwk_es384 = JOSE::JWK.generate_key([:ec, "P-384"])
jwk_es512 = JOSE::JWK.generate_key([:ec, "P-521"])
# ES256
signed_es256 = JOSE::JWS.sign(jwk_es256, "{}", { "alg" => "ES256" }).compact
# => "eyJhbGciOiJFUzI1NiJ9.e30.nb7cEQQuIi2NgcP5A468FHGG8UZg8gWZjloISyVIwNh3X6FiTTFZsvc0mL3RnulWoNJzKF6xwhae3botI1LbRg"
JOSE::JWS.verify(jwk_es256, signed_es256).first
# => true
# ES384
signed_es384 = JOSE::JWS.sign(jwk_es384, "{}", { "alg" => "ES384" }).compact
# => "eyJhbGciOiJFUzM4NCJ9.e30.-2kZkNe66y2SprhgvvtMa0qBrSb2imPhMYkbi_a7vx-vpEHuVKsxCpUyNVLe5_CXaHWhHyc2rNi4uEfU73c8XQB3e03rg_JOj0H5XGIGS5G9f4RmNMSCiYGwqshLSDFI"
JOSE::JWS.verify(jwk_es384, signed_es384).first
# => true
# ES512
signed_es512 = JOSE::JWS.sign(jwk_es512, "{}", { "alg" => "ES512" }).compact
# => "eyJhbGciOiJFUzUxMiJ9.e30.AOIw4KTq5YDu6QNrAYKtFP8R5IljAbhqXuPK1dUARPqlfc5F3mM0kmSh5KOVNHDmdCdapBv0F3b6Hl6glFDPlxpiASuSWtvvs9K8_CRfSkEzvToj8wf3WLGOarQHDwYXtlZoki1zMPGeWABwafTZNQaItNSpqYd_P9GtN0XM3AALdua0"
JOSE::JWS.verify(jwk_es512, signed_es512).first
# => 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
signed_hs256 = JOSE::JWS.sign(jwk_hs256, "{}", { "alg" => "HS256" }).compact
# => "eyJhbGciOiJIUzI1NiJ9.e30.r2JwwMFHECoDZlrETLT-sgFT4qN3w0MLee9MrgkDwXs"
JOSE::JWS.verify(jwk_hs256, signed_hs256).first
# => true
# HS384
signed_hs384 = JOSE::JWS.sign(jwk_hs384, "{}", { "alg" => "HS384" }).compact
# => "eyJhbGciOiJIUzM4NCJ9.e30.brqQFXXM0XtMWDdKf0foEQcvK18swcoDkxBqCPeed_IO317_tisr60H2mz79SlNR"
JOSE::JWS.verify(jwk_hs384, signed_hs384).first
# => true
# HS512
signed_hs512 = JOSE::JWS.sign(jwk_hs512, "{}", { "alg" => "HS512" }).compact
# => "eyJhbGciOiJIUzUxMiJ9.e30.ge1JYomO8Fyl6sgxLbc4g3AMPbaMHLmeTl0jrUYAJZSloN9j4VyhjucX8d-RWIlMjzdG0xyklw53k1-kaTlRVQ"
JOSE::JWS.verify(jwk_hs512, signed_hs512).first
# => true
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
signed_ps256 = JOSE::JWS.sign(jwk_ps256, "{}", { "alg" => "PS256" }).compact
# => "eyJhbGciOiJQUzI1NiJ9.e30.RY5A3rG2TjmdlARE57eSSSFE6plkuQPKLKsyqz3WrqKRWZgSrvROACRTzoGyrx1sNvQEZJLZ-xVhrFvP-80Q14XzQbPfYLubvn-2wcMNCmih3OVQNVtFdFjA5U2NG-sF-SWAUmm9V_DvMShFGG0qHxLX7LqT83lAIgEulgsytb0xgOjtJObBru5jLjN_uEnc7fCfnxi3my1GAtnrs9NiKvMfuIVlttvOORDFBTO2aFiCv1F-S6Xgj16rc0FGImG0x3amQcmFAD9g41KY0_KsCXgUfoiVpC6CqO6saRC4UDykks91B7Nuoxjsm3nKWa_4vKh9QJy-V8Sf0gHxK58j8Q"
JOSE::JWS.verify(jwk_ps256, signed_ps256).first
# => true
# PS384
signed_ps384 = JOSE::JWS.sign(jwk_ps384, "{}", { "alg" => "PS384" }).compact
# => "eyJhbGciOiJQUzM4NCJ9.e30.xmYVenIhi75hDMy3bnL6WVpVlTzYmO1ejOZeq9AkSjkp_STrdIp6uUEs9H_y7CLD9LrGYYHDNDl9WmoH6cn95WZT9KJgAVNFFYd8owY6JUHGKU1jUbLkptAgvdphVpWZ1C5fVCRt4vmp8K9f6jy3er9jCBNjl9gSBdmToFwYdXI26ZKSBjfoVm2tFFQIOThye4YQWCWHbzSho6J7d5ATje72L30zDvWXavJ-XNvof5Tkju4WQQB-ukFoqTw4yV8RVwCa-DX61I1hNrq-Zr75_iWmHak3GqNkg5ACBEjDtvtyxJizqy9KINKSlbB9jGztiWoEiXZ6wJ5sSJ6ZrSFJuQVEmns_dLqzpSHEFkWfczEV_gj9Eu_EXwMp9YQlQ3GktfXaz-mzH_jUaLmudEUskQGCiR92gK9KR6_ROQPJfD54Tkqdh6snwg6y17k8GdlTc5qMM3V84q3R6zllmhrRhV1Dlduc0MEqKcsQSX_IX21-sfiVMIcUsW73dIPXVZI2jsNlEHKqwMjWdSfjYUf3YApxSGERU3u4lRS3F0yRrZur8KWS3ToilApjg0cNg9jKas8g8C8ZPgGFYM6StVxUnXRmsJILDnsZMIPjbUDAPHhB0DwLwOB7OqGUBcItX-zwur1OVnHR7aIh1DbfWfyTIml8VIhYfGfazgXfgQVcGEM"
JOSE::JWS.verify(jwk_ps384, signed_ps384).first
# => true
# PS512
signed_ps512 = JOSE::JWS.sign(jwk_ps512, "{}", { "alg" => "PS512" }).compact
# => "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"
JOSE::JWS.verify(jwk_ps512, signed_ps512).first
# => 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
signed_rs256 = JOSE::JWS.sign(jwk_rs256, "{}", { "alg" => "RS256" }).compact
# => "eyJhbGciOiJSUzI1NiJ9.e30.C0J8v5R-sEe9-g_s0SMgPorCh8VDdaZ9gLpWNm1Tn1Cv2xRph1Xn9Rzm10ZCEs84sj7kxA4v28fVShQ_P1AHN83yQ2mvstkKwsuwXxr-cludx_NLQL5CKKQtTR0ITD_pxUowjfAkBYuJv0677jUj-8lGKs1P5e2dbwW9IqFe4uE"
JOSE::JWS.verify(jwk_rs256, signed_rs256).first
# => true
# RS384
signed_rs384 = JOSE::JWS.sign(jwk_rs384, "{}", { "alg" => "RS384" }).compact
# => "eyJhbGciOiJSUzM4NCJ9.e30.fvPxeNhO0oitOsdqFmrBgpGE7Gn_NdJ1J8F5ArKon54pdHB2v30hua9wbG4V2Hr-hNAyflaBJtoGAwIpKVkfHn-IW7d06hKw_Hv0ecG-VvZr60cK2IJnHS149Htz_652egThZh1GIKRZN1IrRVlraLMozFcWP0Ojc-L-g5XjcTFafesmV0GFGfFubAiQWEiWIgNV3822L-wPe7ZGeFe5yYsZ70WMHQQ1tSuNsm5QUOUVInOThAhJ30FRTCNFgv46l4TEF9aaI9443cKAbwzd_EavD0FpvgpwEhGyNTVx0sxiCZIYUE_jN53aSaHXB82d0xwIr2-GXlr3Y-dLwERIMw"
JOSE::JWS.verify(jwk_rs384, signed_rs384).first
# => true
# RS512
signed_rs512 = JOSE::JWS.sign(jwk_rs512, "{}", { "alg" => "RS512" }).compact
# => "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"
JOSE::JWS.verify(jwk_rs512, signed_rs512).first
# => true
Defined Under Namespace
Modules: ALG Classes: ALG_ECDSA, ALG_EDDSA, ALG_HMAC, ALG_RSA_PKCS1_V1_5, ALG_RSA_PSS, ALG_none
Instance Attribute Summary collapse
-
#alg ⇒ Object
Returns the value of attribute alg.
-
#b64 ⇒ Object
Returns the value of attribute b64.
-
#fields ⇒ Object
Returns the value of attribute fields.
Class Method Summary collapse
-
.compact(map) ⇒ JOSE::SignedBinary
Compacts an expanded signed map or signed list into a binary.
-
.expand(binary) ⇒ JOSE::SignedMap
Expands a compacted signed binary or list of signed binaries into a map.
-
.from(object, modules = {}) ⇒ JOSE::JWS+
Converts a binary or map into a JOSE::JWS.
-
.from_binary(object, modules = {}) ⇒ JOSE::JWS+
Converts a binary into a JOSE::JWS.
-
.from_file(file, modules = {}) ⇒ JOSE::JWS
Reads file and calls JWS.from_binary to convert into a JOSE::JWS.
-
.from_map(object, modules = {}) ⇒ JOSE::JWS+
Converts a map into a JOSE::JWS.
- .generate_key(jws, modules = {}) ⇒ JOSE::JWK+
-
.merge(left, right) ⇒ JOSE::JWS
Merges map on right into map on left.
-
.peek_payload(signed) ⇒ String
Returns the decoded payload portion of a signed binary or map without verifying the signature.
-
.peek_protected(signed) ⇒ JOSE::Map
Returns the decoded protected portion of a signed binary or map without verifying the signature.
-
.peek_signature(signed) ⇒ String
Returns the decoded signature portion of a signed binary or map without verifying the signature.
-
.sign(jwk, plain_text, jws, header = nil) ⇒ JOSE::SignedMap
Signs the
plain_text
using thejwk
and algorithm specified by thejws
. -
.signing_input(payload, jws, protected_binary = nil) ⇒ String
Combines
payload
andprotected_binary
based on the"b64"
setting on thejws
for the signing input used by JOSE::JWS.sign. -
.to_binary(jws) ⇒ String+
Converts a JOSE::JWS into a binary.
-
.to_file(jws, file) ⇒ Fixnum
Calls JWS.to_binary on a JOSE::JWS and then writes the binary to
file
. -
.to_map(jws) ⇒ JOSE::Map+
Converts a JOSE::JWS into a map.
-
.verify(jwk, signed) ⇒ [Boolean, String, JOSE::JWS]
Verifies the
signed
using thejwk
. -
.verify_strict(jwk, allow, signed) ⇒ [Boolean, String, (JOSE::JWS, JOSE::Map)]
Same as JOSE::JWS.verify, but uses
allow
as a whitelist for"alg"
which are allowed to verify against.
Instance Method Summary collapse
- #generate_key ⇒ JOSE::JWK
-
#merge(object) ⇒ JOSE::JWS
Merges object into current map.
-
#sign(jwk, plain_text, header = nil) ⇒ JOSE::SignedMap
Signs the
plain_text
using thejwk
and algorithm specified by thejws
. -
#signing_input(payload, protected_binary = nil) ⇒ Object
Combines
payload
andprotected_binary
based on the"b64"
setting on thejws
for the signing input used by JOSE::JWS.sign. -
#to_binary ⇒ String
Converts a JOSE::JWS into a binary.
-
#to_file(file) ⇒ Fixnum
Calls #to_binary on a JOSE::JWS and then writes the binary to
file
. -
#to_map ⇒ JOSE::Map
Converts a JOSE::JWS into a map.
-
#verify(jwk, plain_text, signature, protected_binary = nil) ⇒ [Boolean, String, JOSE::JWS]
Verifies the
signature
using thejwk
,plain_text
, andprotected_binary
.
Instance Attribute Details
#alg ⇒ Object
Returns the value of attribute alg
234 235 236 |
# File 'lib/jose/jws.rb', line 234 def alg @alg end |
#b64 ⇒ Object
Returns the value of attribute b64
234 235 236 |
# File 'lib/jose/jws.rb', line 234 def b64 @b64 end |
#fields ⇒ Object
Returns the value of attribute fields
234 235 236 |
# File 'lib/jose/jws.rb', line 234 def fields @fields end |
Class Method Details
.compact(map) ⇒ JOSE::SignedBinary
388 389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/jose/jws.rb', line 388 def self.compact(map) if map.is_a?(Hash) or map.is_a?(JOSE::Map) return JOSE::SignedBinary.new([ map['protected'] || '', '.', map['payload'] || '', '.', map['signature'] || '' ].join) else raise ArgumentError, "'map' must be a Hash or a JOSE::Map" end end |
.expand(binary) ⇒ JOSE::SignedMap
Expands a compacted signed binary or list of signed binaries into a map.
JOSE::JWS.("eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
# => JOSE::SignedMap[
# "protected" => "eyJhbGciOiJIUzI1NiJ9",
# "payload" => "e30",
# "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"]
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 |
# File 'lib/jose/jws.rb', line 414 def self.(binary) if binary.is_a?(String) if binary.count('.') == 2 and (parts = binary.split('.', 3)).length == 3 protected_binary, payload, signature = parts return JOSE::SignedMap[ 'payload' => payload, 'protected' => protected_binary, 'signature' => signature ] else raise ArgumentError, "'binary' is not a valid signed String" end else raise ArgumentError, "'binary' must be a String" end end |
.from(object, modules = {}) ⇒ JOSE::JWS+
Converts a binary or map into a JOSE::JWS.
JOSE::JWS.from({ "alg" => "HS256" })
# => #<struct JOSE::JWS
# alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
# b64=nil,
# fields=JOSE::Map[]>
JOSE::JWS.from("{\"alg\":\"HS256\"}")
# => #<struct JOSE::JWS
# alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>,
# b64=nil,
# fields=JOSE::Map[]>
Support for custom algorithms may be added by specifying :alg
under modules
:
JOSE::JWS.from({ "alg" => "custom" }, { alg: MyCustomAlgorithm })
# => #<struct JOSE::JWS
# alg=#<MyCustomAlgorithm:0x007f8c5419ff68>,
# b64=nil,
# fields=JOSE::Map[]>
Note: MyCustomAlgorithm
must implement the methods mentioned in other alg modules.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'lib/jose/jws.rb', line 265 def self.from(object, modules = {}) case object when JOSE::Map, Hash return from_map(object, modules) when String return from_binary(object, modules) when JOSE::JWS return object when Array return object.map { |obj| from(obj, modules) } else raise ArgumentError, "'object' must be a Hash, String, JOSE::JWS, or Array" end end |
.from_binary(object, modules = {}) ⇒ JOSE::JWS+
Converts a binary into a JOSE::JWS.
284 285 286 287 288 289 290 291 292 293 |
# File 'lib/jose/jws.rb', line 284 def self.from_binary(object, modules = {}) case object when String return from_map(JOSE.decode(object), modules) when Array return object.map { |obj| from_binary(obj, modules) } else raise ArgumentError, "'object' must be a String or Array" end end |
.from_file(file, modules = {}) ⇒ JOSE::JWS
Reads file and calls from_binary to convert into a JOSE::JWS.
299 300 301 |
# File 'lib/jose/jws.rb', line 299 def self.from_file(file, modules = {}) return from_binary(File.binread(file), modules) end |
.from_map(object, modules = {}) ⇒ JOSE::JWS+
Converts a map into a JOSE::JWS.
307 308 309 310 311 312 313 314 315 316 |
# File 'lib/jose/jws.rb', line 307 def self.from_map(object, modules = {}) case object when JOSE::Map, Hash return from_fields(JOSE::JWS.new(nil, nil, JOSE::Map.new(object)), modules) when Array return object.map { |obj| from_map(obj, modules) } else raise ArgumentError, "'object' must be a Hash or Array" end end |
.generate_key(jws, modules = {}) ⇒ JOSE::JWK+
Generates a new JOSE::JWK based on the algorithms of the specified JOSE::JWS.
JOSE::JWS.generate_key({"alg" => "HS256"})
# => #<struct JOSE::JWK
# keys=nil,
# kty=
# #<struct JOSE::JWK::KTY_oct
# oct="\x96G\x1DO\xE4 \xDA\x04o\xFA\xD4\x81\xE2\xADV\xCDH0bdBDq\r+<z\xF8\xB3,\x8C\x18">,
# fields=JOSE::Map["alg" => "HS256", "use" => "sig"]>
444 445 446 447 448 449 450 |
# File 'lib/jose/jws.rb', line 444 def self.generate_key(jws, modules = {}) if jws.is_a?(Array) return from(jws, modules).map { |obj| obj.generate_key } else return from(jws, modules).generate_key end end |
.merge(left, right) ⇒ JOSE::JWS
Merges map on right into map on left.
464 465 466 |
# File 'lib/jose/jws.rb', line 464 def self.merge(left, right) return from(left).merge(right) end |
.peek_payload(signed) ⇒ String
493 494 495 496 497 498 |
# File 'lib/jose/jws.rb', line 493 def self.peek_payload(signed) if signed.is_a?(String) signed = (signed) end return JOSE.urlsafe_decode64(signed['payload']) end |
.peek_protected(signed) ⇒ JOSE::Map
508 509 510 511 512 513 |
# File 'lib/jose/jws.rb', line 508 def self.peek_protected(signed) if signed.is_a?(String) signed = (signed) end return JOSE::Map.new(JOSE.decode(JOSE.urlsafe_decode64(signed['protected']))) end |
.peek_signature(signed) ⇒ String
Returns the decoded signature portion of a signed binary or map without verifying the signature.
JOSE::JWS.peek_signature("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.dMAojPMVbFvvkouYUSI9AxIRBxgqretQMCvNF7KmTHU")
# => "t\xC0(\x8C\xF3\x15l[\xEF\x92\x8B\x98Q\"=\x03\x12\x11\a\x18*\xAD\xEBP0+\xCD\x17\xB2\xA6Lu"
523 524 525 526 527 528 |
# File 'lib/jose/jws.rb', line 523 def self.peek_signature(signed) if signed.is_a?(String) signed = (signed) end return JOSE.urlsafe_decode64(signed['signature']) end |
.sign(jwk, plain_text, jws, header = nil) ⇒ JOSE::SignedMap
Signs the plain_text
using the jwk
and algorithm specified by the jws
.
jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
# => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
JOSE::JWS.sign(jwk, "{}", { "alg" => "HS256" })
# => JOSE::SignedMap[
# "signature" => "5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU",
# "protected" => "eyJhbGciOiJIUzI1NiJ9",
# "payload" => "e30"]
If the jwk
has a "kid"
assigned, it will be added to the "header"
on the signed map:
jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw", "kty" => "oct"})
# => #<struct JOSE::JWK
# keys=nil,
# kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">,
# fields=JOSE::Map["kid" => "eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"]>
JOSE::JWS.sign(jwk, "test", { "alg" => "HS256" })
# => JOSE::SignedMap[
# "signature" => "ZEBxtZ4SAW5hYyT7CKxH8dqynTAg-Y24QjkudQMaA_M",
# "header" => {"kid"=>"eyHC48MN26DvoBpkaudvOVXuI5Sy8fKMxQMYiRWmjFw"},
# "protected" => "eyJhbGciOiJIUzI1NiJ9",
# "payload" => "dGVzdA"]
Note: Signed maps with a "header"
or other fields will have data loss when used with JOSE::JWS.compact.
562 563 564 |
# File 'lib/jose/jws.rb', line 562 def self.sign(jwk, plain_text, jws, header = nil) return from(jws).sign(jwk, plain_text, header) end |
.signing_input(payload, jws, protected_binary = nil) ⇒ String
Combines payload
and protected_binary
based on the "b64"
setting on the jws
for the signing input used by JOSE::JWS.sign.
If "b64"
is set to false
on the jws
, the raw payload
will be used:
JOSE::JWS.signing_input("{}", { "alg" => "HS256" })
# => "eyJhbGciOiJIUzI1NiJ9.e30"
JOSE::JWS.signing_input("{}", { "alg" => "HS256", "b64" => false })
# => "eyJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9.{}"
595 596 597 |
# File 'lib/jose/jws.rb', line 595 def self.signing_input(payload, jws, protected_binary = nil) return from(jws).signing_input(payload, protected_binary) end |
.to_binary(jws) ⇒ String+
Converts a JOSE::JWS into a binary.
323 324 325 326 327 328 329 |
# File 'lib/jose/jws.rb', line 323 def self.to_binary(jws) if jws.is_a?(Array) return from(jws).map { |obj| obj.to_binary } else return from(jws).to_binary end end |
.to_file(jws, file) ⇒ Fixnum
341 342 343 |
# File 'lib/jose/jws.rb', line 341 def self.to_file(jws, file) return from(jws).to_file(file) end |
.to_map(jws) ⇒ JOSE::Map+
Converts a JOSE::JWS into a map.
355 356 357 358 359 360 361 |
# File 'lib/jose/jws.rb', line 355 def self.to_map(jws) if jws.is_a?(Array) return from(jws).map { |obj| obj.to_map } else return from(jws).to_map end end |
.verify(jwk, signed) ⇒ [Boolean, String, JOSE::JWS]
Verifies the signed
using the jwk
.
jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
# => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
JOSE::JWS.verify(jwk, "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU")
# => => [true, "{}", #<struct JOSE::JWS alg=#<struct JOSE::JWS::ALG_HMAC hmac=OpenSSL::Digest::SHA256>, b64=nil, fields=JOSE::Map[]>]
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
# File 'lib/jose/jws.rb', line 620 def self.verify(jwk, signed) if signed.is_a?(String) signed = JOSE::JWS.(signed) end if signed.is_a?(Hash) signed = JOSE::SignedMap.new(signed) end if signed.is_a?(JOSE::Map) and signed['payload'].is_a?(String) and signed['protected'].is_a?(String) and signed['signature'].is_a?(String) jws = from_binary(JOSE.urlsafe_decode64(signed['protected'])) signature = JOSE.urlsafe_decode64(signed['signature']) plain_text = JOSE.urlsafe_decode64(signed['payload']) return jws.verify(jwk, plain_text, signature, signed['protected']) else raise ArgumentError, "'signed' is not a valid signed String, Hash, or JOSE::Map" end end |
.verify_strict(jwk, allow, signed) ⇒ [Boolean, String, (JOSE::JWS, JOSE::Map)]
Same as JOSE::JWS.verify, 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.
jwk = JOSE::JWK.from({"k" => "qUg4Yw", "kty" => "oct"})
# => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="\xA9H8c">, fields=JOSE::Map[]>
signed_hs256 = JOSE::JWS.sign(jwk, "{}", { "alg" => "HS256" }).compact
# => "eyJhbGciOiJIUzI1NiJ9.e30.5paAJxaOXSqRUIXrP_vJXUZu2SCBH-ojgP4D6Xr6GPU"
signed_hs512 = JOSE::JWS.sign(jwk, "{}", { "alg" => "HS512" }).compact
# => "eyJhbGciOiJIUzUxMiJ9.e30.DN_JCks5rzQiDJJ15E6uJFskAMw-KcasGINKK_4S8xKo7W6tZ-a00ZL8UWOWgE7oHpcFrYnvSpNRldAMp19iyw"
JOSE::JWS.verify_strict(jwk, ["HS256"], signed_hs256).first
# => true
JOSE::JWS.verify_strict(jwk, ["HS256"], signed_hs512).first
# => false
JOSE::JWS.verify_strict(jwk, ["HS256", "HS512"], signed_hs512).first
# => true
673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 |
# File 'lib/jose/jws.rb', line 673 def self.verify_strict(jwk, allow, signed) if signed.is_a?(String) signed = JOSE::JWS.(signed) end if signed.is_a?(Hash) signed = JOSE::SignedMap.new(signed) end if signed.is_a?(JOSE::Map) and signed['payload'].is_a?(String) and signed['protected'].is_a?(String) and signed['signature'].is_a?(String) protected_map = JOSE.decode(JOSE.urlsafe_decode64(signed['protected'])) plain_text = JOSE.urlsafe_decode64(signed['payload']) if allow.member?(protected_map['alg']) jws = from_map(protected_map) signature = JOSE.urlsafe_decode64(signed['signature']) return jws.verify(jwk, plain_text, signature, signed['protected']) else return false, plain_text, protected_map end else raise ArgumentError, "'signed' is not a valid signed String, Hash, or JOSE::Map" end end |
Instance Method Details
#generate_key ⇒ JOSE::JWK
456 457 458 |
# File 'lib/jose/jws.rb', line 456 def generate_key return alg.generate_key(fields) end |
#merge(object) ⇒ JOSE::JWS
Merges object into current map.
471 472 473 474 475 476 477 478 479 480 481 482 483 |
# File 'lib/jose/jws.rb', line 471 def merge(object) object = case object when JOSE::Map, Hash object when String JOSE.decode(object) when JOSE::JWS object.to_map else raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWS" end return JOSE::JWS.from_map(self.to_map.merge(object)) end |
#sign(jwk, plain_text, header = nil) ⇒ JOSE::SignedMap
Signs the plain_text
using the jwk
and algorithm specified by the jws
.
572 573 574 575 576 577 578 |
# File 'lib/jose/jws.rb', line 572 def sign(jwk, plain_text, header = nil) protected_binary = JOSE.urlsafe_encode64(to_binary) payload = JOSE.urlsafe_encode64(plain_text) signing_input = signing_input(plain_text, protected_binary) signature = JOSE.urlsafe_encode64(alg.sign(jwk, signing_input)) return signature_to_map(payload, protected_binary, header, jwk, signature) end |
#signing_input(payload, protected_binary = nil) ⇒ Object
Combines payload
and protected_binary
based on the "b64"
setting on the jws
for the signing input used by JOSE::JWS.sign.
601 602 603 604 605 606 607 |
# File 'lib/jose/jws.rb', line 601 def signing_input(payload, protected_binary = nil) if b64 == true or b64.nil? payload = JOSE.urlsafe_encode64(payload) end protected_binary ||= JOSE.urlsafe_encode64(to_binary) return [protected_binary, '.', payload].join end |
#to_binary ⇒ String
Converts a JOSE::JWS into a binary.
333 334 335 |
# File 'lib/jose/jws.rb', line 333 def to_binary return JOSE.encode(to_map) end |
#to_file(file) ⇒ Fixnum
Calls #to_binary on a JOSE::JWS and then writes the binary to file
.
348 349 350 |
# File 'lib/jose/jws.rb', line 348 def to_file(file) return File.binwrite(file, to_binary) end |
#to_map ⇒ JOSE::Map
Converts a JOSE::JWS into a map.
365 366 367 368 369 370 371 |
# File 'lib/jose/jws.rb', line 365 def to_map map = alg.to_map(fields) if b64 == false or b64 == true map = map.put('b64', b64) end return map end |
#verify(jwk, plain_text, signature, protected_binary = nil) ⇒ [Boolean, String, JOSE::JWS]
Verifies the signature
using the jwk
, plain_text
, and protected_binary
.
645 646 647 648 649 |
# File 'lib/jose/jws.rb', line 645 def verify(jwk, plain_text, signature, protected_binary = nil) protected_binary ||= JOSE.urlsafe_encode64(to_binary) signing_input = signing_input(plain_text, protected_binary) return alg.verify(jwk, signing_input, signature), plain_text, self end |