jwt 2.5.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/jwt/jwk/hmac.rb CHANGED
@@ -4,15 +4,27 @@ module JWT
4
4
  module JWK
5
5
  class HMAC < KeyBase
6
6
  KTY = 'oct'
7
- KTYS = [KTY, String].freeze
7
+ KTYS = [KTY, String, JWT::JWK::HMAC].freeze
8
+ HMAC_PUBLIC_KEY_ELEMENTS = %i[kty].freeze
9
+ HMAC_PRIVATE_KEY_ELEMENTS = %i[k].freeze
10
+ HMAC_KEY_ELEMENTS = (HMAC_PRIVATE_KEY_ELEMENTS + HMAC_PUBLIC_KEY_ELEMENTS).freeze
8
11
 
9
- attr_reader :signing_key
12
+ def initialize(key, params = nil, options = {})
13
+ params ||= {}
10
14
 
11
- def initialize(signing_key, options = {})
12
- raise ArgumentError, 'signing_key must be of type String' unless signing_key.is_a?(String)
15
+ # For backwards compatibility when kid was a String
16
+ params = { kid: params } if params.is_a?(String)
13
17
 
14
- @signing_key = signing_key
15
- super(options)
18
+ key_params = extract_key_params(key)
19
+
20
+ params = params.transform_keys(&:to_sym)
21
+ check_jwk(key_params, params)
22
+
23
+ super(options, key_params.merge(params))
24
+ end
25
+
26
+ def keypair
27
+ secret
16
28
  end
17
29
 
18
30
  def private?
@@ -23,43 +35,67 @@ module JWT
23
35
  nil
24
36
  end
25
37
 
26
- # See https://tools.ietf.org/html/rfc7517#appendix-A.3
27
- def export(options = {})
28
- exported_hash = {
29
- kty: KTY,
30
- kid: kid
31
- }
38
+ def verify_key
39
+ secret
40
+ end
32
41
 
33
- return exported_hash unless private? && options[:include_private] == true
42
+ def signing_key
43
+ secret
44
+ end
34
45
 
35
- exported_hash.merge(
36
- k: signing_key
37
- )
46
+ # See https://tools.ietf.org/html/rfc7517#appendix-A.3
47
+ def export(options = {})
48
+ exported = parameters.clone
49
+ exported.reject! { |k, _| HMAC_PRIVATE_KEY_ELEMENTS.include? k } unless private? && options[:include_private] == true
50
+ exported
38
51
  end
39
52
 
40
53
  def members
41
- {
42
- kty: KTY,
43
- k: signing_key
44
- }
54
+ HMAC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
45
55
  end
46
56
 
47
- alias keypair signing_key # for backwards compatibility
48
-
49
57
  def key_digest
50
58
  sequence = OpenSSL::ASN1::Sequence([OpenSSL::ASN1::UTF8String.new(signing_key),
51
59
  OpenSSL::ASN1::UTF8String.new(KTY)])
52
60
  OpenSSL::Digest::SHA256.hexdigest(sequence.to_der)
53
61
  end
54
62
 
55
- class << self
56
- def import(jwk_data)
57
- jwk_k = jwk_data[:k] || jwk_data['k']
58
- jwk_kid = jwk_data[:kid] || jwk_data['kid']
63
+ def []=(key, value)
64
+ if HMAC_KEY_ELEMENTS.include?(key.to_sym)
65
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes'
66
+ end
67
+
68
+ super(key, value)
69
+ end
70
+
71
+ private
72
+
73
+ def secret
74
+ self[:k]
75
+ end
59
76
 
60
- raise JWT::JWKError, 'Key format is invalid for HMAC' unless jwk_k
77
+ def extract_key_params(key)
78
+ case key
79
+ when JWT::JWK::HMAC
80
+ key.export(include_private: true)
81
+ when String # Accept String key as input
82
+ { kty: KTY, k: key }
83
+ when Hash
84
+ key.transform_keys(&:to_sym)
85
+ else
86
+ raise ArgumentError, 'key must be of type String or Hash with key parameters'
87
+ end
88
+ end
89
+
90
+ def check_jwk(keypair, params)
91
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes' unless (HMAC_KEY_ELEMENTS & params.keys).empty?
92
+ raise JWT::JWKError, "Incorrect 'kty' value: #{keypair[:kty]}, expected #{KTY}" unless keypair[:kty] == KTY
93
+ raise JWT::JWKError, 'Key format is invalid for HMAC' unless keypair[:k]
94
+ end
61
95
 
62
- new(jwk_k, kid: jwk_kid)
96
+ class << self
97
+ def import(jwk_data)
98
+ new(jwk_data)
63
99
  end
64
100
  end
65
101
  end
@@ -8,28 +8,48 @@ module JWT
8
8
  ::JWT::JWK.classes << klass
9
9
  end
10
10
 
11
- def initialize(options)
11
+ def initialize(options, params = {})
12
12
  options ||= {}
13
13
 
14
- if options.is_a?(String) # For backwards compatibility when kid was a String
15
- options = { kid: options }
16
- end
14
+ @parameters = params.transform_keys(&:to_sym) # Uniform interface
17
15
 
18
- @kid = options[:kid]
19
- @kid_generator = options[:kid_generator] || ::JWT.configuration.jwk.kid_generator
16
+ # For backwards compatibility, kid_generator may be specified in the parameters
17
+ options[:kid_generator] ||= @parameters.delete(:kid_generator)
18
+
19
+ # Make sure the key has a kid
20
+ kid_generator = options[:kid_generator] || ::JWT.configuration.jwk.kid_generator
21
+ self[:kid] ||= kid_generator.new(self).generate
20
22
  end
21
23
 
22
24
  def kid
23
- @kid ||= generate_kid
25
+ self[:kid]
24
26
  end
25
27
 
26
- private
28
+ def hash
29
+ self[:kid].hash
30
+ end
31
+
32
+ def [](key)
33
+ @parameters[key.to_sym]
34
+ end
35
+
36
+ def []=(key, value)
37
+ @parameters[key.to_sym] = value
38
+ end
39
+
40
+ def ==(other)
41
+ self[:kid] == other[:kid]
42
+ end
27
43
 
28
- attr_reader :kid_generator
44
+ alias eql? ==
29
45
 
30
- def generate_kid
31
- kid_generator.new(self).generate
46
+ def <=>(other)
47
+ self[:kid] <=> other[:kid]
32
48
  end
49
+
50
+ private
51
+
52
+ attr_reader :parameters
33
53
  end
34
54
  end
35
55
  end
@@ -4,58 +4,42 @@ module JWT
4
4
  module JWK
5
5
  class KeyFinder
6
6
  def initialize(options)
7
+ @allow_nil_kid = options[:allow_nil_kid]
7
8
  jwks_or_loader = options[:jwks]
8
- @jwks = jwks_or_loader if jwks_or_loader.is_a?(Hash)
9
- @jwk_loader = jwks_or_loader if jwks_or_loader.respond_to?(:call)
9
+
10
+ @jwks_loader = if jwks_or_loader.respond_to?(:call)
11
+ jwks_or_loader
12
+ else
13
+ ->(_options) { jwks_or_loader }
14
+ end
10
15
  end
11
16
 
12
17
  def key_for(kid)
13
- raise ::JWT::DecodeError, 'No key id (kid) found from token headers' unless kid
18
+ raise ::JWT::DecodeError, 'No key id (kid) found from token headers' unless kid || @allow_nil_kid
19
+ raise ::JWT::DecodeError, 'Invalid type for kid header parameter' unless kid.nil? || kid.is_a?(String)
14
20
 
15
21
  jwk = resolve_key(kid)
16
22
 
17
- raise ::JWT::DecodeError, 'No keys found in jwks' if jwks_keys.empty?
23
+ raise ::JWT::DecodeError, 'No keys found in jwks' unless @jwks.any?
18
24
  raise ::JWT::DecodeError, "Could not find public key for kid #{kid}" unless jwk
19
25
 
20
- ::JWT::JWK.import(jwk).keypair
26
+ jwk.verify_key
21
27
  end
22
28
 
23
29
  private
24
30
 
25
31
  def resolve_key(kid)
26
- jwk = find_key(kid)
27
-
28
- return jwk if jwk
29
-
30
- if reloadable?
31
- load_keys(invalidate: true, kid_not_found: true, kid: kid) # invalidate for backwards compatibility
32
- return find_key(kid)
33
- end
34
-
35
- nil
36
- end
32
+ key_matcher = ->(key) { (kid.nil? && @allow_nil_kid) || key[:kid] == kid }
37
33
 
38
- def jwks
39
- return @jwks if @jwks
34
+ # First try without invalidation to facilitate application caching
35
+ @jwks ||= JWT::JWK::Set.new(@jwks_loader.call(kid: kid))
36
+ jwk = @jwks.find { |key| key_matcher.call(key) }
40
37
 
41
- load_keys
42
- @jwks
43
- end
44
-
45
- def load_keys(opts = {})
46
- @jwks = @jwk_loader.call(opts)
47
- end
48
-
49
- def jwks_keys
50
- Array(jwks[:keys] || jwks['keys'])
51
- end
52
-
53
- def find_key(kid)
54
- jwks_keys.find { |key| (key[:kid] || key['kid']) == kid }
55
- end
38
+ return jwk if jwk
56
39
 
57
- def reloadable?
58
- @jwk_loader
40
+ # Second try, invalidate for backwards compatibility
41
+ @jwks = JWT::JWK::Set.new(@jwks_loader.call(invalidate: true, kid_not_found: true, kid: kid))
42
+ @jwks.find { |key| key_matcher.call(key) }
59
43
  end
60
44
  end
61
45
  end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module JWK
5
+ class OKPRbNaCl < KeyBase
6
+ KTY = 'OKP'
7
+ KTYS = [KTY, JWT::JWK::OKPRbNaCl, RbNaCl::Signatures::Ed25519::SigningKey, RbNaCl::Signatures::Ed25519::VerifyKey].freeze
8
+ OKP_PUBLIC_KEY_ELEMENTS = %i[kty n x].freeze
9
+ OKP_PRIVATE_KEY_ELEMENTS = %i[d].freeze
10
+
11
+ def initialize(key, params = nil, options = {})
12
+ params ||= {}
13
+
14
+ # For backwards compatibility when kid was a String
15
+ params = { kid: params } if params.is_a?(String)
16
+
17
+ key_params = extract_key_params(key)
18
+
19
+ params = params.transform_keys(&:to_sym)
20
+ check_jwk_params!(key_params, params)
21
+ super(options, key_params.merge(params))
22
+ end
23
+
24
+ def verify_key
25
+ return @verify_key if defined?(@verify_key)
26
+
27
+ @verify_key = verify_key_from_parameters
28
+ end
29
+
30
+ def signing_key
31
+ return @signing_key if defined?(@signing_key)
32
+
33
+ @signing_key = signing_key_from_parameters
34
+ end
35
+
36
+ def key_digest
37
+ Thumbprint.new(self).to_s
38
+ end
39
+
40
+ def private?
41
+ !signing_key.nil?
42
+ end
43
+
44
+ def members
45
+ OKP_PUBLIC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
46
+ end
47
+
48
+ def export(options = {})
49
+ exported = parameters.clone
50
+ exported.reject! { |k, _| OKP_PRIVATE_KEY_ELEMENTS.include?(k) } unless private? && options[:include_private] == true
51
+ exported
52
+ end
53
+
54
+ private
55
+
56
+ def extract_key_params(key)
57
+ case key
58
+ when JWT::JWK::KeyBase
59
+ key.export(include_private: true)
60
+ when RbNaCl::Signatures::Ed25519::SigningKey
61
+ @signing_key = key
62
+ @verify_key = key.verify_key
63
+ parse_okp_key_params(@verify_key, @signing_key)
64
+ when RbNaCl::Signatures::Ed25519::VerifyKey
65
+ @signing_key = nil
66
+ @verify_key = key
67
+ parse_okp_key_params(@verify_key)
68
+ when Hash
69
+ key.transform_keys(&:to_sym)
70
+ else
71
+ raise ArgumentError, 'key must be of type RbNaCl::Signatures::Ed25519::SigningKey, RbNaCl::Signatures::Ed25519::VerifyKey or Hash with key parameters'
72
+ end
73
+ end
74
+
75
+ def check_jwk_params!(key_params, _given_params)
76
+ raise JWT::JWKError, "Incorrect 'kty' value: #{key_params[:kty]}, expected #{KTY}" unless key_params[:kty] == KTY
77
+ end
78
+
79
+ def parse_okp_key_params(verify_key, signing_key = nil)
80
+ params = {
81
+ kty: KTY,
82
+ crv: 'Ed25519',
83
+ x: ::JWT::Base64.url_encode(verify_key.to_bytes)
84
+ }
85
+
86
+ if signing_key
87
+ params[:d] = ::JWT::Base64.url_encode(signing_key.to_bytes)
88
+ end
89
+
90
+ params
91
+ end
92
+
93
+ def verify_key_from_parameters
94
+ RbNaCl::Signatures::Ed25519::VerifyKey.new(::JWT::Base64.url_decode(self[:x]))
95
+ end
96
+
97
+ def signing_key_from_parameters
98
+ return nil unless self[:d]
99
+
100
+ RbNaCl::Signatures::Ed25519::SigningKey.new(::JWT::Base64.url_decode(self[:d]))
101
+ end
102
+
103
+ class << self
104
+ def import(jwk_data)
105
+ new(jwk_data)
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
data/lib/jwt/jwk/rsa.rb CHANGED
@@ -2,44 +2,59 @@
2
2
 
3
3
  module JWT
4
4
  module JWK
5
- class RSA < KeyBase
5
+ class RSA < KeyBase # rubocop:disable Metrics/ClassLength
6
6
  BINARY = 2
7
7
  KTY = 'RSA'
8
- KTYS = [KTY, OpenSSL::PKey::RSA].freeze
9
- RSA_KEY_ELEMENTS = %i[n e d p q dp dq qi].freeze
8
+ KTYS = [KTY, OpenSSL::PKey::RSA, JWT::JWK::RSA].freeze
9
+ RSA_PUBLIC_KEY_ELEMENTS = %i[kty n e].freeze
10
+ RSA_PRIVATE_KEY_ELEMENTS = %i[d p q dp dq qi].freeze
11
+ RSA_KEY_ELEMENTS = (RSA_PRIVATE_KEY_ELEMENTS + RSA_PUBLIC_KEY_ELEMENTS).freeze
10
12
 
11
- attr_reader :keypair
13
+ RSA_OPT_PARAMS = %i[p q dp dq qi].freeze
14
+ RSA_ASN1_SEQUENCE = (%i[n e d] + RSA_OPT_PARAMS).freeze # https://www.rfc-editor.org/rfc/rfc3447#appendix-A.1.2
12
15
 
13
- def initialize(keypair, options = {})
14
- raise ArgumentError, 'keypair must be of type OpenSSL::PKey::RSA' unless keypair.is_a?(OpenSSL::PKey::RSA)
16
+ def initialize(key, params = nil, options = {})
17
+ params ||= {}
15
18
 
16
- @keypair = keypair
19
+ # For backwards compatibility when kid was a String
20
+ params = { kid: params } if params.is_a?(String)
17
21
 
18
- super(options)
22
+ key_params = extract_key_params(key)
23
+
24
+ params = params.transform_keys(&:to_sym)
25
+ check_jwk_params!(key_params, params)
26
+
27
+ super(options, key_params.merge(params))
28
+ end
29
+
30
+ def keypair
31
+ rsa_key
19
32
  end
20
33
 
21
34
  def private?
22
- keypair.private?
35
+ rsa_key.private?
23
36
  end
24
37
 
25
38
  def public_key
26
- keypair.public_key
39
+ rsa_key.public_key
27
40
  end
28
41
 
29
- def export(options = {})
30
- exported_hash = members.merge(kid: kid)
42
+ def signing_key
43
+ rsa_key if private?
44
+ end
31
45
 
32
- return exported_hash unless private? && options[:include_private] == true
46
+ def verify_key
47
+ rsa_key.public_key
48
+ end
33
49
 
34
- append_private_parts(exported_hash)
50
+ def export(options = {})
51
+ exported = parameters.clone
52
+ exported.reject! { |k, _| RSA_PRIVATE_KEY_ELEMENTS.include? k } unless private? && options[:include_private] == true
53
+ exported
35
54
  end
36
55
 
37
56
  def members
38
- {
39
- kty: KTY,
40
- n: encode_open_ssl_bn(public_key.n),
41
- e: encode_open_ssl_bn(public_key.e)
42
- }
57
+ RSA_PUBLIC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
43
58
  end
44
59
 
45
60
  def key_digest
@@ -48,89 +63,139 @@ module JWT
48
63
  OpenSSL::Digest::SHA256.hexdigest(sequence.to_der)
49
64
  end
50
65
 
66
+ def []=(key, value)
67
+ if RSA_KEY_ELEMENTS.include?(key.to_sym)
68
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes'
69
+ end
70
+
71
+ super(key, value)
72
+ end
73
+
51
74
  private
52
75
 
53
- def append_private_parts(the_hash)
54
- the_hash.merge(
55
- d: encode_open_ssl_bn(keypair.d),
56
- p: encode_open_ssl_bn(keypair.p),
57
- q: encode_open_ssl_bn(keypair.q),
58
- dp: encode_open_ssl_bn(keypair.dmp1),
59
- dq: encode_open_ssl_bn(keypair.dmq1),
60
- qi: encode_open_ssl_bn(keypair.iqmp)
61
- )
76
+ def rsa_key
77
+ @rsa_key ||= self.class.create_rsa_key(jwk_attributes(*(RSA_KEY_ELEMENTS - [:kty])))
78
+ end
79
+
80
+ def extract_key_params(key)
81
+ case key
82
+ when JWT::JWK::RSA
83
+ key.export(include_private: true)
84
+ when OpenSSL::PKey::RSA # Accept OpenSSL key as input
85
+ @rsa_key = key # Preserve the object to avoid recreation
86
+ parse_rsa_key(key)
87
+ when Hash
88
+ key.transform_keys(&:to_sym)
89
+ else
90
+ raise ArgumentError, 'key must be of type OpenSSL::PKey::RSA or Hash with key parameters'
91
+ end
92
+ end
93
+
94
+ def check_jwk_params!(key_params, params)
95
+ raise ArgumentError, 'cannot overwrite cryptographic key attributes' unless (RSA_KEY_ELEMENTS & params.keys).empty?
96
+ raise JWT::JWKError, "Incorrect 'kty' value: #{key_params[:kty]}, expected #{KTY}" unless key_params[:kty] == KTY
97
+ raise JWT::JWKError, 'Key format is invalid for RSA' unless key_params[:n] && key_params[:e]
98
+ end
99
+
100
+ def parse_rsa_key(key)
101
+ {
102
+ kty: KTY,
103
+ n: encode_open_ssl_bn(key.n),
104
+ e: encode_open_ssl_bn(key.e),
105
+ d: encode_open_ssl_bn(key.d),
106
+ p: encode_open_ssl_bn(key.p),
107
+ q: encode_open_ssl_bn(key.q),
108
+ dp: encode_open_ssl_bn(key.dmp1),
109
+ dq: encode_open_ssl_bn(key.dmq1),
110
+ qi: encode_open_ssl_bn(key.iqmp)
111
+ }.compact
112
+ end
113
+
114
+ def jwk_attributes(*attributes)
115
+ attributes.each_with_object({}) do |attribute, hash|
116
+ hash[attribute] = decode_open_ssl_bn(self[attribute])
117
+ end
62
118
  end
63
119
 
64
120
  def encode_open_ssl_bn(key_part)
121
+ return unless key_part
122
+
65
123
  ::JWT::Base64.url_encode(key_part.to_s(BINARY))
66
124
  end
67
125
 
126
+ def decode_open_ssl_bn(jwk_data)
127
+ self.class.decode_open_ssl_bn(jwk_data)
128
+ end
129
+
68
130
  class << self
69
131
  def import(jwk_data)
70
- pkey_params = jwk_attributes(jwk_data, *RSA_KEY_ELEMENTS) do |value|
71
- decode_open_ssl_bn(value)
72
- end
73
- new(rsa_pkey(pkey_params), kid: jwk_attributes(jwk_data, :kid)[:kid])
132
+ new(jwk_data)
74
133
  end
75
134
 
76
- private
135
+ def decode_open_ssl_bn(jwk_data)
136
+ return nil unless jwk_data
77
137
 
78
- def jwk_attributes(jwk_data, *attributes)
79
- attributes.each_with_object({}) do |attribute, hash|
80
- value = jwk_data[attribute] || jwk_data[attribute.to_s]
81
- value = yield(value) if block_given?
82
- hash[attribute] = value
83
- end
138
+ OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
84
139
  end
85
140
 
86
- def rsa_pkey(rsa_parameters)
87
- raise JWT::JWKError, 'Key format is invalid for RSA' unless rsa_parameters[:n] && rsa_parameters[:e]
141
+ def create_rsa_key_using_der(rsa_parameters)
142
+ validate_rsa_parameters!(rsa_parameters)
88
143
 
89
- create_rsa_key(rsa_parameters)
90
- end
91
-
92
- if ::JWT.openssl_3?
93
- ASN1_SEQUENCE = %i[n e d p q dp dq qi].freeze
94
- def create_rsa_key(rsa_parameters)
95
- sequence = ASN1_SEQUENCE.each_with_object([]) do |key, arr|
96
- next if rsa_parameters[key].nil?
144
+ sequence = RSA_ASN1_SEQUENCE.each_with_object([]) do |key, arr|
145
+ next if rsa_parameters[key].nil?
97
146
 
98
- arr << OpenSSL::ASN1::Integer.new(rsa_parameters[key])
99
- end
147
+ arr << OpenSSL::ASN1::Integer.new(rsa_parameters[key])
148
+ end
100
149
 
101
- if sequence.size > 2 # For a private key
102
- sequence.unshift(OpenSSL::ASN1::Integer.new(0))
103
- end
150
+ if sequence.size > 2 # Append "two-prime" version for private key
151
+ sequence.unshift(OpenSSL::ASN1::Integer.new(0))
104
152
 
105
- OpenSSL::PKey::RSA.new(OpenSSL::ASN1::Sequence(sequence).to_der)
153
+ raise JWT::JWKError, 'Creating a RSA key with a private key requires the CRT parameters to be defined' if sequence.size < RSA_ASN1_SEQUENCE.size
106
154
  end
107
- elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
108
- def create_rsa_key(rsa_parameters)
109
- OpenSSL::PKey::RSA.new.tap do |rsa_key|
110
- rsa_key.set_key(rsa_parameters[:n], rsa_parameters[:e], rsa_parameters[:d])
111
- rsa_key.set_factors(rsa_parameters[:p], rsa_parameters[:q]) if rsa_parameters[:p] && rsa_parameters[:q]
112
- rsa_key.set_crt_params(rsa_parameters[:dp], rsa_parameters[:dq], rsa_parameters[:qi]) if rsa_parameters[:dp] && rsa_parameters[:dq] && rsa_parameters[:qi]
113
- end
155
+
156
+ OpenSSL::PKey::RSA.new(OpenSSL::ASN1::Sequence(sequence).to_der)
157
+ end
158
+
159
+ def create_rsa_key_using_sets(rsa_parameters)
160
+ validate_rsa_parameters!(rsa_parameters)
161
+
162
+ OpenSSL::PKey::RSA.new.tap do |rsa_key|
163
+ rsa_key.set_key(rsa_parameters[:n], rsa_parameters[:e], rsa_parameters[:d])
164
+ rsa_key.set_factors(rsa_parameters[:p], rsa_parameters[:q]) if rsa_parameters[:p] && rsa_parameters[:q]
165
+ rsa_key.set_crt_params(rsa_parameters[:dp], rsa_parameters[:dq], rsa_parameters[:qi]) if rsa_parameters[:dp] && rsa_parameters[:dq] && rsa_parameters[:qi]
114
166
  end
115
- else
116
- def create_rsa_key(rsa_parameters) # rubocop:disable Metrics/AbcSize
117
- OpenSSL::PKey::RSA.new.tap do |rsa_key|
118
- rsa_key.n = rsa_parameters[:n]
119
- rsa_key.e = rsa_parameters[:e]
120
- rsa_key.d = rsa_parameters[:d] if rsa_parameters[:d]
121
- rsa_key.p = rsa_parameters[:p] if rsa_parameters[:p]
122
- rsa_key.q = rsa_parameters[:q] if rsa_parameters[:q]
123
- rsa_key.dmp1 = rsa_parameters[:dp] if rsa_parameters[:dp]
124
- rsa_key.dmq1 = rsa_parameters[:dq] if rsa_parameters[:dq]
125
- rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
126
- end
167
+ end
168
+
169
+ def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/AbcSize
170
+ validate_rsa_parameters!(rsa_parameters)
171
+
172
+ OpenSSL::PKey::RSA.new.tap do |rsa_key|
173
+ rsa_key.n = rsa_parameters[:n]
174
+ rsa_key.e = rsa_parameters[:e]
175
+ rsa_key.d = rsa_parameters[:d] if rsa_parameters[:d]
176
+ rsa_key.p = rsa_parameters[:p] if rsa_parameters[:p]
177
+ rsa_key.q = rsa_parameters[:q] if rsa_parameters[:q]
178
+ rsa_key.dmp1 = rsa_parameters[:dp] if rsa_parameters[:dp]
179
+ rsa_key.dmq1 = rsa_parameters[:dq] if rsa_parameters[:dq]
180
+ rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
127
181
  end
128
182
  end
129
183
 
130
- def decode_open_ssl_bn(jwk_data)
131
- return nil unless jwk_data
184
+ def validate_rsa_parameters!(rsa_parameters)
185
+ return unless rsa_parameters.key?(:d)
132
186
 
133
- OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
187
+ parameters = RSA_OPT_PARAMS - rsa_parameters.keys
188
+ return if parameters.empty? || parameters.size == RSA_OPT_PARAMS.size
189
+
190
+ raise JWT::JWKError, 'When one of p, q, dp, dq or qi is given all the other optimization parameters also needs to be defined' # https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2
191
+ end
192
+
193
+ if ::JWT.openssl_3?
194
+ alias create_rsa_key create_rsa_key_using_der
195
+ elsif OpenSSL::PKey::RSA.new.respond_to?(:set_key)
196
+ alias create_rsa_key create_rsa_key_using_sets
197
+ else
198
+ alias create_rsa_key create_rsa_key_using_accessors
134
199
  end
135
200
  end
136
201
  end