Class: JOSE::JWK::KTY_RSA

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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#keyObject

Returns the value of attribute key

Returns:

  • (Object)

    the current value of key



1
2
3
# File 'lib/jose/jwk/kty_rsa.rb', line 1

def key
  @key
end

Class Method Details

.from_key(key) ⇒ Object

API functions



209
210
211
212
213
214
215
216
217
# File 'lib/jose/jwk/kty_rsa.rb', line 209

def self.from_key(key)
  key = key.__getobj__ if key.is_a?(JOSE::JWK::PKeyProxy)
  case key
  when OpenSSL::PKey::RSA
    return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(key)), JOSE::Map[]
  else
    raise ArgumentError, "'key' must be a OpenSSL::PKey::RSA"
  end
end

.from_map(fields) ⇒ Object

JOSE::JWK callbacks



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/jose/jwk/kty_rsa.rb', line 5

def self.from_map(fields)
  if fields['kty'] == 'RSA' and fields['e'].is_a?(String) and fields['n'].is_a?(String)
    if fields['oth'].is_a?(Array)
      raise ArgumentError, "multi-prime RSA keys are not supported"
    elsif fields['d'].is_a?(String)
      if fields['dp'].is_a?(String) and fields['dq'].is_a?(String) and fields['p'].is_a?(String) and fields['q'].is_a?(String) and fields['qi'].is_a?(String)
        asn1_sequence = OpenSSL::ASN1::Sequence.new([
          OpenSSL::ASN1::Integer.new(0),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['p']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['q']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['dp']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['dq']), 2)),
          OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['qi']), 2))
        ])
        rsa = OpenSSL::PKey::RSA.new(asn1_sequence.to_der)
        return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
      else
        d = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)
        e = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)
        n = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)
        rsa = convert_sfm_to_crt(d, e, n)
        return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
      end
    else
      asn1_sequence = OpenSSL::ASN1::Sequence.new([
        OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)),
        OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2))
      ])
      rsa = OpenSSL::PKey::RSA.new(OpenSSL::PKey::RSA.new(asn1_sequence.to_der))
      return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'e', 'n')
    end
  else
    raise ArgumentError, "invalid 'RSA' JWK"
  end
end

.generate_key(modulus_size, exponent_size = nil) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/jose/jwk/kty_rsa.rb', line 129

def self.generate_key(modulus_size, exponent_size = nil)
  if modulus_size.is_a?(Array)
    if modulus_size.length == 2 and modulus_size[0] == :rsa
      modulus_size = modulus_size[1]
    elsif modulus_size.length == 3 and modulus_size[0] == :rsa
      exponent_size = modulus_size[2]
      modulus_size  = modulus_size[1]
    end
  end
  if modulus_size.is_a?(Integer) and (exponent_size.nil? or exponent_size.is_a?(Integer))
    return from_key(OpenSSL::PKey::RSA.generate(modulus_size)) if exponent_size.nil?
    return from_key(OpenSSL::PKey::RSA.generate(modulus_size, exponent_size)) if exponent_size.is_a?(Integer)
  else
    raise ArgumentError, "'modulus_size' must be an Integer and 'exponent_size' must be nil or an Integer"
  end
end

Instance Method Details

#block_encryptor(fields = nil) ⇒ Object

JOSE::JWK::KTY callbacks



79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/jose/jwk/kty_rsa.rb', line 79

def block_encryptor(fields = nil)
  if fields and fields['use'] == 'enc' and not fields['alg'].nil? and not fields['enc'].nil?
    return JOSE::Map[
      'alg' => fields['alg'],
      'enc' => fields['enc']
    ]
  else
    return JOSE::Map[
      'alg' => 'RSA-OAEP',
      'enc' => 'A128GCM'
    ]
  end
end

#decrypt_private(cipher_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/jose/jwk/kty_rsa.rb', line 93

def decrypt_private(cipher_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil)
  case rsa_padding
  when :rsa_pkcs1_padding
    return key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_PADDING)
  when :rsa_pkcs1_oaep_padding
    rsa_oaep_md ||= OpenSSL::Digest::SHA1
    if rsa_oaep_md == OpenSSL::Digest::SHA1
      return key.private_decrypt(cipher_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
    elsif rsa_oaep_md == OpenSSL::Digest::SHA256
      return JOSE::JWA::PKCS1.rsaes_oaep_decrypt(rsa_oaep_md, cipher_text, key)
    else
      raise ArgumentError, "unsupported RSA OAEP md: #{rsa_oaep_md.inspect}"
    end
  else
    raise ArgumentError, "unsupported RSA padding: #{rsa_padding.inspect}"
  end
end

#encrypt_public(plain_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/jose/jwk/kty_rsa.rb', line 111

def encrypt_public(plain_text, rsa_padding: :rsa_pkcs1_padding, rsa_oaep_md: nil)
  case rsa_padding
  when :rsa_pkcs1_padding
    return key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_PADDING)
  when :rsa_pkcs1_oaep_padding
    rsa_oaep_md ||= OpenSSL::Digest::SHA1
    if rsa_oaep_md == OpenSSL::Digest::SHA1
      return key.public_encrypt(plain_text, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
    elsif rsa_oaep_md == OpenSSL::Digest::SHA256
      return JOSE::JWA::PKCS1.rsaes_oaep_encrypt(rsa_oaep_md, plain_text, key)
    else
      raise ArgumentError, "unsupported RSA OAEP md: #{rsa_oaep_md.inspect}"
    end
  else
    raise ArgumentError, "unsupported RSA padding: #{rsa_padding.inspect}"
  end
end

#generate_key(fields) ⇒ Object



146
147
148
149
# File 'lib/jose/jwk/kty_rsa.rb', line 146

def generate_key(fields)
  kty, other_fields = JOSE::JWK::KTY_RSA.generate_key([:rsa, key.n.num_bits, key.e.to_i])
  return kty, fields.delete('kid').merge(other_fields)
end

#key_encryptor(fields, key) ⇒ Object



151
152
153
# File 'lib/jose/jwk/kty_rsa.rb', line 151

def key_encryptor(fields, key)
  return JOSE::JWK::KTY.key_encryptor(self, fields, key)
end

#sign(message, digest_type, padding: :rsa_pkcs1_padding) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/jose/jwk/kty_rsa.rb', line 155

def sign(message, digest_type, padding: :rsa_pkcs1_padding)
  case padding
  when :rsa_pkcs1_padding
    return key.sign(digest_type.new, message)
  when :rsa_pkcs1_pss_padding
    if key.respond_to?(:sign_pss)
      digest_name = digest_type.new.name
      return key.sign_pss(digest_name, message, salt_length: :digest, mgf1_hash: digest_name)
    else
      return JOSE::JWA::PKCS1.rsassa_pss_sign(digest_type, message, key)
    end
  else
    raise ArgumentError, "unsupported RSA padding: #{padding.inspect}"
  end
end

#signer(fields = nil) ⇒ Object



171
172
173
174
175
176
177
178
179
# File 'lib/jose/jwk/kty_rsa.rb', line 171

def signer(fields = nil)
  if key.private? and fields and fields['use'] == 'sig' and not fields['alg'].nil?
    return JOSE::Map['alg' => fields['alg']]
  elsif key.private?
    return JOSE::Map['alg' => 'RS256']
  else
    raise ArgumentError, "signing not supported for public keys"
  end
end

#to_keyObject



44
45
46
# File 'lib/jose/jwk/kty_rsa.rb', line 44

def to_key
  return key.__getobj__
end

#to_map(fields) ⇒ Object



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/jose/jwk/kty_rsa.rb', line 48

def to_map(fields)
  if key.private?
    return fields.
      put('d',   JOSE.urlsafe_encode64(key.d.to_s(2))).
      put('dp',  JOSE.urlsafe_encode64(key.dmp1.to_s(2))).
      put('dq',  JOSE.urlsafe_encode64(key.dmq1.to_s(2))).
      put('e',   JOSE.urlsafe_encode64(key.e.to_s(2))).
      put('kty', 'RSA').
      put('n',   JOSE.urlsafe_encode64(key.n.to_s(2))).
      put('p',   JOSE.urlsafe_encode64(key.p.to_s(2))).
      put('q',   JOSE.urlsafe_encode64(key.q.to_s(2))).
      put('qi',  JOSE.urlsafe_encode64(key.iqmp.to_s(2))).
      put('q',   JOSE.urlsafe_encode64(key.q.to_s(2)))
  else
    return fields.
      put('e',   JOSE.urlsafe_encode64(key.e.to_s(2))).
      put('kty', 'RSA').
      put('n',   JOSE.urlsafe_encode64(key.n.to_s(2)))
  end
end

#to_pem(password = nil) ⇒ Object



219
220
221
# File 'lib/jose/jwk/kty_rsa.rb', line 219

def to_pem(password = nil)
  return JOSE::JWK::PEM.to_binary(key, password)
end

#to_public_map(fields) ⇒ Object



69
70
71
# File 'lib/jose/jwk/kty_rsa.rb', line 69

def to_public_map(fields)
  return to_map(fields).except('d', 'dp', 'dq', 'p', 'q', 'qi', 'oth')
end

#to_thumbprint_map(fields) ⇒ Object



73
74
75
# File 'lib/jose/jwk/kty_rsa.rb', line 73

def to_thumbprint_map(fields)
  return to_public_map(fields).slice('e', 'kty', 'n')
end

#verifier(fields) ⇒ Object



181
182
183
184
185
186
187
# File 'lib/jose/jwk/kty_rsa.rb', line 181

def verifier(fields)
  if fields and fields['use'] == 'sig' and not fields['alg'].nil?
    return [fields['alg']]
  else
    return ['PS256', 'PS384', 'PS512', 'RS256', 'RS384', 'RS512']
  end
end

#verify(message, digest_type, signature, padding: :rsa_pkcs1_padding) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/jose/jwk/kty_rsa.rb', line 189

def verify(message, digest_type, signature, padding: :rsa_pkcs1_padding)
  case padding
  when :rsa_pkcs1_padding
    return key.verify(digest_type.new, signature, message)
  when :rsa_pkcs1_pss_padding
    if key.respond_to?(:verify_pss)
      digest_name = digest_type.new.name
      return key.verify_pss(digest_name, signature, message, salt_length: :digest, mgf1_hash: digest_name)
    else
      return JOSE::JWA::PKCS1.rsassa_pss_verify(digest_type, message, signature, key)
    end
  else
    raise ArgumentError, "unsupported RSA padding: #{padding.inspect}"
  end
rescue OpenSSL::PKey::PKeyError # jruby raises this error if the signature is invalid
  false
end