Class: JOSE::JWE

Inherits:
Struct
  • Object
show all
Defined in:
lib/jose/jwe.rb

Overview

JWE stands for JSON Web Encryption which is defined in RFC 7516.

Key Derivation Algorithms

The following key derivation algorithms for the "alg" field are currently supported by JOSE::JWE (some may need the JOSE.crypto_fallback= option to be enabled):

  • "A128GCMKW"
  • "A192GCMKW"
  • "A256GCMKW"
  • "A128KW"
  • "A192KW"
  • "A256KW"
  • "dir"
  • "ECDH-ES"
  • "ECDH-ES+A128KW"
  • "ECDH-ES+A192KW"
  • "ECDH-ES+A256KW"
  • "PBES2-HS256+A128KW"
  • "PBES2-HS384+A192KW"
  • "PBES2-HS512+A256KW"
  • "RSA1_5"
  • "RSA-OAEP"
  • "RSA-OAEP-256"

Encryption Algorithms

The following encryption algorithms for the "enc" field are currently supported by JOSE::JWE (some may need the JOSE.crypto_fallback= option to be enabled):

  • "A128CBC-HS256"
  • "A192CBC-HS384"
  • "A256CBC-HS512"
  • "A128GCM"
  • "A192GCM"
  • "A256GCM"

Compression Algorithms

The following compression algorithms for the "zip" field are currently supported by JOSE::JWE:

  • "DEF"

Key Derivation Examples

All of the examples below will use "enc" set to "A128GCM", "A192GCM", or "A256GCM" depending on the derived key size.

The octet key used will typically be all zeroes of the required size in the form of ([0]*16).pack('C*') (for a 128-bit key).

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

# octet keys we'll use below
jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
jwk_oct192 = JOSE::JWK.from_oct(([0]*24).pack('C*'))
jwk_oct256 = JOSE::JWK.from_oct(([0]*32).pack('C*'))
jwk_secret = JOSE::JWK.from_oct("secret")

# EC keypairs we'll use below
jwk_ec256_alice_sk = JOSE::JWK.generate_key([:ec, "P-256"])
jwk_ec256_alice_pk = JOSE::JWK.to_public(jwk_ec256_alice_sk)
jwk_ec256_bob_sk   = JOSE::JWK.generate_key([:ec, "P-256"])
jwk_ec256_bob_pk   = JOSE::JWK.to_public(jwk_ec256_bob_sk)

# X25519 keypairs we'll use below
jwk_x25519_alice_sk = JOSE::JWK.generate_key([:okp, :X25519])
jwk_x25519_alice_pk = JOSE::JWK.to_public(jwk_x25519_alice_sk)
jwk_x25519_bob_sk   = JOSE::JWK.generate_key([:okp, :X25519])
jwk_x25519_bob_pk   = JOSE::JWK.to_public(jwk_x25519_bob_sk)

# X448 keypairs we'll use below
jwk_x448_alice_sk = JOSE::JWK.generate_key([:okp, :X448])
jwk_x448_alice_pk = JOSE::JWK.to_public(jwk_x448_alice_sk)
jwk_x448_bob_sk   = JOSE::JWK.generate_key([:okp, :X448])
jwk_x448_bob_pk   = JOSE::JWK.to_public(jwk_x448_bob_sk)

# RSA keypairs we'll use below
jwk_rsa_sk = JOSE::JWK.generate_key([:rsa, 4096])
jwk_rsa_pk = JOSE::JWK.to_public(jwk_rsa_sk)

A128GCMKW, A192GCMKW, and A256GCMKW

# A128GCMKW
encrypted_a128gcmkw = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "A128GCMKW", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJBMTI4R0NNS1ciLCJlbmMiOiJBMTI4R0NNIiwiaXYiOiJzODNFNjhPNjhsWlM5ZVprIiwidGFnIjoieF9Ea2M5dm1LMk5RQV8tU2hvTkFRdyJ9.8B2qX8fVEa-s61RsZXqkCg.J7yJ8sKLbUlzyor6.FRs.BhBwImTv9B14NwVuxmfU6A"
JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128gcmkw).first
# => "{}"

# A192GCMKW
encrypted_a192gcmkw = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "A192GCMKW", "enc" => "A192GCM" }).compact
# => "eyJhbGciOiJBMTkyR0NNS1ciLCJlbmMiOiJBMTkyR0NNIiwiaXYiOiIxMkduZWQyTDB6NE5LZG83IiwidGFnIjoiM0thbG9iaER1Wmx5dE1YSjhjcXhZZyJ9.jJC4E1c6augIhvGDp3fquRfO-mnnud4F.S2NkKNGxBKTsCnKo.gZA.MvfhqSTeEN75H8HDyvfzRQ"
JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192gcmkw).first
# => "{}"

# A256GCMKW
encrypted_a256gcmkw = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "A256GCMKW", "enc" => "A256GCM" }).compact
# => "eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMjU2R0NNIiwiaXYiOiJHU3lFMTBLQURxZTczNUMzIiwidGFnIjoiR3dVbDJCbXRNWlVseDlXNEMtY0tQZyJ9.sSsbFw9z8WTkzBLvPMywSedTXXygFxfP9g5U2qpzUX8.eiVFfe7iojfK0AXb._v8.YVfk9dNrtS7wxbGqCVge-g"
JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256gcmkw).first
# => "{}"

A128KW, A192KW, and A256KW

# A128KW
encrypted_a128kw = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "A128KW", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw"
JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128kw).first
# => "{}"

# A192KW
encrypted_a192kw = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "A192KW", "enc" => "A192GCM" }).compact
# => "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIn0.edpvNrztlNADbkwfq5YBJgqFBSH_Znv1Y1uXKNQ_13w.yCkEYTCPOKH6CoxZ.siw.zP_ZM9OEeX1FIdFjqNawtQ"
JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192kw).first
# => "{}"

# A256KW
encrypted_a256kw = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "A256KW", "enc" => "A256GCM" }).compact
# => "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.OvAhC1a2BoP_2SMIiZXwIHWPoIkD-Cosgp3nlpiTs8ySUBPfPzwG1g.4GeackYJbuBksAWA.HPE.vG0sGC2kuklH5xk8KXhyNA"
JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256kw).first
# => "{}"

dir

The "dir" key derivation algorithm is essentially just a pass-through to the underlying "enc" algorithm.

The "encrypted_key" is not included in the protected header, so the key must be fully known by both parties.

# dir
encrypted_dir = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..HdRR8O0kk_SvOjAS.rxo.JTMPGPKZZKVNlWV0RexsmQ"
JOSE::JWE.block_decrypt(jwk_oct128, encrypted_dir).first
# => "{}"

ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, and ECDH-ES+A256KW

The "ECDH-ES" key derivation algorithm does not include the "encrypted_key" field in the protected header, similar to how "dir" functions.

The size of the generated key is dependent on the "enc" setting (for example, "A128GCM" will generate a 128-bit key, "A256GCM" a 256-bit key, etc).

# ECDH-ES with EC keypairs
encrypted_ecdhes_ec256_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjQ4UVUzUTBDeVN4d0piRXdXckpyWVhscDg4X2RWcEhUeHE0YXZjNjZoNVEiLCJ5IjoiWnpxcklOdE1NeEh4US1RQjcyUk1jZGxtRHNPSXdsS2hNcVZtX2dZV0MxNCJ9fQ..UssNrY5qEeFdluZY.R6g.32nlr0wHF2TwfL1UnBtIow"
JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhes_ec256_alice2bob).first
# => "{}"

# ECDH-ES with X25519 keypairs
encrypted_ecdhes_x25519_alice2bob = JOSE::JWE.block_encrypt([jwk_x25519_bob_pk, jwk_x25519_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZ0g3TjJwT0duenZfd0tBLUhqREZKTlVSZVhfdG05XzdiMkZSUjI3cXFYcyJ9fQ..T-0q42FPCUy3hlla.MHU.9TNP2jG5bN1vSvaesijdww"
JOSE::JWE.block_decrypt([jwk_x25519_alice_pk, jwk_x25519_bob_sk], encrypted_ecdhes_x25519_alice2bob).first
# => "{}"

# ECDH-ES with X448 keypairs
encrypted_ecdhes_x448_alice2bob = JOSE::JWE.block_encrypt([jwk_x448_bob_pk, jwk_x448_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYNDQ4Iiwia3R5IjoiT0tQIiwieCI6ImFFaHZISGxFM2V1Y3lsY0RNNzBMd1paY2dDRk9acXExNWM3YXZNMjJkcWZIUEtja1FZNmo3LXFfM19kMGI1cGVWZEFoNVoyQWZIWSJ9fQ..T-UNE-wOApuRH71r.Uj8.l8bIfhC1UPAPVWBV3wkc6A"
JOSE::JWE.block_decrypt([jwk_x448_alice_pk, jwk_x448_bob_sk], encrypted_ecdhes_x448_alice2bob).first
# => "{}"

When decrypting with any of the "ECDH-ES" related algorithms, the other party's public key is recommended, but not required for decryption (the embedded Ephemeral Public Key will be used instead):

# decrypting the X448 example with and without the public key specified
JOSE::JWE.block_decrypt([jwk_x448_alice_pk, jwk_x448_bob_sk], encrypted_ecdhes_x448_alice2bob).first
# => "{}"
JOSE::JWE.block_decrypt(jwk_x448_bob_sk, encrypted_ecdhes_x448_alice2bob).first
# => "{}"

The "ECDH-ES+A128KW", "ECDH-ES+A192KW", and "ECDH-ES+A256KW" key derivation algorithms do include the "encrypted_key" and the suffix after "ECDH-ES+" determines the key size (so "ECDH-ES+A128KW" computes a 128-bit key).

# ECDH-ES+A128KW with EC keypairs
encrypted_ecdhesa128kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A128KW", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.ZwuqXf7svd3SH0M-XYLjWz5JsN6xX03C.l8tt83EJjy86IovL.i5A.nw05dPUA0a18xdtvmHbhHA"
JOSE::JWE::block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa128kw_alice2bob).first
# => "{}"

# ECDH-ES+A192KW with EC keypairs
encrypted_ecdhesa192kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A192KW", "enc" => "A192GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.S9LZ1i_Lua_if4I83WcaCQ9yT5qqPI_NhCFR7tMiZDQ.kG3taKEjGeKDRTzs.H1s.oVGBFP63z4gd3e-R2d1cmA"
JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa192kw_alice2bob).first
# => "{}"

# ECDH-ES+A256KW with EC keypairs
encrypted_ecdhesa256kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A256KW", "enc" => "A256GCM" }).compact
# => "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.4KWy1-vRiJyNINF6mWYbUPPTVNG9ADfvvfpSDbddPTftz7GmUHUsuQ.IkRhtGH23R-9dFF3.9yk.RnALhnqWMHWCZFxqc-DU4A"
JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa256kw_alice2bob).first
# => "{}"

See JOSE::JWK.box_encrypt for generating an Ephemeral Public Key based on the same curve as the supplied other party key in the same step.

PBES2-HS256+A128KW, PBES2-HS384+A192KW, and PBES2-HS512+A256KW

# PBES2-HS256+A128KW
encrypted_pbes2hs256a128kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS256+A128KW", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMnMiOiJRR0laNTlzbjRnQThySHBWYjFrSkd3In0.8WMQ0fysLiHU8AjpjkcqJGpYe53VRf2s.vVEb2ZtKmtPIw8M-.Cmg.GCjDtdKV6khqEuyZy2gUxw"
JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs256a128kw).first
# => "{}"

# PBES2-HS384+A192KW
encrypted_pbes2hs384a192kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS384+A192KW", "enc" => "A192GCM" }).compact
# => "eyJhbGciOiJQQkVTMi1IUzM4NCtBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIiwicDJjIjo2MTQ0LCJwMnMiOiJKSDRjZ0hlNTZiU0prZ1d6VktpWWJCb0FzWEJBY1A1NiJ9.Ck5GvgXxmyac3jzs0lRavoRh6tI9nEs3lYkx8sdDzGw.IdxaPATMkQ8FYiYQ.uHk.rDU6ltWsTsw9vuvA73bgJQ"
JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs384a192kw).first
# => "{}"

# PBES2-HS512+A256KW
encrypted_pbes2hs512a256kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS512+A256KW", "enc" => "A256GCM" }).compact
# => "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJ6YWRiMVNmT1F4V1gyTHJrSVgwWDFGM2QzNlBIdUdxRVFzUDVhbWVnTk00In0.6SUVO9sSevqZrZ5yPX-JvJNJrzfIQeTTzrkWBHEqHra1_AITtwEe0A.0AaF_3ZlJOkRlqgb.W8I.jFWob73QTn52IFSIPEWHFA"
JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs512a256kw).first
# => "{}"

The "p2s" and "p2i" fields may also be specified to control the Salt and Iterations of the PBES2 Key Derivation Function, respectively.

The default Salt is a randomly generated binary the same length of bytes as the key wrap (for example, "PBES2-HS256+A128KW" will generate a 16-byte Salt).

The default Iterations is 32 times the number of bits specified by the key wrap (for example, "PBES2-HS256+A128KW" will have 4096 Iterations).

# let's setup the JWE header
iterations = 8192
salt = ([0]*32).pack('C*') # all zero 256-bit salt, for example usage only
jwe = {
  "alg" => "PBES2-HS256+A128KW",
  "enc" => "A128GCM",
  "p2i" => iterations,
  "p2s" => JOSE.urlsafe_encode64(salt)
}
# PBES2-HS256+A128KW
encrypted_pbes2 = JOSE::JWE.block_encrypt(jwk_secret, "{}", jwe).compact
# => "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMmkiOjgxOTIsInAycyI6IkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEifQ.I7wcBmg7O_rOWpg1aak7wQWX84YtED6k.Rgh3f6Kzl5SZ1z7x.FNo.eyK1ySx4SGR-xC2EYNySQA"
JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2).first
# => "{}"

RSA1_5, RSA-OAEP, and RSA-OAEP-256

# RSA1_5
encrypted_rsa1_5 = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA1_5", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4R0NNIn0.NlndPTqULN1vArshEzfEXY0nHCf4ubsTK9iHAeIxL85fReYrYG8EDB2_IirUneavvHSa-hsVLXNzBu0F9OY3TRFAIuJ8Jt1tqZZEhHZ97vzTEIjdlPNctGNI11-mhNCJ0doSvx9T4ByngaAFtJnRoR2cqbJkJFGja60fHtO0CfKLW5XzPf0NAhr8Tof-5IJfbNpMcC_LdCItJ6i8cuj4i5pG_CikOKDrNzbaBP72200_kl_-YaLDMA4tVb2YjWksY5Vau0Hz16QvI9QwDIcIDLYPAlTlDrU7s_FfmO_89S9Z69-lc_OBG7x2CYzIhB-0wzx753nZRl_WNJKi1Ya_AV552FEqVUhR-SuKcyrTA9OwkKC2JoL3lFqsCL9jkZkBrVREQlT0cxNI_AInyx5FHNLBbdtkz0JQbvzMJ854RP0V_eTlI5u8DZ42aOTRMBLHPi-4gP0J_CGWyKDQreXEEF6LSuLJb1cGk-NX1Vd85aARstQPuOoy7pWJjPvBEKEib70fjkUuMA0Atid-5BusQLKc1H-D6c5HIFH0DgYtXhN6AtQ_fmqw1F_X1JrGnYiYGzJCD2hh0Yt2UJZoCuHlPKk8aM5L3lNU3AISb1soSQl3hfX8Skb817ffC7jYezdhZc12cRNzOPAYqJYjN2eDlQhx-gpFjVzc-W1bFG8Yijo.grliT3M1iZ48aSY9.F4Y.pBRqIGZ4Q_fI1kmeAggvRg"
JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsa1_5).first
# => "{}"

# RSA-OAEP
encrypted_rsaoaep = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA-OAEP", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.YZfGKTTU2KuvwIMpSYadbNmGzWIbLrwRYD8JvZAWkvcnFeky09S04VadRNPXmCBSl4EF1K7oBm0fiYXuvNbLFNKYT_Jo_y6Lb-XsP--BZKaEcq6wIdZ4-xTJ7YYX5dfco_cMknZLG8W2sQRwtWopisn9NyzSpfGNlYqeJqjpoJy0qnO8yZeEYeadwoVF9-XZfYwvMjEt7HORqBIPF1JIaOYTQ-LQBvya6XYhOR7dkSnuCZ_ITGW5ZbPvzOILSMW_3Ixe78ncfO2gxF6AiLh02oTLsOSrF9xDlJvuU0k1TdkNWtGroeP_WVbXEO7O_GI5LVW-cDzoVm5ZCQs2Df0018-qDxFyY9xhKS9aNDi_btiarstXMSz3EkOfPhWR_IzlVyUkYnzs3GS993gKLQ0Tk-ipvOT9Bcw9VTLLK3-f5YSkf51IA---hPFlxVlboH9bmTXlT4JzSbErQEYp3JuXjOP7FQn0OPko5Utqbbm41XBEJhUpBNhjrBGDspsMxML_eJdyzBgA5UyNfdCEQ2vM1pCegxG_hSKAhCKVNn71wW4O_y_eqUcoyhjB7HtVxiF29jzNUKF-y14171L4-mxsIpixaM1ofnayWMiherVP0Wz2MXkzWB0AUv8c3kNEJIh3oeyrczWwzpmeCh1Bq7-J4D6aaFjyGFcm-03_QZmfwho.ymxveKBeRuaZ8HzD.3H4.6oKLh2NouhPGpO1dmA-tTg"
JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep).first
# => "{}"

# RSA-OAEP-256
encrypted_rsaoaep256 = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA-OAEP-256", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIn0.OW9Hy9qpOIgVueODQXcWIUw_-Sm3UFGtxosyOAaI6JUQFt8q-iEtKkUp4NHrOlczO6tP5t8zRKdNXFfCm9QZk6F9PsSO-NzE2-DV1ANAMck-CDfGTK0mwG5U_KZwlObSgU0gxf87K49Wuno1rWlHWzJb__C_hCJXi_aQW17tLmbuTpJMkB0NTCKX3y6QaxvynP98jqwMJT6uGmE3AeuZYhPGzAOWbltbWyw-TqWqyLJirAUY_fvDNsKt1TDrTd9216TK5y7RQeUtdGfbuYK9lt2TIwfh9ycAHd7SANH_YJc2cKYa3e6CgqnQAjVpbhpogBz5sz5HaK95XYbXOdnYyHQ00gS44YquiQCvX331UgEWnthtmYwDZfnCxTkPydafGOBsjaagGvV2tQtxUKW3JmVChF97bNj5lQZ7rAkyooxx-k3IMT0005x6_74O5tXGN5fb7oyT3Mx_NZ5dKzlYAA_V8oOpNslaFhV5K5Q_-hRkUsEPWdaD5s2uS9Z7l7ot39CzzTKDj65f2eCTWFReFKOjhabCL4ZiFXbElB3dA3y5FdxXPAfe6N31G9ynalx1JIcrEaRb8sdqk6U6uC3s3DpkoRSnp3osBJOxxuk_Lgb-ZM9d8UuRVj4W78-qjfX_lcG1RlRmlYoDIU03ly0UfRWi-7HmpPECrGTsGZEfULg.J-txckmMXEi-bZVh.Rbw.D7UpSkticmDCGiNyLVggLg"
JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep256).first
# => "{}"

Encryption Examples

All of the examples below will use "alg" set to "dir" passing the key directly to the Encryption Algorithm.

The octet key used will typically be all zeroes of the required size in the form of ([0]*16).pack('C*') (for a 128-bit key).

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

# octet keys we'll use below
jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
jwk_oct192 = JOSE::JWK.from_oct(([0]*24).pack('C*'))
jwk_oct256 = JOSE::JWK.from_oct(([0]*32).pack('C*'))
jwk_oct384 = JOSE::JWK.from_oct(([0]*48).pack('C*'))
jwk_oct512 = JOSE::JWK.from_oct(([0]*64).pack('C*'))

A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512

# A128CBC-HS256
encrypted_a128cbchs256 = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "dir", "enc" => "A128CBC-HS256" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..bxps64-UIQoFvhkjr05e9A.HrtJ3AqrqJ4f5PHjGseHYw.kopJoTDxk34IVhheoToLSA"
JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a128cbchs256).first
# => "{}"

# A192CBC-HS384
encrypted_a192cbchs384 = JOSE::JWE.block_encrypt(jwk_oct384, "{}", { "alg" => "dir", "enc" => "A192CBC-HS384" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0..3zSCHwvHrcxsNyssIgEBRA.XB70tUoQZlnOgY5ygMxfKA.Avl7Z8jCpShh3_iTcPcU3Woh6E9ykNyB"
JOSE::JWE.block_decrypt(jwk_oct384, encrypted_a192cbchs384).first
# => "{}"

# A256CBC-HS512
encrypted_a256cbchs512 = JOSE::JWE.block_encrypt(jwk_oct512, "{}", { "alg" => "dir", "enc" => "A256CBC-HS512" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..mqMhkWAMF7HmW_Nu1ERUzQ.bzd-tmykuru0Lu_rsNZ2ow.mlOFO8JcC_UJ35TsZgiUeEwAjRDs6cwfN7Umyzm7mmY"
JOSE::JWE.block_decrypt(jwk_oct512, encrypted_a256cbchs512).first
# => "{}"

A128GCM, A192GCM, and A256GCM

# A128GCM
encrypted_a128gcm = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..pPF4SbzGZwxS1J-M.Ic0.qkHuC-hOO44HPlykBJLSsA"
JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128gcm).first
# => "{}"

# A192GCM
encrypted_a192gcm = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "dir", "enc" => "A192GCM" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyR0NNIn0..muNgk2GFW9ATwqqZ.bvE.gYvC0G6DAodJdyrUqLw7Iw"
JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192gcm).first
# => "{}"

# A256GCM
encrypted_a256gcm = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "dir", "enc" => "A256GCM" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..rDTJhd5ja5pDAYtn.PrM.MQdLgiVXQsG_cLas93ZEHw"
JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256gcm).first
# => "{}"

Compression Examples

All of the examples below will use "alg" set to "dir" passing the key directly to the Encryption Algorithm ("enc" is set to "A128GCM").

The octet key used will typically be all zeroes of the required size in the form of ([0]*16).pack('C*') (for a 128-bit key).

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

# octet keys we'll use below
jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))

DEF

# DEF
encrypted_def = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM", "zip" => "DEF" }).compact
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0..Vvr0vlKWE9rAJ8CR.UpOz7w10Uc9pMg.Pctxzz0ijPSOY8zyRcbjww"
JOSE::JWE.block_decrypt(jwk_oct128, encrypted_def).first
# => "{}"

Defined Under Namespace

Modules: ALG, ENC, ZIP Classes: ALG_AES_GCM_KW, ALG_AES_KW, ALG_C20P_KW, ALG_ECDH_ES, ALG_PBES2, ALG_RSA, ALG_XC20P_KW, ALG_dir, ENC_AES_CBC_HMAC, ENC_AES_GCM, ENC_C20P, ENC_XC20P, ZIP_DEF

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#algObject

Returns the value of attribute alg

Returns:

  • (Object)

    the current value of alg



389
390
391
# File 'lib/jose/jwe.rb', line 389

def alg
  @alg
end

#encObject

Returns the value of attribute enc

Returns:

  • (Object)

    the current value of enc



389
390
391
# File 'lib/jose/jwe.rb', line 389

def enc
  @enc
end

#fieldsObject

Returns the value of attribute fields

Returns:

  • (Object)

    the current value of fields



389
390
391
# File 'lib/jose/jwe.rb', line 389

def fields
  @fields
end

#zipObject

Returns the value of attribute zip

Returns:

  • (Object)

    the current value of zip



389
390
391
# File 'lib/jose/jwe.rb', line 389

def zip
  @zip
end

Class Method Details

.block_decrypt(key, encrypted) ⇒ [String, JOSE::JWE]

Decrypts the encrypted binary or map using the key.

jwk = JOSE::JWK.from({"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"})
# => #<struct JOSE::JWK
#  keys=nil,
#  kty=#<struct JOSE::JWK::KTY_oct oct="I9j\xB4\x83\xA1X\x98\xE8Vv#R<E-\x9E\xA87Z\x01\xEFD\x00Dd\x93Y\x0F\xF9^\x03">,
#  fields=JOSE::Map[]>
JOSE::JWE.block_decrypt(jwk, "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA")
# => ["{}",
#  #<struct JOSE::JWE
#   alg=#<struct JOSE::JWE::ALG_dir direct=true>,
#   enc=
#    #<struct JOSE::JWE::ENC_AES_CBC_HMAC
#     cipher_name="aes-128-cbc",
#     bits=256,
#     cek_len=32,
#     iv_len=16,
#     enc_len=16,
#     mac_len=16,
#     tag_len=16,
#     hmac=OpenSSL::Digest::SHA256>,
#   zip=nil,
#   fields=JOSE::Map[]>]

Parameters:

Returns:

See Also:



567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
# File 'lib/jose/jwe.rb', line 567

def self.block_decrypt(key, encrypted)
  if encrypted.is_a?(String)
    encrypted = JOSE::JWE.expand(encrypted)
  end
  if encrypted.is_a?(Hash)
    encrypted = JOSE::EncryptedMap.new(encrypted)
  end
  if encrypted.is_a?(JOSE::Map) and encrypted['ciphertext'].is_a?(String) and encrypted['encrypted_key'].is_a?(String) and encrypted['iv'].is_a?(String) and encrypted['protected'].is_a?(String) and encrypted['tag'].is_a?(String)
    jwe = from_binary(JOSE.urlsafe_decode64(encrypted['protected']))
    encrypted_key = JOSE.urlsafe_decode64(encrypted['encrypted_key'])
    iv = JOSE.urlsafe_decode64(encrypted['iv'])
    cipher_text = JOSE.urlsafe_decode64(encrypted['ciphertext'])
    cipher_tag = JOSE.urlsafe_decode64(encrypted['tag'])
    if encrypted['aad'].is_a?(String)
      concat_aad = [encrypted['protected'], '.', encrypted['aad']].join
      return jwe.block_decrypt(key, concat_aad, cipher_text, cipher_tag, encrypted_key, iv), jwe
    else
      return jwe.block_decrypt(key, encrypted['protected'], cipher_text, cipher_tag, encrypted_key, iv), jwe
    end
  else
    raise ArgumentError, "'encrypted' is not a valid encrypted String, Hash, or JOSE::Map"
  end
end

.block_encrypt(key, block, jwe, cek = nil, iv = nil) ⇒ JOSE::EncryptedMap

Encrypts the block using the key, cek, iv, and algorithm specified by the jwe.

jwk = JOSE::JWK.from({"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"})
# => #<struct JOSE::JWK
#  keys=nil,
#  kty=#<struct JOSE::JWK::KTY_oct oct="I9j\xB4\x83\xA1X\x98\xE8Vv#R<E-\x9E\xA87Z\x01\xEFD\x00Dd\x93Y\x0F\xF9^\x03">,
#  fields=JOSE::Map[]>
JOSE::JWE.block_encrypt(jwk, "{}", { "alg" => "dir", "enc" => "A128CBC-HS256" })
# => JOSE::EncryptedMap[
#  "tag" => "tSGaAlI2xiMThBZ9XTW2AQ",
#  "ciphertext" => "c2T5O6WafTCKEX5R_9D-LQ",
#  "encrypted_key" => "",
#  "iv" => "U56zV4sW4bOovxeXcz7fUg",
#  "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"]

Parameters:

Returns:

See Also:



628
629
630
# File 'lib/jose/jwe.rb', line 628

def self.block_encrypt(key, block, jwe, cek = nil, iv = nil)
  return from(jwe).block_encrypt(key, block, cek, iv)
end

.compact(map) ⇒ JOSE::EncryptedBinary

Compacts an expanded encrypted map into a binary.

JOSE::JWE.compact({
  "ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA",
  "encrypted_key" => "",
  "iv" => "jBt5tTa1Q0N3uFPEkf30MQ",
  "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
  "tag" => "gMWOAmhZSq9ksHCZm6VSoA"
})
# => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA"

Parameters:

Returns:

See Also:



691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
# File 'lib/jose/jwe.rb', line 691

def self.compact(map)
  if map.is_a?(Hash) or map.is_a?(JOSE::Map)
    if map.has_key?('aad')
      raise ArgumentError, "'map' with 'aad' cannot be compacted"
    end
    return JOSE::EncryptedBinary.new([
      map['protected'] || '',
      '.',
      map['encrypted_key'] || '',
      '.',
      map['iv'] || '',
      '.',
      map['ciphertext'] || '',
      '.',
      map['tag'] || ''
    ].join)
  else
    raise ArgumentError, "'map' must be a Hash or a JOSE::Map"
  end
end

.compress(plain_text, jwe) ⇒ String

Compresses the plain_text using the "zip" algorithm specified by the jwe.

JOSE::JWE.compress("{}", { "alg" => "dir", "zip" => "DEF" })
# => "x\x9C\xAB\xAE\x05\x00\x01u\x00\xF9"

Parameters:

Returns:

  • (String)


721
722
723
# File 'lib/jose/jwe.rb', line 721

def self.compress(plain_text, jwe)
  return from(jwe).compress(plain_text)
end

.expand(binary) ⇒ JOSE::EncryptedMap

Expands a compacted encrypted binary into a map.

JOSE::JWE.expand("eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA")
# => JOSE::EncryptedMap[
#  "tag" => "gMWOAmhZSq9ksHCZm6VSoA",
#  "ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA",
#  "encrypted_key" => "",
#  "iv" => "jBt5tTa1Q0N3uFPEkf30MQ",
#  "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"]

Parameters:

Returns:

See Also:



751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
# File 'lib/jose/jwe.rb', line 751

def self.expand(binary)
  if binary.is_a?(String)
    parts = binary.split('.')
    if parts.length == 5
      protected_binary, encrypted_key, initialization_vector, cipher_text, authentication_tag = parts
      return JOSE::EncryptedMap[
        'ciphertext'    => cipher_text,
        'encrypted_key' => encrypted_key,
        'iv'            => initialization_vector,
        'protected'     => protected_binary,
        'tag'           => authentication_tag
      ]
    else
      raise ArgumentError, "'binary' is not a valid encrypted String"
    end
  else
    raise ArgumentError, "'binary' must be a String"
  end
end

.from(object, modules = {}) ⇒ JOSE::JWE+

Converts a binary or map into a JOSE::JWE.

JOSE::JWE.from({ "alg" => "dir" })
# => #<struct JOSE::JWE
#  alg=#<struct JOSE::JWE::ALG_dir direct=true>,
#  enc=nil,
#  zip=nil,
#  fields=JOSE::Map[]>
JOSE::JWE.from("{\"alg\":\"dir\"}")
# => #<struct JOSE::JWE
#  alg=#<struct JOSE::JWE::ALG_dir direct=true>,
#  enc=nil,
#  zip=nil,
#  fields=JOSE::Map[]>

There are 3 keys which can have custom modules defined for them:

For example:

JOSE::JWE.from({ "alg" => "dir", "zip" => "custom" }, { zip: MyCustomCompress })
# => #<struct JOSE::JWE
#  alg=#<struct JOSE::JWE::ALG_dir direct=true>,
#  enc=nil,
#  zip=#<MyCustomAlgorithm:0x007f8c5419ff68>,
#  fields=JOSE::Map[]>

Parameters:

Returns:



428
429
430
431
432
433
434
435
436
437
438
439
440
441
# File 'lib/jose/jwe.rb', line 428

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::JWE
    return object
  when Array
    return object.map { |obj| from(obj, modules) }
  else
    raise ArgumentError, "'object' must be a Hash, String, JOSE::JWE, or Array"
  end
end

.from_binary(object, modules = {}) ⇒ JOSE::JWE+

Converts a binary into a JOSE::JWE.

Parameters:

  • object (String, Array<String>)
  • modules (Hash) (defaults to: {})

Returns:



447
448
449
450
451
452
453
454
455
456
# File 'lib/jose/jwe.rb', line 447

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::JWE

Reads file and calls from_binary to convert into a JOSE::JWE.

Parameters:

  • file (String)
  • modules (Hash) (defaults to: {})

Returns:



462
463
464
# File 'lib/jose/jwe.rb', line 462

def self.from_file(file, modules = {})
  return from_binary(File.binread(file), modules)
end

.from_map(object, modules = {}) ⇒ JOSE::JWE+

Converts a map into a JOSE::JWE.

Parameters:

Returns:



470
471
472
473
474
475
476
477
478
479
# File 'lib/jose/jwe.rb', line 470

def self.from_map(object, modules = {})
  case object
  when JOSE::Map, Hash
    return from_fields(JOSE::JWE.new(nil, 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(jwe, modules = {}) ⇒ JOSE::JWK+

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

JOSE::JWE.generate_key({"alg" => "dir", "enc" => "A128GCM"})
# => #<struct JOSE::JWK
#  keys=nil,
#  kty=#<struct JOSE::JWK::KTY_oct oct="\xED4\x19\x14\xA1\xDB\xB5\xCF*\xAE\xEE7\xDA\xDE\xA9\xCB">,
#  fields=JOSE::Map["alg" => "dir", "enc" => "A128GCM", "use" => "enc"]>

Parameters:

Returns:



783
784
785
786
787
788
789
# File 'lib/jose/jwe.rb', line 783

def self.generate_key(jwe, modules = {})
  if jwe.is_a?(Array)
    return from(jwe, modules).map { |obj| obj.generate_key }
  else
    return from(jwe, modules).generate_key
  end
end

.key_decrypt(key, encrypted_key, jwe) ⇒ String

Decrypts the encrypted_key using the key and the "alg" and "enc" specified by the jwe.

# let's define our jwk and encrypted_key
jwk = JOSE::JWK.from({"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"})
enc = [27,123,126,121,56,105,105,81,140,76,30,2,14,92,231,174,203,196,110,204,57,238,248,73].pack('C*')

JOSE::JWE.key_decrypt(jwk, enc, { "alg" => "A128KW", "enc" => "A128CBC-HS256" })
# => "\x86R\x0F\xB0\xB5s\xAD\x13\r,\xBD\xB9\xBB}\x1C\xF0"

Parameters:

Returns:

  • (String)

See Also:



814
815
816
# File 'lib/jose/jwe.rb', line 814

def self.key_decrypt(key, encrypted_key, jwe)
  return from(jwe).key_decrypt(key, encrypted_key)
end

.key_encrypt(key, decrypted_key, jwe) ⇒ [String, JOSE::JWE]

Encrypts the decrypted_key using the key and the "alg" and "enc" specified by the jwe.

!!!ruby # let's define our jwk and encrypted_key jwk = JOSE::JWK.from(=> "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct") cek = [134,82,15,176,181,115,173,19,13,44,189,185,187,125,28,240].pack('C*')

JOSE::JWE.key_encrypt(jwk, cek, { "alg" => "A128KW", "enc" => "A128CBC-HS256" }) # => ["\e{~y8iiQ\x8CL\x1E\x02\x0E\\xE7\xAE\xCB\xC4n\xCC9\xEE\xF8I", # #, # enc= # #, # zip=nil, # fields=JOSE::Map[]>]

Parameters:

Returns:

See Also:



856
857
858
# File 'lib/jose/jwe.rb', line 856

def self.key_encrypt(key, decrypted_key, jwe)
  return from(jwe).key_encrypt(key, decrypted_key)
end

.merge(left, right) ⇒ JOSE::JWE

Merges map on right into map on left.

Parameters:

Returns:



876
877
878
# File 'lib/jose/jwe.rb', line 876

def self.merge(left, right)
  return from(left).merge(right)
end

.next_cek(key, jwe) ⇒ [String, JOSE::JWE]

Returns the next cek using the jwk and the "alg" and "enc" specified by the jwe.

# let's define our jwk
jwk = JOSE::JWK.from({"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"}) # JOSE::JWK.generate_key([:oct, 16])

JOSE::JWE.next_cek(jwk, { "alg" => "A128KW", "enc" => "A128CBC-HS256" })
# => ["%S\x8B\xA5,\x17\xA3\xBA\xFF\x9B\xB7\x11\xDC\xD3P\xF7\xEF\x95\xC25\x86)\xFE\xB0\x00\xF7B&\xD9\xFCR\xE9",
#  #<struct JOSE::JWE
#   alg=#<struct JOSE::JWE::ALG_AES_KW bits=128>,
#   enc=
#    #<struct JOSE::JWE::ENC_AES_CBC_HMAC
#     cipher_name="aes-128-cbc",
#     bits=256,
#     cek_len=32,
#     iv_len=16,
#     enc_len=16,
#     mac_len=16,
#     tag_len=16,
#     hmac=OpenSSL::Digest::SHA256>,
#   zip=nil,
#   fields=JOSE::Map[]>]

# when using the "dir" algorithm, the jwk itself will be used
JOSE::JWE.next_cek(jwk, { "alg" => "dir", "enc" => "A128GCM" })
# => ["\x89\xD3\x7Fc'\x98f\xA1\x04\xEC\x19){\x18@\xD9",
#  #<struct JOSE::JWE
#   alg=#<struct JOSE::JWE::ALG_dir direct=true>,
#   enc=#<struct JOSE::JWE::ENC_AES_GCM cipher_name="aes-128-gcm", bits=128, cek_len=16, iv_len=12>,
#   zip=nil,
#   fields=JOSE::Map[]>]

Parameters:

Returns:



932
933
934
# File 'lib/jose/jwe.rb', line 932

def self.next_cek(key, jwe)
  return from(jwe).next_cek(key)
end

.next_iv(jwe) ⇒ String

Returns the next iv the "alg" and "enc" specified by the jwe.

# typically just returns random bytes for the specified "enc" algorithm
JOSE::JWE.next_iv({ "alg" => "dir", "enc" => "A128CBC-HS256" }).bytesize * 8
# => 128
JOSE::JWE.next_iv({ "alg" => "dir", "enc" => "A128GCM" }).bytesize * 8
# => 96

Parameters:

Returns:

  • (String)


958
959
960
# File 'lib/jose/jwe.rb', line 958

def self.next_iv(jwe)
  return from(jwe).next_iv
end

.peek_ciphertext(encrypted) ⇒ String

Returns the decoded ciphertext portion of a encrypted binary or map without decrypting the ciphertext.

JOSE::JWE.peek_ciphertext("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
# => "&a"

Parameters:

Returns:

  • (String)


977
978
979
980
981
982
# File 'lib/jose/jwe.rb', line 977

def self.peek_ciphertext(encrypted)
  if encrypted.is_a?(String)
    encrypted = expand(encrypted)
  end
  return JOSE.urlsafe_decode64(encrypted['ciphertext'])
end

.peek_encrypted_key(encrypted) ⇒ String

Returns the decoded encrypted key portion of a encrypted binary or map without decrypting the ciphertext.

JOSE::JWE.peek_encrypted_key("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
# => "\xB7\x8F\xC5o\x89\x02\x97\xA0\\KW\x17\x9D\x1E\x0F\xE1x&\xFA<\x156\xC1e"

Parameters:

Returns:

  • (String)


992
993
994
995
996
997
# File 'lib/jose/jwe.rb', line 992

def self.peek_encrypted_key(encrypted)
  if encrypted.is_a?(String)
    encrypted = expand(encrypted)
  end
  return JOSE.urlsafe_decode64(encrypted['encrypted_key'])
end

.peek_iv(encrypted) ⇒ String

Returns the decoded initialization vector portion of a encrypted binary or map without decrypting the ciphertext.

JOSE::JWE.peek_iv("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
# => "E\xEA\xCA}h\xF3\xAA\xABK!J\xF3"

Parameters:

Returns:

  • (String)


1007
1008
1009
1010
1011
1012
# File 'lib/jose/jwe.rb', line 1007

def self.peek_iv(encrypted)
  if encrypted.is_a?(String)
    encrypted = expand(encrypted)
  end
  return JOSE.urlsafe_decode64(encrypted['iv'])
end

.peek_protected(encrypted) ⇒ JOSE::Map

Returns the decoded protected portion of a encrypted binary or map without decrypting the ciphertext.

JOSE::JWE.peek_protected("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
# => JOSE::Map["enc" => "A128GCM", "alg" => "A128KW"]

Parameters:

Returns:



1022
1023
1024
1025
1026
1027
# File 'lib/jose/jwe.rb', line 1022

def self.peek_protected(encrypted)
  if encrypted.is_a?(String)
    encrypted = expand(encrypted)
  end
  return JOSE::Map.new(JOSE.decode(JOSE.urlsafe_decode64(encrypted['protected'])))
end

.peek_tag(encrypted) ⇒ String

Returns the decoded tag portion of a encrypted binary or map without decrypting the ciphertext.

JOSE::JWE.peek_tag("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
# => "d:U\x95j>i\x06\f\xE6V\xBDz\xC8p["

Parameters:

Returns:

  • (String)


1037
1038
1039
1040
1041
1042
# File 'lib/jose/jwe.rb', line 1037

def self.peek_tag(encrypted)
  if encrypted.is_a?(String)
    encrypted = expand(encrypted)
  end
  return JOSE.urlsafe_decode64(encrypted['tag'])
end

.to_binary(jwe) ⇒ String+

Converts a JOSE::JWE into a binary.

Parameters:

Returns:

  • (String, Array<String>)


486
487
488
489
490
491
492
# File 'lib/jose/jwe.rb', line 486

def self.to_binary(jwe)
  if jwe.is_a?(Array)
    return from(jwe).map { |obj| obj.to_binary }
  else
    return from(jwe).to_binary
  end
end

.to_file(jwe, file) ⇒ Fixnum

Calls to_binary on a JOSE::JWE and then writes the binary to file.

Parameters:

Returns:

  • (Fixnum)

    bytes written



504
505
506
# File 'lib/jose/jwe.rb', line 504

def self.to_file(jwe, file)
  return from(jwe).to_file(file)
end

.to_map(jwe) ⇒ JOSE::Map+

Converts a JOSE::JWE into a map.

Parameters:

Returns:



518
519
520
521
522
523
524
# File 'lib/jose/jwe.rb', line 518

def self.to_map(jwe)
  if jwe.is_a?(Array)
    return from(jwe).map { |obj| obj.to_map }
  else
    return from(jwe).to_map
  end
end

.uncompress(cipher_text, jwe) ⇒ String

Uncompresses the cipher_text using the "zip" algorithm specified by the jwe.

JOSE::JWE.uncompress([120,156,171,174,5,0,1,117,0,249].pack('C*'), { "alg" => "dir", "zip" => "DEF" })
# => "{}"

Parameters:

Returns:

  • (String)

See Also:



1054
1055
1056
# File 'lib/jose/jwe.rb', line 1054

def self.uncompress(cipher_text, jwe)
  return from(jwe).uncompress(cipher_text)
end

Instance Method Details

#block_decrypt(key, aad, cipher_text, cipher_tag, encrypted_key, iv) ⇒ [String, JOSE::JWE]

Decrypts the cipher_text binary using the key, aad, cipher_tag, encrypted_key, and iv.

Parameters:

Returns:

See Also:



600
601
602
603
# File 'lib/jose/jwe.rb', line 600

def block_decrypt(key, aad, cipher_text, cipher_tag, encrypted_key, iv)
  cek = key_decrypt(key, encrypted_key)
  return uncompress(enc.block_decrypt([aad, cipher_text, cipher_tag], cek, iv))
end

#block_encrypt(key, block, cek = nil, iv = nil) ⇒ JOSE::EncryptedMap

Encrypts the block binary using the key, cek, and iv.

Parameters:

  • key (JOSE::JWK, [JOSE::JWK, JOSE::JWK])
  • block (String, [String, String])
  • cek (String) (defaults to: nil)
  • iv (String) (defaults to: nil)

Returns:

See Also:



639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
# File 'lib/jose/jwe.rb', line 639

def block_encrypt(key, block, cek = nil, iv = nil)
  jwe = self
  if cek.nil?
    cek, jwe = next_cek(key)
  end
  iv ||= jwe.next_iv
  aad, plain_text = block
  if plain_text.nil?
    plain_text = aad
    aad = nil
  end
  encrypted_key, jwe = jwe.key_encrypt(key, cek)
  protected_binary = JOSE.urlsafe_encode64(jwe.to_binary)
  if aad.nil?
    cipher_text, cipher_tag = enc.block_encrypt([protected_binary, jwe.compress(plain_text)], cek, iv)
    return JOSE::EncryptedMap[
      'ciphertext'    => JOSE.urlsafe_encode64(cipher_text),
      'encrypted_key' => JOSE.urlsafe_encode64(encrypted_key),
      'iv'            => JOSE.urlsafe_encode64(iv),
      'protected'     => protected_binary,
      'tag'           => JOSE.urlsafe_encode64(cipher_tag)
    ]
  else
    aad_b64 = JOSE.urlsafe_encode64(aad)
    concat_aad = [protected_binary, '.', aad_b64].join
    cipher_text, cipher_tag = enc.block_encrypt([concat_aad, jwe.compress(plain_text)], cek, iv)
    return JOSE::EncryptedMap[
      'aad'           => aad_b64,
      'ciphertext'    => JOSE.urlsafe_encode64(cipher_text),
      'encrypted_key' => JOSE.urlsafe_encode64(encrypted_key),
      'iv'            => JOSE.urlsafe_encode64(iv),
      'protected'     => protected_binary,
      'tag'           => JOSE.urlsafe_encode64(cipher_tag)
    ]
  end
end

#compress(plain_text) ⇒ String

Compresses the plain_text using the "zip" algorithm specified by the jwe.

Parameters:

  • plain_text (String)

Returns:

  • (String)

See Also:



729
730
731
732
733
734
735
# File 'lib/jose/jwe.rb', line 729

def compress(plain_text)
  if zip.nil?
    return plain_text
  else
    return zip.compress(plain_text)
  end
end

#generate_keyJOSE::JWK

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

Returns:

See Also:



795
796
797
# File 'lib/jose/jwe.rb', line 795

def generate_key
  return alg.generate_key(fields, enc)
end

#key_decrypt(key, encrypted_key) ⇒ String

Decrypts the encrypted_key using the key and the "alg" and "enc" specified by the jwe.

Parameters:

Returns:

  • (String)


823
824
825
# File 'lib/jose/jwe.rb', line 823

def key_decrypt(key, encrypted_key)
  return alg.key_decrypt(key, enc, encrypted_key)
end

#key_encrypt(key, decrypted_key) ⇒ [String, JOSE::JWE]

Encrypts the decrypted_key using the key and the "alg" and "enc" specified by the jwe.

Parameters:

Returns:



865
866
867
868
869
870
# File 'lib/jose/jwe.rb', line 865

def key_encrypt(key, decrypted_key)
  encrypted_key, new_alg = alg.key_encrypt(key, enc, decrypted_key)
  new_jwe = JOSE::JWE.from_map(to_map)
  new_jwe.alg = new_alg
  return encrypted_key, new_jwe
end

#merge(object) ⇒ JOSE::JWE

Merges object into current map.

Parameters:

Returns:



883
884
885
886
887
888
889
890
891
892
893
894
895
# File 'lib/jose/jwe.rb', line 883

def merge(object)
  object = case object
  when JOSE::Map, Hash
    object
  when String
    JOSE.decode(object)
  when JOSE::JWE
    object.to_map
  else
    raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWE"
  end
  return JOSE::JWE.from_map(self.to_map.merge(object))
end

#next_cek(key) ⇒ [String, JOSE::JWE]

Returns the next cek using the jwk and the "alg" and "enc" specified by the jwe.

Parameters:

Returns:



940
941
942
943
944
945
# File 'lib/jose/jwe.rb', line 940

def next_cek(key)
  decrypted_key, new_alg = alg.next_cek(key, enc)
  new_jwe = JOSE::JWE.from_map(to_map)
  new_jwe.alg = new_alg
  return decrypted_key, new_jwe
end

#next_ivString

Returns the next iv the "alg" and "enc" specified by the jwe.

Returns:

  • (String)


965
966
967
# File 'lib/jose/jwe.rb', line 965

def next_iv
  return enc.next_iv
end

#to_binaryString

Converts a JOSE::JWE into a binary.

Returns:

  • (String)


496
497
498
# File 'lib/jose/jwe.rb', line 496

def to_binary
  return JOSE.encode(to_map)
end

#to_file(file) ⇒ Fixnum

Calls #to_binary on a JOSE::JWE and then writes the binary to file.

Parameters:

  • file (String)

Returns:

  • (Fixnum)

    bytes written



511
512
513
# File 'lib/jose/jwe.rb', line 511

def to_file(file)
  return File.binwrite(file, to_binary)
end

#to_mapJOSE::Map

Converts a JOSE::JWE into a map.

Returns:



528
529
530
531
532
533
534
# File 'lib/jose/jwe.rb', line 528

def to_map
  if zip.nil?
    return alg.to_map(enc.to_map(fields))
  else
    return alg.to_map(enc.to_map(zip.to_map(fields)))
  end
end

#uncompress(cipher_text) ⇒ String

Uncompresses the cipher_text using the "zip" algorithm specified by the jwe.

Parameters:

  • cipher_text (String)

Returns:

  • (String)


1062
1063
1064
1065
1066
1067
1068
# File 'lib/jose/jwe.rb', line 1062

def uncompress(cipher_text)
  if zip.nil?
    return cipher_text
  else
    return zip.uncompress(cipher_text)
  end
end