diff --git a/README.md b/README.md index fad777c..b354c05 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,27 @@ plaintext = JWE.decrypt(encrypted, key) puts plaintext #"The quick brown fox jumps over the lazy dog." ``` +This example dynamically detects the key to use during decryption. + +```ruby +require 'jwe' + +keys = { + 'A128GCM' => OpenSSL::PKey::RSA.generate(2048), + 'A192GCM' => OpenSSL::PKey::RSA.generate(2048) +} +payload = "The quick brown fox jumps over the lazy dog." + +encrypted = JWE.encrypt(payload, keys['A192GCM'], enc: 'A192GCM') +puts encrypted + +plaintext = JWE.decrypt(encrypted, nil) do |headers| + enc = headers['enc'] + keys[enc] +end +puts plaintext #"The quick brown fox jumps over the lazy dog." +``` + ## Available Algorithms The RFC 7518 JSON Web Algorithms (JWA) spec defines the algorithms for [encryption](https://tools.ietf.org/html/rfc7518#section-5.1) diff --git a/lib/jwe.rb b/lib/jwe.rb index 2192915..598e36a 100644 --- a/lib/jwe.rb +++ b/lib/jwe.rb @@ -39,7 +39,7 @@ def self.encrypt(payload, key, alg: 'RSA-OAEP', enc: 'A128GCM', zip: nil) Serialization::Compact.encode(header.to_json, encrypted_cek, cipher.iv, ciphertext, cipher.tag) end - def self.decrypt(payload, key) + def self.decrypt(payload, key, &keyfinder) header, enc_key, iv, ciphertext, tag = Serialization::Compact.decode(payload) header = JSON.parse(header) base64header = payload.split('.').first @@ -47,6 +47,8 @@ def self.decrypt(payload, key) raise ArgumentError.new("\"#{header['alg']}\" is not a valid alg method") unless VALID_ALG.include?(header['alg']) raise ArgumentError.new("\"#{header['enc']}\" is not a valid enc method") unless VALID_ENC.include?(header['enc']) raise ArgumentError.new("\"#{header['zip']}\" is not a valid zip method") unless header['zip'].nil? || VALID_ZIP.include?(header['zip']) + + key = yield(header) if keyfinder raise ArgumentError.new('The key must not be nil or blank') if key.nil? || (key.is_a?(String) && key.strip == '') cek = Alg.for(header['alg']).new(key).decrypt(enc_key) diff --git a/spec/jwe_spec.rb b/spec/jwe_spec.rb index a9a8a15..5ff7673 100644 --- a/spec/jwe_spec.rb +++ b/spec/jwe_spec.rb @@ -29,6 +29,15 @@ end end + it 'allows dynamic key detection' do + encrypted = JWE.encrypt(plaintext, rsa_key) + result = JWE.decrypt(encrypted, nil) do |header| + rsa_key + end + + expect(result).to eq plaintext + end + it 'raises when passed a bad alg' do expect { JWE.encrypt(plaintext, rsa_key, alg: 'TEST') }.to raise_error(ArgumentError) end