Class: JWT::JWK::RSA

Inherits:
KeyBase show all
Defined in:
lib/jwt/jwk/rsa.rb

Overview

JSON Web Key (JWK) representation of a RSA key

Constant Summary collapse

BINARY =

rubocop:disable Metrics/ClassLength

2
KTY =
'RSA'
KTYS =
[KTY, OpenSSL::PKey::RSA, JWT::JWK::RSA].freeze
RSA_PUBLIC_KEY_ELEMENTS =
%i[kty n e].freeze
RSA_PRIVATE_KEY_ELEMENTS =
%i[d p q dp dq qi].freeze
RSA_KEY_ELEMENTS =
(RSA_PRIVATE_KEY_ELEMENTS + RSA_PUBLIC_KEY_ELEMENTS).freeze
RSA_OPT_PARAMS =
%i[p q dp dq qi].freeze
RSA_ASN1_SEQUENCE =
(%i[n e d] + RSA_OPT_PARAMS).freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from KeyBase

#<=>, #==, #[], #hash, inherited, #kid

Constructor Details

#initialize(key, params = nil, options = {}) ⇒ RSA

Returns a new instance of RSA.



17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/jwt/jwk/rsa.rb', line 17

def initialize(key, params = nil, options = {})
  params ||= {}

  # For backwards compatibility when kid was a String
  params = { kid: params } if params.is_a?(String)

  key_params = extract_key_params(key)

  params = params.transform_keys(&:to_sym)
  check_jwk_params!(key_params, params)

  super(options, key_params.merge(params))
end

Class Method Details

.create_rsa_key_using_accessors(rsa_parameters) ⇒ Object

rubocop:disable Metrics/AbcSize



168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/jwt/jwk/rsa.rb', line 168

def create_rsa_key_using_accessors(rsa_parameters) # rubocop:disable Metrics/AbcSize
  validate_rsa_parameters!(rsa_parameters)

  OpenSSL::PKey::RSA.new.tap do |rsa_key|
    rsa_key.n = rsa_parameters[:n]
    rsa_key.e = rsa_parameters[:e]
    rsa_key.d = rsa_parameters[:d] if rsa_parameters[:d]
    rsa_key.p = rsa_parameters[:p] if rsa_parameters[:p]
    rsa_key.q = rsa_parameters[:q] if rsa_parameters[:q]
    rsa_key.dmp1 = rsa_parameters[:dp] if rsa_parameters[:dp]
    rsa_key.dmq1 = rsa_parameters[:dq] if rsa_parameters[:dq]
    rsa_key.iqmp = rsa_parameters[:qi] if rsa_parameters[:qi]
  end
end

.create_rsa_key_using_der(rsa_parameters) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/jwt/jwk/rsa.rb', line 140

def create_rsa_key_using_der(rsa_parameters)
  validate_rsa_parameters!(rsa_parameters)

  sequence = RSA_ASN1_SEQUENCE.each_with_object([]) do |key, arr|
    next if rsa_parameters[key].nil?

    arr << OpenSSL::ASN1::Integer.new(rsa_parameters[key])
  end

  if sequence.size > 2 # Append "two-prime" version for private key
    sequence.unshift(OpenSSL::ASN1::Integer.new(0))

    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
  end

  OpenSSL::PKey::RSA.new(OpenSSL::ASN1::Sequence(sequence).to_der)
end

.create_rsa_key_using_sets(rsa_parameters) ⇒ Object



158
159
160
161
162
163
164
165
166
# File 'lib/jwt/jwk/rsa.rb', line 158

def create_rsa_key_using_sets(rsa_parameters)
  validate_rsa_parameters!(rsa_parameters)

  OpenSSL::PKey::RSA.new.tap do |rsa_key|
    rsa_key.set_key(rsa_parameters[:n], rsa_parameters[:e], rsa_parameters[:d])
    rsa_key.set_factors(rsa_parameters[:p], rsa_parameters[:q]) if rsa_parameters[:p] && rsa_parameters[:q]
    rsa_key.set_crt_params(rsa_parameters[:dp], rsa_parameters[:dq], rsa_parameters[:qi]) if rsa_parameters[:dp] && rsa_parameters[:dq] && rsa_parameters[:qi]
  end
end

.decode_open_ssl_bn(jwk_data) ⇒ Object



134
135
136
137
138
# File 'lib/jwt/jwk/rsa.rb', line 134

def decode_open_ssl_bn(jwk_data)
  return nil unless jwk_data

  OpenSSL::BN.new(::JWT::Base64.url_decode(jwk_data), BINARY)
end

.import(jwk_data) ⇒ Object



130
131
132
# File 'lib/jwt/jwk/rsa.rb', line 130

def import(jwk_data)
  new(jwk_data)
end

.validate_rsa_parameters!(rsa_parameters) ⇒ Object

Raises:



183
184
185
186
187
188
189
190
# File 'lib/jwt/jwk/rsa.rb', line 183

def validate_rsa_parameters!(rsa_parameters)
  return unless rsa_parameters.key?(:d)

  parameters = RSA_OPT_PARAMS - rsa_parameters.keys
  return if parameters.empty? || parameters.size == RSA_OPT_PARAMS.size

  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
end

Instance Method Details

#[]=(key, value) ⇒ Object

Raises:

  • (ArgumentError)


67
68
69
70
71
# File 'lib/jwt/jwk/rsa.rb', line 67

def []=(key, value)
  raise ArgumentError, 'cannot overwrite cryptographic key attributes' if RSA_KEY_ELEMENTS.include?(key.to_sym)

  super(key, value)
end

#export(options = {}) ⇒ Object



51
52
53
54
55
# File 'lib/jwt/jwk/rsa.rb', line 51

def export(options = {})
  exported = parameters.clone
  exported.reject! { |k, _| RSA_PRIVATE_KEY_ELEMENTS.include? k } unless private? && options[:include_private] == true
  exported
end

#key_digestObject



61
62
63
64
65
# File 'lib/jwt/jwk/rsa.rb', line 61

def key_digest
  sequence = OpenSSL::ASN1::Sequence([OpenSSL::ASN1::Integer.new(public_key.n),
                                      OpenSSL::ASN1::Integer.new(public_key.e)])
  OpenSSL::Digest::SHA256.hexdigest(sequence.to_der)
end

#keypairObject



31
32
33
# File 'lib/jwt/jwk/rsa.rb', line 31

def keypair
  rsa_key
end

#membersObject



57
58
59
# File 'lib/jwt/jwk/rsa.rb', line 57

def members
  RSA_PUBLIC_KEY_ELEMENTS.each_with_object({}) { |i, h| h[i] = self[i] }
end

#private?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'lib/jwt/jwk/rsa.rb', line 35

def private?
  rsa_key.private?
end

#public_keyObject



39
40
41
# File 'lib/jwt/jwk/rsa.rb', line 39

def public_key
  rsa_key.public_key
end

#signing_keyObject



43
44
45
# File 'lib/jwt/jwk/rsa.rb', line 43

def signing_key
  rsa_key if private?
end

#verify_keyObject



47
48
49
# File 'lib/jwt/jwk/rsa.rb', line 47

def verify_key
  rsa_key.public_key
end