jose 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +7 -0
- data/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +10 -3
- data/docs/EncryptionAlgorithms.md +55 -0
- data/docs/GettingStarted.md +137 -0
- data/docs/KeyGeneration.md +263 -0
- data/docs/SignatureAlgorithms.md +35 -0
- data/lib/jose.rb +86 -22
- data/lib/jose/jwa.rb +293 -0
- data/lib/jose/jwa/concat_kdf.rb +2 -0
- data/lib/jose/jwa/curve25519_ruby.rb +1 -1
- data/lib/jose/jwa/curve448_ruby.rb +1 -1
- data/lib/jose/jwa/ed448.rb +8 -8
- data/lib/jose/jwe.rb +819 -11
- data/lib/jose/jwe/alg_aes_gcm_kw.rb +1 -1
- data/lib/jose/jwe/alg_aes_kw.rb +1 -1
- data/lib/jose/jwe/alg_dir.rb +4 -4
- data/lib/jose/jwe/alg_ecdh_es.rb +45 -18
- data/lib/jose/jwe/alg_pbes2.rb +3 -3
- data/lib/jose/jwe/alg_rsa.rb +1 -1
- data/lib/jose/jwk.rb +639 -48
- data/lib/jose/jwk/kty_ec.rb +22 -4
- data/lib/jose/jwk/kty_oct.rb +16 -0
- data/lib/jose/jwk/kty_okp_ed25519.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_x25519.rb +28 -0
- data/lib/jose/jwk/kty_okp_x448.rb +28 -0
- data/lib/jose/jwk/kty_rsa.rb +8 -0
- data/lib/jose/jwk/openssh_key.rb +278 -0
- data/lib/jose/jws.rb +486 -21
- data/lib/jose/jws/alg_none.rb +2 -2
- data/lib/jose/jwt.rb +208 -14
- data/lib/jose/version.rb +1 -1
- metadata +9 -3
data/lib/jose/jwe/alg_aes_kw.rb
CHANGED
data/lib/jose/jwe/alg_dir.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
|
-
class JOSE::JWE::ALG_dir
|
1
|
+
class JOSE::JWE::ALG_dir < Struct.new(:direct)
|
2
2
|
|
3
3
|
# JOSE::JWE callbacks
|
4
4
|
|
5
5
|
def self.from_map(fields)
|
6
6
|
case fields['alg']
|
7
7
|
when 'dir'
|
8
|
-
return new(), fields.delete('alg')
|
8
|
+
return new(true), fields.delete('alg')
|
9
9
|
else
|
10
10
|
raise ArgumentError, "invalid 'alg' for JWE: #{fields['alg'].inspect}"
|
11
11
|
end
|
@@ -35,9 +35,9 @@ class JOSE::JWE::ALG_dir
|
|
35
35
|
|
36
36
|
def next_cek(key, enc)
|
37
37
|
if key.is_a?(String)
|
38
|
-
return key
|
38
|
+
return key, self
|
39
39
|
else
|
40
|
-
return key.kty.derive_key
|
40
|
+
return key.kty.derive_key, self
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
data/lib/jose/jwe/alg_ecdh_es.rb
CHANGED
@@ -57,26 +57,31 @@ class JOSE::JWE::ALG_ECDH_ES < Struct.new(:bits, :epk, :apu, :apv)
|
|
57
57
|
|
58
58
|
def key_decrypt(box_keys, enc, encrypted_key)
|
59
59
|
other_public_key, my_private_key = box_keys
|
60
|
-
if my_private_key
|
61
|
-
raise ArgumentError, "other and ephemeral public key mismatch"
|
62
|
-
elsif epk and my_private_key.nil?
|
60
|
+
if my_private_key.nil?
|
63
61
|
my_private_key = other_public_key
|
64
|
-
other_public_key =
|
65
|
-
|
66
|
-
|
62
|
+
other_public_key = nil
|
63
|
+
end
|
64
|
+
if epk.nil? and other_public_key.nil?
|
65
|
+
raise ArgumentError, "missing 'epk' and 'other_public_key'"
|
66
|
+
elsif epk and other_public_key and epk.thumbprint != other_public_key.thumbprint
|
67
|
+
raise ArgumentError, "other and ephemeral public key mismatch"
|
68
|
+
end
|
69
|
+
new_alg = self
|
70
|
+
if epk.nil?
|
71
|
+
new_alg = JOSE::JWE::ALG_ECDH_ES.new(bits, other_public_key.to_public, apu, apv)
|
67
72
|
end
|
68
|
-
z =
|
73
|
+
z = new_alg.epk.derive_key(my_private_key)
|
69
74
|
if bits.nil?
|
70
75
|
algorithm_id = enc.algorithm
|
71
76
|
key_data_len = enc.bits
|
72
77
|
supp_pub_info = [key_data_len].pack('N')
|
73
|
-
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, apu, apv, supp_pub_info], key_data_len)
|
78
|
+
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, new_alg.apu, new_alg.apv, supp_pub_info], key_data_len)
|
74
79
|
return derived_key
|
75
80
|
else
|
76
|
-
algorithm_id = algorithm
|
77
|
-
key_data_len = bits
|
81
|
+
algorithm_id = new_alg.algorithm
|
82
|
+
key_data_len = new_alg.bits
|
78
83
|
supp_pub_info = [key_data_len].pack('N')
|
79
|
-
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, apu, apv, supp_pub_info], key_data_len)
|
84
|
+
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, new_alg.apu, new_alg.apv, supp_pub_info], key_data_len)
|
80
85
|
decrypted_key = JOSE::JWA::AES_KW.unwrap(encrypted_key, derived_key)
|
81
86
|
return decrypted_key
|
82
87
|
end
|
@@ -87,27 +92,49 @@ class JOSE::JWE::ALG_ECDH_ES < Struct.new(:bits, :epk, :apu, :apv)
|
|
87
92
|
return '', self
|
88
93
|
else
|
89
94
|
other_public_key, my_private_key = box_keys
|
95
|
+
if my_private_key.nil?
|
96
|
+
raise ArgumentError, "missing 'my_private_key'"
|
97
|
+
elsif other_public_key.nil?
|
98
|
+
raise ArgumentError, "missing 'other_public_key'"
|
99
|
+
elsif epk and my_private_key and epk.thumbprint != my_private_key.thumbprint
|
100
|
+
raise ArgumentError, "private and ephemeral public key mismatch"
|
101
|
+
end
|
102
|
+
new_alg = self
|
103
|
+
if epk.nil?
|
104
|
+
new_alg = JOSE::JWE::ALG_ECDH_ES.new(bits, my_private_key.to_public, apu, apv)
|
105
|
+
end
|
90
106
|
z = other_public_key.derive_key(my_private_key)
|
91
|
-
algorithm_id = algorithm
|
92
|
-
key_data_len = bits
|
107
|
+
algorithm_id = new_alg.algorithm
|
108
|
+
key_data_len = new_alg.bits
|
93
109
|
supp_pub_info = [key_data_len].pack('N')
|
94
|
-
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, apu, apv, supp_pub_info], key_data_len)
|
110
|
+
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, new_alg.apu, new_alg.apv, supp_pub_info], key_data_len)
|
95
111
|
encrypted_key = JOSE::JWA::AES_KW.wrap(decrypted_key, derived_key)
|
96
|
-
return encrypted_key,
|
112
|
+
return encrypted_key, new_alg
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
116
|
def next_cek(box_keys, enc)
|
101
117
|
if bits.nil?
|
102
118
|
other_public_key, my_private_key = box_keys
|
119
|
+
if my_private_key.nil?
|
120
|
+
raise ArgumentError, "missing 'my_private_key'"
|
121
|
+
elsif other_public_key.nil?
|
122
|
+
raise ArgumentError, "missing 'other_public_key'"
|
123
|
+
elsif epk and my_private_key and epk.thumbprint != my_private_key.thumbprint
|
124
|
+
raise ArgumentError, "private and ephemeral public key mismatch"
|
125
|
+
end
|
126
|
+
new_alg = self
|
127
|
+
if epk.nil?
|
128
|
+
new_alg = JOSE::JWE::ALG_ECDH_ES.new(bits, my_private_key.to_public, apu, apv)
|
129
|
+
end
|
103
130
|
z = other_public_key.derive_key(my_private_key)
|
104
131
|
algorithm_id = enc.algorithm
|
105
132
|
key_data_len = enc.bits
|
106
133
|
supp_pub_info = [key_data_len].pack('N')
|
107
|
-
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, apu, apv, supp_pub_info], key_data_len)
|
108
|
-
return derived_key
|
134
|
+
derived_key = JOSE::JWA::ConcatKDF.kdf(OpenSSL::Digest::SHA256, z, [algorithm_id, new_alg.apu, new_alg.apv, supp_pub_info], key_data_len)
|
135
|
+
return derived_key, new_alg
|
109
136
|
else
|
110
|
-
return enc.next_cek
|
137
|
+
return enc.next_cek, self
|
111
138
|
end
|
112
139
|
end
|
113
140
|
|
data/lib/jose/jwe/alg_pbes2.rb
CHANGED
@@ -20,7 +20,7 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
20
20
|
end
|
21
21
|
iter = nil
|
22
22
|
if not fields.has_key?('p2c')
|
23
|
-
iter =
|
23
|
+
iter = bits * 32
|
24
24
|
elsif fields['p2c'].is_a?(Integer) and fields['p2c'] >= 0
|
25
25
|
iter = fields['p2c']
|
26
26
|
else
|
@@ -76,7 +76,7 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
76
76
|
end
|
77
77
|
new_alg = self
|
78
78
|
if new_alg.salt.nil?
|
79
|
-
new_alg = JOSE::JWE::ALG_PBES2.new(hmac, bits, wrap_salt(SecureRandom.random_bytes(8)), iter)
|
79
|
+
new_alg = JOSE::JWE::ALG_PBES2.new(hmac, bits, wrap_salt(SecureRandom.random_bytes(bits.div(8))), iter)
|
80
80
|
end
|
81
81
|
derived_key = OpenSSL::PKCS5.pbkdf2_hmac(key, new_alg.salt, new_alg.iter, new_alg.bits.div(8) + (new_alg.bits % 8), new_alg.hmac.new)
|
82
82
|
encrypted_key = JOSE::JWA::AES_KW.wrap(decrypted_key, derived_key)
|
@@ -84,7 +84,7 @@ class JOSE::JWE::ALG_PBES2 < Struct.new(:hmac, :bits, :salt, :iter)
|
|
84
84
|
end
|
85
85
|
|
86
86
|
def next_cek(key, enc)
|
87
|
-
return enc.next_cek
|
87
|
+
return enc.next_cek, self
|
88
88
|
end
|
89
89
|
|
90
90
|
# API functions
|
data/lib/jose/jwe/alg_rsa.rb
CHANGED
data/lib/jose/jwk.rb
CHANGED
@@ -1,8 +1,45 @@
|
|
1
1
|
module JOSE
|
2
|
+
# JWK stands for JSON Web Key which is defined in [RFC 7517](https://tools.ietf.org/html/rfc7517).
|
2
3
|
class JWK < Struct.new(:keys, :kty, :fields)
|
3
4
|
|
4
5
|
# Decode API
|
5
6
|
|
7
|
+
# Converts a binary or map into a `JOSE.JWK`.
|
8
|
+
#
|
9
|
+
# !!!ruby
|
10
|
+
# JOSE::JWK.from({"k" => "", "kty" => "oct"})
|
11
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="">, fields=JOSE::Map[]>
|
12
|
+
# JOSE::JWK.from("{\"k\":\"\",\"kty\":\"oct\"}")
|
13
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="">, fields=JOSE::Map[]>
|
14
|
+
#
|
15
|
+
# The `"kty"` field may be overridden with a custom module that implements the {JOSE::JWK::KTY JOSE::JWK::KTY} behaviours.
|
16
|
+
#
|
17
|
+
# For example:
|
18
|
+
#
|
19
|
+
# !!!ruby
|
20
|
+
# JOSE::JWK.from({ "kty" => "custom" }, { kty: MyCustomKey })
|
21
|
+
# # => #<struct JOSE::JWK keys=nil, kty=#<MyCustomKey:0x007f8c5419ff68>, fields=JOSE::Map[]>
|
22
|
+
#
|
23
|
+
# If a `key` has been specified, it will decrypt an encrypted binary or map into a {JOSE::JWK JOSE::JWK} using the specified `key`.
|
24
|
+
#
|
25
|
+
# !!!ruby
|
26
|
+
# JOSE::JWK.from("eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJjdHkiOiJqd2sranNvbiIsImVuYyI6IkExMjhHQ00iLCJwMmMiOjQwOTYsInAycyI6Im5OQ1ZNQUktNTU5UVFtbWRFcnBsZFEifQ.Ucye69ii4dxd1ykNFlJyBVeA6xeNu4aV.2pZ4nBoxBjmdrneS.boqwdFZVNAFHk1M5P6kPYgBUgGwW32QuKzHuFA.wL9Hy6dcE_DPkUW9s5iwKA", "password")
|
27
|
+
# # => [#<struct JOSE::JWK keys=nil, kty=#<struct JOSE::JWK::KTY_oct oct="secret">, fields=JOSE::Map[]>,
|
28
|
+
# # #<struct JOSE::JWE
|
29
|
+
# # alg=
|
30
|
+
# # #<struct JOSE::JWE::ALG_PBES2
|
31
|
+
# # hmac=OpenSSL::Digest::SHA256,
|
32
|
+
# # bits=128,
|
33
|
+
# # salt="PBES2-HS256+A128KW\x00\x9C\xD0\x950\x02>\xE7\x9FPBi\x9D\x12\xBAeu",
|
34
|
+
# # iter=4096>,
|
35
|
+
# # enc=#<struct JOSE::JWE::ENC_AES_GCM cipher_name="aes-128-gcm", bits=128, cek_len=16, iv_len=12>,
|
36
|
+
# # zip=nil,
|
37
|
+
# # fields=JOSE::Map["cty" => "jwk+json"]>]
|
38
|
+
#
|
39
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] object
|
40
|
+
# @param [Hash] modules
|
41
|
+
# @param [String] key
|
42
|
+
# @return [JOSE::JWK, Array<JOSE::JWK>]
|
6
43
|
def self.from(object, modules = nil, key = nil)
|
7
44
|
case object
|
8
45
|
when JOSE::Map, Hash
|
@@ -11,11 +48,19 @@ module JOSE
|
|
11
48
|
return from_binary(object, modules, key)
|
12
49
|
when JOSE::JWK
|
13
50
|
return object
|
51
|
+
when Array
|
52
|
+
return object.map { |obj| from(obj, modules, key) }
|
14
53
|
else
|
15
|
-
raise ArgumentError, "'object' must be a Hash, String,
|
54
|
+
raise ArgumentError, "'object' must be a Hash, String, JOSE::JWK, or Array"
|
16
55
|
end
|
17
56
|
end
|
18
57
|
|
58
|
+
# Converts a binary into a {JOSE::JWK JOSE::JWK}.
|
59
|
+
#
|
60
|
+
# @param [String, Array<String>] object
|
61
|
+
# @param [Hash] modules
|
62
|
+
# @param [String] key
|
63
|
+
# @return [JOSE::JWK, Array<JOSE::JWK>]
|
19
64
|
def self.from_binary(object, modules = nil, key = nil)
|
20
65
|
if (modules.is_a?(String) or modules.is_a?(JOSE::JWK)) and key.nil?
|
21
66
|
key = modules
|
@@ -30,20 +75,39 @@ module JOSE
|
|
30
75
|
else
|
31
76
|
return from_map(JOSE.decode(object), modules)
|
32
77
|
end
|
78
|
+
when Array
|
79
|
+
return object.map { |obj| from_binary(obj, modules, key) }
|
33
80
|
else
|
34
|
-
raise ArgumentError, "'object' must be a String"
|
81
|
+
raise ArgumentError, "'object' must be a String or Array"
|
35
82
|
end
|
36
83
|
end
|
37
84
|
|
85
|
+
# Reads file and calls {.from_binary} to convert into a {JOSE::JWK JOSE::JWK}.
|
86
|
+
#
|
87
|
+
# @param [String] file
|
88
|
+
# @param [Hash] modules
|
89
|
+
# @param [String] key
|
90
|
+
# @return [JOSE::JWK]
|
38
91
|
def self.from_file(file, modules = nil, key = nil)
|
39
92
|
return from_binary(File.binread(file), modules, key)
|
40
93
|
end
|
41
94
|
|
95
|
+
# Converts Ruby records for EC and RSA keys into a {JOSE::JWK JOSE::JWK}.
|
96
|
+
#
|
97
|
+
# @param [OpenSSL::PKey] object
|
98
|
+
# @param [Hash] modules
|
99
|
+
# @return [JOSE::JWK]
|
42
100
|
def self.from_key(object, modules = {})
|
43
101
|
kty = modules[:kty] || JOSE::JWK::KTY
|
44
102
|
return JOSE::JWK.new(nil, *kty.from_key(object))
|
45
103
|
end
|
46
104
|
|
105
|
+
# Converts a map into a {JOSE::JWK JOSE::JWK}.
|
106
|
+
#
|
107
|
+
# @param [JOSE::Map, Hash, Array<JOSE::Map, Hash>] object
|
108
|
+
# @param [Hash] modules
|
109
|
+
# @param [String] key
|
110
|
+
# @return [JOSE::JWK, Array<JOSE::JWK>]
|
47
111
|
def self.from_map(object, modules = nil, key = nil)
|
48
112
|
if (modules.is_a?(String) or modules.is_a?(JOSE::JWK)) and key.nil?
|
49
113
|
key = modules
|
@@ -58,34 +122,37 @@ module JOSE
|
|
58
122
|
else
|
59
123
|
return from_fields(JOSE::JWK.new(nil, nil, JOSE::Map.new(object)), modules)
|
60
124
|
end
|
125
|
+
when Array
|
126
|
+
return object.map { |obj| from_map(obj, modules, key) }
|
61
127
|
else
|
62
|
-
raise ArgumentError, "'object' must be a
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.from_pem(object, modules = nil, password = nil)
|
67
|
-
if modules.is_a?(String) and password.nil?
|
68
|
-
password = modules
|
69
|
-
modules = {}
|
128
|
+
raise ArgumentError, "'object' must be a JOSE::Map, Hash, or Array"
|
70
129
|
end
|
71
|
-
modules ||= {}
|
72
|
-
kty = modules[:kty] || JOSE::JWK::PEM
|
73
|
-
return JOSE::JWK.new(nil, *kty.from_binary(object, password))
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.from_pem_file(file, modules = nil, password = nil)
|
77
|
-
return from_pem(File.binread(file), modules, password)
|
78
130
|
end
|
79
131
|
|
132
|
+
# Converts an arbitrary binary into a {JOSE::JWK JOSE::JWK} with `"kty"` of `"oct"`.
|
133
|
+
#
|
134
|
+
# @param [String] object
|
135
|
+
# @param [Hash] modules
|
136
|
+
# @return [JOSE::JWK]
|
80
137
|
def self.from_oct(object, modules = {})
|
81
138
|
kty = modules[:kty] || JOSE::JWK::KTY_oct
|
82
139
|
return JOSE::JWK.new(nil, *kty.from_oct(object))
|
83
140
|
end
|
84
141
|
|
142
|
+
# Reads file and calls {JOSE::JWK.from_oct JOSE::JWK.from_oct} to convert into a {JOSE::JWK JOSE::JWK}.
|
143
|
+
#
|
144
|
+
# @param [String] file
|
145
|
+
# @param [Hash] modules
|
146
|
+
# @return [JOSE::JWK]
|
85
147
|
def self.from_oct_file(file, modules = {})
|
86
148
|
return from_oct(File.binread(file), modules)
|
87
149
|
end
|
88
150
|
|
151
|
+
# Converts an octet key pair into a {JOSE::JWK JOSE::JWK} with `"kty"` of `"OKP"`.
|
152
|
+
#
|
153
|
+
# @param [Array] object
|
154
|
+
# @param [Hash] modules
|
155
|
+
# @return [JOSE::JWK]
|
89
156
|
def self.from_okp(object, modules = {})
|
90
157
|
raise ArgumentError, "object must be an Array of length 2" if not object.is_a?(Array) or object.length != 2
|
91
158
|
kty = modules[:kty] || case object[0]
|
@@ -107,12 +174,100 @@ module JOSE
|
|
107
174
|
return JOSE::JWK.new(nil, *kty.from_okp(object))
|
108
175
|
end
|
109
176
|
|
177
|
+
# Converts an openssh key into a {JOSE::JWK JOSE::JWK} with `"kty"` of `"OKP"`.
|
178
|
+
#
|
179
|
+
# @param [String, Array] object
|
180
|
+
# @param [Hash] modules
|
181
|
+
# @return [JOSE::JWK]
|
182
|
+
def self.from_openssh_key(object, modules = {})
|
183
|
+
raise ArgumentError, "object must be a String or Array" if not object.is_a?(String) and not object.is_a?(Array)
|
184
|
+
keys = object
|
185
|
+
if object.is_a?(String)
|
186
|
+
keys = JOSE::JWK::OpenSSHKey.from_binary(object)
|
187
|
+
end
|
188
|
+
((pk_type, pk), key), = keys[0]
|
189
|
+
sk_type, sk_pk, = key
|
190
|
+
if pk_type and pk and key and sk_type and sk_pk and pk_type == sk_type and pk == sk_pk
|
191
|
+
kty = modules[:kty] || case pk_type
|
192
|
+
when 'ssh-ed25519'
|
193
|
+
JOSE::JWK::KTY_OKP_Ed25519
|
194
|
+
when 'ssh-ed25519ph'
|
195
|
+
JOSE::JWK::KTY_OKP_Ed25519ph
|
196
|
+
when 'ssh-ed448'
|
197
|
+
JOSE::JWK::KTY_OKP_Ed448
|
198
|
+
when 'ssh-ed448ph'
|
199
|
+
JOSE::JWK::KTY_OKP_Ed448ph
|
200
|
+
when 'ssh-x25519'
|
201
|
+
JOSE::JWK::KTY_OKP_X25519
|
202
|
+
when 'ssh-x448'
|
203
|
+
JOSE::JWK::KTY_OKP_X448
|
204
|
+
else
|
205
|
+
raise ArgumentError, "unrecognized openssh key type: #{pk_type.inspect}"
|
206
|
+
end
|
207
|
+
return JOSE::JWK.new(nil, *kty.from_openssh_key(key))
|
208
|
+
else
|
209
|
+
raise ArgumentError, "unrecognized openssh key format"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# Reads file and calls {JOSE::JWK.from_openssh_key JOSE::JWK.from_openssh_key} to convert into a {JOSE::JWK JOSE::JWK}.
|
214
|
+
#
|
215
|
+
# @param [String] file
|
216
|
+
# @param [Hash] modules
|
217
|
+
# @return [JOSE::JWK]
|
218
|
+
def self.from_openssh_key_file(file, modules = {})
|
219
|
+
return from_openssh_key(File.binread(file), modules)
|
220
|
+
end
|
221
|
+
|
222
|
+
# Converts a PEM (Privacy Enhanced Email) binary into a {JOSE::JWK JOSE::JWK}.
|
223
|
+
#
|
224
|
+
# If `password` is present, decrypts an encrypted PEM (Privacy Enhanced Email) binary into a {JOSE::JWK JOSE::JWK} using `password`.
|
225
|
+
#
|
226
|
+
# @param [String] object
|
227
|
+
# @param [Hash] modules
|
228
|
+
# @param [String] password
|
229
|
+
# @return [JOSE::JWK]
|
230
|
+
def self.from_pem(object, modules = nil, password = nil)
|
231
|
+
if modules.is_a?(String) and password.nil?
|
232
|
+
password = modules
|
233
|
+
modules = {}
|
234
|
+
end
|
235
|
+
modules ||= {}
|
236
|
+
kty = modules[:kty] || JOSE::JWK::PEM
|
237
|
+
return JOSE::JWK.new(nil, *kty.from_binary(object, password))
|
238
|
+
end
|
239
|
+
|
240
|
+
# Reads file and calls {JOSE::JWK.from_pem JOSE::JWK.from_pem} to convert into a {JOSE::JWK JOSE::JWK}.
|
241
|
+
#
|
242
|
+
# @param [String] file
|
243
|
+
# @param [Hash] modules
|
244
|
+
# @param [String] password
|
245
|
+
# @return [JOSE::JWK]
|
246
|
+
def self.from_pem_file(file, modules = nil, password = nil)
|
247
|
+
return from_pem(File.binread(file), modules, password)
|
248
|
+
end
|
249
|
+
|
110
250
|
# Encode API
|
111
251
|
|
252
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a binary.
|
253
|
+
#
|
254
|
+
# @param [JOSE::JWK, Array<JOSE::JWK>] jwk
|
255
|
+
# @param [String] key
|
256
|
+
# @param [JOSE::JWE] jwe
|
257
|
+
# @return [String, JOSE::EncryptedBinary, Array<String, JOSE::EncryptedBinary>]
|
112
258
|
def self.to_binary(jwk, key = nil, jwe = nil)
|
113
|
-
|
259
|
+
if jwk.is_a?(Array)
|
260
|
+
return jwk.map { |obj| from(obj).to_binary(key, jwe) }
|
261
|
+
else
|
262
|
+
return from(jwk).to_binary(key, jwe)
|
263
|
+
end
|
114
264
|
end
|
115
265
|
|
266
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a binary.
|
267
|
+
#
|
268
|
+
# @param [String] key
|
269
|
+
# @param [JOSE::JWE] jwe
|
270
|
+
# @return [String, JOSE::EncryptedBinary]
|
116
271
|
def to_binary(key = nil, jwe = nil)
|
117
272
|
if not key.nil?
|
118
273
|
jwe ||= kty.key_encryptor(fields, key)
|
@@ -124,26 +279,61 @@ module JOSE
|
|
124
279
|
end
|
125
280
|
end
|
126
281
|
|
282
|
+
# Calls {JOSE::JWK.to_binary JOSE::JWK.to_binary} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
283
|
+
#
|
284
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] jwk
|
285
|
+
# @param [String] file
|
286
|
+
# @param [String] key
|
287
|
+
# @param [JOSE::JWE] jwe
|
288
|
+
# @return [Fixnum] bytes written
|
127
289
|
def self.to_file(jwk, file, key = nil, jwe = nil)
|
128
290
|
return from(jwk).to_file(file, key, jwe)
|
129
291
|
end
|
130
292
|
|
293
|
+
# Calls {JOSE::JWK.to_binary JOSE::JWK.to_binary} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
294
|
+
#
|
295
|
+
# @param [String] file
|
296
|
+
# @param [String] key
|
297
|
+
# @param [JOSE::JWE] jwe
|
298
|
+
# @return [Fixnum] bytes written
|
131
299
|
def to_file(file, key = nil, jwe = nil)
|
132
300
|
return File.binwrite(file, to_binary(key, jwe))
|
133
301
|
end
|
134
302
|
|
303
|
+
# Converts a {JOSE::JWK JOSE::JWK} into the raw key format.
|
304
|
+
#
|
305
|
+
# @param [JOSE::JWK] jwk
|
306
|
+
# @return [OpenSSL::PKey, Object]
|
135
307
|
def self.to_key(jwk)
|
136
308
|
return from(jwk).to_key
|
137
309
|
end
|
138
310
|
|
311
|
+
# Converts a {JOSE::JWK JOSE::JWK} into the raw key format.
|
312
|
+
#
|
313
|
+
# @return [OpenSSL::PKey, Object]
|
139
314
|
def to_key
|
140
315
|
return kty.to_key
|
141
316
|
end
|
142
317
|
|
318
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a map.
|
319
|
+
#
|
320
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
321
|
+
# @param [String] key
|
322
|
+
# @param [JOSE::JWE] jwe
|
323
|
+
# @return [JOSE::Map, Array<JOSE::Map>]
|
143
324
|
def self.to_map(jwk, key = nil, jwe = nil)
|
144
|
-
|
325
|
+
if jwk.is_a?(Array)
|
326
|
+
return from(jwk).map { |obj| obj.to_map(key, jwe) }
|
327
|
+
else
|
328
|
+
return from(jwk).to_map(key, jwe)
|
329
|
+
end
|
145
330
|
end
|
146
331
|
|
332
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a map.
|
333
|
+
#
|
334
|
+
# @param [String] key
|
335
|
+
# @param [JOSE::JWE] jwe
|
336
|
+
# @return [JOSE::Map]
|
147
337
|
def to_map(key = nil, jwe = nil)
|
148
338
|
if not key.nil?
|
149
339
|
jwe ||= kty.key_encryptor(fields, key)
|
@@ -155,98 +345,363 @@ module JOSE
|
|
155
345
|
end
|
156
346
|
end
|
157
347
|
|
348
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a raw binary octet.
|
349
|
+
#
|
350
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
351
|
+
# @return [String, Array<String>]
|
158
352
|
def self.to_oct(jwk)
|
159
|
-
|
353
|
+
if jwk.is_a?(Array)
|
354
|
+
return from(jwk).map { |obj| obj.to_oct }
|
355
|
+
else
|
356
|
+
return from(jwk).to_oct
|
357
|
+
end
|
160
358
|
end
|
161
359
|
|
360
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a raw binary octet.
|
361
|
+
#
|
362
|
+
# @return [String]
|
162
363
|
def to_oct
|
163
364
|
return kty.to_oct
|
164
365
|
end
|
165
366
|
|
367
|
+
# Calls {JOSE::JWK.to_oct JOSE::JWK.to_oct} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
368
|
+
#
|
369
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] jwk
|
370
|
+
# @param [String] file
|
371
|
+
# @return [Fixnum] bytes written
|
372
|
+
def self.to_oct_file(jwk, file)
|
373
|
+
return from(jwk).to_file(file)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Calls {JOSE::JWK#to_oct JOSE::JWK#to_oct} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
377
|
+
#
|
378
|
+
# @param [String] file
|
379
|
+
# @return [Fixnum] bytes written
|
380
|
+
def to_oct_file(file)
|
381
|
+
return File.binwrite(file, to_oct)
|
382
|
+
end
|
383
|
+
|
384
|
+
# Converts a {JOSE::JWK JOSE::JWK} into an octet key pair.
|
385
|
+
#
|
386
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
387
|
+
# @return [Object, Array<Object>]
|
166
388
|
def self.to_okp(jwk)
|
167
|
-
|
389
|
+
if jwk.is_a?(Array)
|
390
|
+
return from(jwk).map { |obj| obj.to_okp }
|
391
|
+
else
|
392
|
+
return from(jwk).to_okp
|
393
|
+
end
|
168
394
|
end
|
169
395
|
|
396
|
+
# Converts a {JOSE::JWK JOSE::JWK} into an octet key pair.
|
397
|
+
#
|
398
|
+
# @return [String]
|
170
399
|
def to_okp
|
171
400
|
return kty.to_okp
|
172
401
|
end
|
173
402
|
|
403
|
+
# Converts a {JOSE::JWK JOSE::JWK} into an OpenSSH key binary.
|
404
|
+
#
|
405
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
406
|
+
# @return [Object, Array<Object>]
|
407
|
+
def self.to_openssh_key(jwk)
|
408
|
+
if jwk.is_a?(Array)
|
409
|
+
return from(jwk).map { |obj| obj.to_openssh_key }
|
410
|
+
else
|
411
|
+
return from(jwk).to_openssh_key
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
# Converts a {JOSE::JWK JOSE::JWK} into an OpenSSH key binary.
|
416
|
+
#
|
417
|
+
# @return [Object]
|
418
|
+
def to_openssh_key
|
419
|
+
return kty.to_openssh_key(fields)
|
420
|
+
end
|
421
|
+
|
422
|
+
# Calls {JOSE::JWK.to_openssh_key JOSE::JWK.to_openssh_key} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
423
|
+
#
|
424
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] jwk
|
425
|
+
# @param [String] file
|
426
|
+
# @return [Fixnum] bytes written
|
427
|
+
def self.to_openssh_key_file(jwk, file)
|
428
|
+
return from(jwk).to_file(file)
|
429
|
+
end
|
430
|
+
|
431
|
+
# Calls {JOSE::JWK#to_openssh_key JOSE::JWK#to_openssh_key} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
432
|
+
#
|
433
|
+
# @param [String] file
|
434
|
+
# @return [Fixnum] bytes written
|
435
|
+
def to_openssh_key_file(file)
|
436
|
+
return File.binwrite(file, to_openssh_key)
|
437
|
+
end
|
438
|
+
|
439
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a PEM (Privacy Enhanced Email) binary.
|
440
|
+
#
|
441
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
442
|
+
# @param [String] password
|
443
|
+
# @return [String, Array<String>]
|
174
444
|
def self.to_pem(jwk, password = nil)
|
175
|
-
|
445
|
+
if jwk.is_a?(Array)
|
446
|
+
return from(jwk).map { |obj| obj.to_pem(password) }
|
447
|
+
else
|
448
|
+
return from(jwk).to_pem(password)
|
449
|
+
end
|
176
450
|
end
|
177
451
|
|
452
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a PEM (Privacy Enhanced Email) binary.
|
453
|
+
#
|
454
|
+
# @param [String] password
|
455
|
+
# @return [String]
|
178
456
|
def to_pem(password = nil)
|
179
457
|
return kty.to_pem(password)
|
180
458
|
end
|
181
459
|
|
460
|
+
# Calls {JOSE::JWK.to_pem JOSE::JWK.to_pem} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
461
|
+
#
|
462
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] jwk
|
463
|
+
# @param [String] file
|
464
|
+
# @param [String] password
|
465
|
+
# @return [Fixnum] bytes written
|
466
|
+
def self.to_pem_file(jwk, file, password = nil)
|
467
|
+
return from(jwk).to_file(file, password)
|
468
|
+
end
|
469
|
+
|
470
|
+
# Calls {JOSE::JWK#to_pem JOSE::JWK#to_pem} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
471
|
+
#
|
472
|
+
# @param [String] file
|
473
|
+
# @param [String] password
|
474
|
+
# @return [Fixnum] bytes written
|
475
|
+
def to_pem_file(file, password = nil)
|
476
|
+
return File.binwrite(file, to_pem(password))
|
477
|
+
end
|
478
|
+
|
479
|
+
# Converts a private {JOSE::JWK JOSE::JWK} into a public {JOSE::JWK JOSE::JWK}.
|
480
|
+
#
|
481
|
+
# !!!ruby
|
482
|
+
# jwk_rsa = JOSE::JWK.generate_key([:rsa, 256]).to_map
|
483
|
+
# # => JOSE::Map[
|
484
|
+
# # "dq" => "Iv_BghpjRyv8hk4AgsX_3w",
|
485
|
+
# # "e" => "AQAB",
|
486
|
+
# # "d" => "imiCh2gK77pDAa_NuQbHN1hZdLY0eTl8tp4WLfe1uQ0",
|
487
|
+
# # "p" => "-eKE_wk7O5JWw_1fw-rciw",
|
488
|
+
# # "qi" => "MqCwIoTTCkYmGQHsOM7IuA",
|
489
|
+
# # "n" => "vj2WbxlGF1yU9SoQJMqKw6c2asTks_cVuXEAO3x_yOU",
|
490
|
+
# # "kty" => "RSA",
|
491
|
+
# # "q" => "wuVog_0-60w7_56y8wZuTw",
|
492
|
+
# # "dp" => "lU_9GEdz1UzD-6hSqMaVsQ"]
|
493
|
+
# JOSE::JWK.to_public(jwk_rsa).to_map
|
494
|
+
# # => JOSE::Map[
|
495
|
+
# # "e" => "AQAB",
|
496
|
+
# # "n" => "vj2WbxlGF1yU9SoQJMqKw6c2asTks_cVuXEAO3x_yOU",
|
497
|
+
# # "kty" => "RSA"]
|
498
|
+
#
|
499
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
500
|
+
# @return [JOSE::JWK, Array<JOSE::JWK>]
|
182
501
|
def self.to_public(jwk)
|
183
|
-
|
502
|
+
if jwk.is_a?(Array)
|
503
|
+
return from(jwk).map { |obj| obj.to_public }
|
504
|
+
else
|
505
|
+
return from(jwk).to_public
|
506
|
+
end
|
184
507
|
end
|
185
508
|
|
509
|
+
# Converts a private {JOSE::JWK JOSE::JWK} into a public {JOSE::JWK JOSE::JWK}.
|
510
|
+
#
|
511
|
+
# @see JOSE::JWK.to_public
|
512
|
+
# @return [JOSE::JWK]
|
186
513
|
def to_public
|
187
514
|
return JOSE::JWK.from_map(to_public_map)
|
188
515
|
end
|
189
516
|
|
517
|
+
# Calls {JOSE::JWK.to_binary JOSE::JWK.to_binary} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
518
|
+
#
|
519
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] jwk
|
520
|
+
# @param [String] file
|
521
|
+
# @return [Fixnum] bytes written
|
522
|
+
def self.to_public_file(jwk, file)
|
523
|
+
return from(jwk).to_public_file(file)
|
524
|
+
end
|
525
|
+
|
526
|
+
# Calls {JOSE::JWK.to_public JOSE::JWK.to_public} on a {JOSE::JWK JOSE::JWK} and then writes the binary to `file`.
|
527
|
+
#
|
528
|
+
# @param [String] file
|
529
|
+
# @return [Fixnum] bytes written
|
530
|
+
def to_public_file(file)
|
531
|
+
return File.binwrite(file, to_public.to_binary)
|
532
|
+
end
|
533
|
+
|
534
|
+
# Calls {JOSE::JWK.to_public JOSE::JWK.to_public} and then {JOSE::JWK.to_key JOSE::JWK.to_key} on a {JOSE::JWK JOSE::JWK}.
|
535
|
+
#
|
536
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
537
|
+
# @return [OpenSSL::PKey, Object, Array<OpenSSL::PKey, Object>]
|
190
538
|
def self.to_public_key(jwk)
|
191
|
-
|
539
|
+
if jwk.is_a?(Array)
|
540
|
+
return from(jwk).map { |obj| obj.to_public_key }
|
541
|
+
else
|
542
|
+
return from(jwk).to_public_key
|
543
|
+
end
|
192
544
|
end
|
193
545
|
|
546
|
+
# Calls {JOSE::JWK#to_public JOSE::JWK#to_public} and then {JOSE::JWK#to_key JOSE::JWK#to_key} on a {JOSE::JWK JOSE::JWK}.
|
547
|
+
#
|
548
|
+
# @return [OpenSSL::PKey, Object]
|
194
549
|
def to_public_key
|
195
550
|
return to_public.to_key
|
196
551
|
end
|
197
552
|
|
553
|
+
# Calls {JOSE::JWK.to_public JOSE::JWK.to_public} and then {JOSE::JWK.to_map JOSE::JWK.to_map} on a {JOSE::JWK JOSE::JWK}.
|
554
|
+
#
|
555
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
556
|
+
# @return [JOSE::Map, Array<JOSE::Map>]
|
198
557
|
def self.to_public_map(jwk)
|
199
|
-
|
558
|
+
if jwk.is_a?(Array)
|
559
|
+
return from(jwk).map { |obj| obj.to_public_map }
|
560
|
+
else
|
561
|
+
return from(jwk).to_public_map
|
562
|
+
end
|
200
563
|
end
|
201
564
|
|
565
|
+
# Calls {JOSE::JWK#to_public JOSE::JWK#to_public} and then {JOSE::JWK#to_map JOSE::JWK#to_map} on a {JOSE::JWK JOSE::JWK}.
|
566
|
+
#
|
567
|
+
# @return [JOSE::Map]
|
202
568
|
def to_public_map
|
203
569
|
return kty.to_public_map(fields)
|
204
570
|
end
|
205
571
|
|
572
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a map that can be used by {JOSE::JWK.thumbprint JOSE::JWK.thumbprint}.
|
573
|
+
#
|
574
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK, Array<JOSE::Map, Hash, String, JOSE::JWK>] jwk
|
575
|
+
# @return [JOSE::Map, Array<JOSE::Map>]
|
206
576
|
def self.to_thumbprint_map(jwk)
|
207
|
-
|
577
|
+
if jwk.is_a?(Array)
|
578
|
+
return from(jwk).map { |obj| obj.to_thumbprint_map }
|
579
|
+
else
|
580
|
+
return from(jwk).to_thumbprint_map
|
581
|
+
end
|
208
582
|
end
|
209
583
|
|
584
|
+
# Converts a {JOSE::JWK JOSE::JWK} into a map that can be used by {JOSE::JWK.thumbprint JOSE::JWK.thumbprint}.
|
585
|
+
#
|
586
|
+
# @return [JOSE::Map]
|
210
587
|
def to_thumbprint_map
|
211
588
|
return kty.to_thumbprint_map(fields)
|
212
589
|
end
|
213
590
|
|
214
591
|
# API
|
215
592
|
|
593
|
+
# Decrypts the `encrypted` binary or map using the `jwk`.
|
594
|
+
#
|
595
|
+
# @see JOWE::JWE.block_decrypt
|
596
|
+
# @param [JOSE::JWK] jwk
|
597
|
+
# @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
|
598
|
+
# @return [[String, JOSE::JWE]]
|
216
599
|
def self.block_decrypt(jwk, encrypted)
|
217
600
|
return from(jwk).block_decrypt(encrypted)
|
218
601
|
end
|
219
602
|
|
603
|
+
# Decrypts the `encrypted` binary or map using the `jwk`.
|
604
|
+
#
|
605
|
+
# @see JOWE::JWE.block_decrypt
|
606
|
+
# @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
|
607
|
+
# @return [[String, JOSE::JWE]]
|
220
608
|
def block_decrypt(encrypted)
|
221
609
|
return JOSE::JWE.block_decrypt(self, encrypted)
|
222
610
|
end
|
223
611
|
|
612
|
+
# Encrypts the `plain_text` using the `jwk` and algorithms specified by the `jwe`.
|
613
|
+
#
|
614
|
+
# @see JOSE::JWE.block_encrypt
|
615
|
+
# @param [JOSE::JWK] jwk
|
616
|
+
# @param [String] plain_text
|
617
|
+
# @param [JOSE::JWE] jwe
|
618
|
+
# @return [JOSE::EncryptedMap]
|
224
619
|
def self.block_encrypt(jwk, plain_text, jwe = nil)
|
225
620
|
return from(jwk).block_encrypt(plain_text, jwe)
|
226
621
|
end
|
227
622
|
|
623
|
+
# Encrypts the `plain_text` using the `jwk` and algorithms specified by the `jwe`.
|
624
|
+
#
|
625
|
+
# @see JOSE::JWE.block_encrypt
|
626
|
+
# @param [String] plain_text
|
627
|
+
# @param [JOSE::JWE] jwe
|
628
|
+
# @return [JOSE::EncryptedMap]
|
228
629
|
def block_encrypt(plain_text, jwe = nil)
|
229
630
|
jwe ||= block_encryptor
|
230
631
|
return JOSE::JWE.block_encrypt(self, plain_text, jwe)
|
231
632
|
end
|
232
633
|
|
233
|
-
|
234
|
-
|
634
|
+
# Returns a block encryptor map for the key type.
|
635
|
+
#
|
636
|
+
# @param [JOSE::JWK, Array<JOSE::JWK>] jwk
|
637
|
+
# @return [JOSE::Map, Array<JOSE::Map>]
|
638
|
+
def self.block_encryptor(jwk)
|
639
|
+
if jwk.is_a?(Array)
|
640
|
+
return from(jwk).map { |obj| obj.block_encryptor }
|
641
|
+
else
|
642
|
+
return from(jwk).block_encryptor
|
643
|
+
end
|
235
644
|
end
|
236
645
|
|
646
|
+
# Returns a block encryptor map for the key type.
|
647
|
+
#
|
648
|
+
# @return [JOSE::Map]
|
237
649
|
def block_encryptor
|
238
650
|
return kty.block_encryptor(fields)
|
239
651
|
end
|
240
652
|
|
241
|
-
|
242
|
-
|
653
|
+
# Key Agreement decryption of the `encrypted` binary or map using `my_private_jwk`.
|
654
|
+
#
|
655
|
+
# @see JOSE::JWK.box_encrypt
|
656
|
+
# @see JOSE::JWE.block_decrypt
|
657
|
+
# @param [JOSE::JWK] jwk
|
658
|
+
# @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
|
659
|
+
# @param [JOSE::JWK] public_jwk
|
660
|
+
# @return [[String, JOSE::JWE]]
|
661
|
+
def self.box_decrypt(jwk, encrypted, public_jwk = nil)
|
662
|
+
return from(jwk).box_decrypt(encrypted, public_jwk)
|
243
663
|
end
|
244
664
|
|
245
|
-
|
246
|
-
|
665
|
+
# Key Agreement decryption of the `encrypted` binary or map using `my_private_jwk`.
|
666
|
+
#
|
667
|
+
# @see JOSE::JWK.box_encrypt
|
668
|
+
# @see JOSE::JWE.block_decrypt
|
669
|
+
# @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
|
670
|
+
# @param [JOSE::JWK] public_jwk
|
671
|
+
# @return [[String, JOSE::JWE]]
|
672
|
+
def box_decrypt(encrypted, public_jwk = nil)
|
673
|
+
if public_jwk
|
674
|
+
return JOSE::JWE.block_decrypt([public_jwk, self], encrypted)
|
675
|
+
else
|
676
|
+
return JOSE::JWE.block_decrypt(self, encrypted)
|
677
|
+
end
|
247
678
|
end
|
248
679
|
|
249
|
-
#
|
680
|
+
# Key Agreement encryption of `plain_text` by generating an ephemeral private key based on `other_public_jwk` curve.
|
681
|
+
#
|
682
|
+
# If no private key has been specified in `box_keys`, it generates an ephemeral private key based on other public key curve.
|
683
|
+
#
|
684
|
+
# @see JOSE::JWK.box_decrypt
|
685
|
+
# @see JOSE::JWE.block_encrypt
|
686
|
+
# @param [String] plain_text
|
687
|
+
# @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] box_keys
|
688
|
+
# @param [JOSE::JWE] jwe
|
689
|
+
# @return [JOSE::EncryptedMap, [JOSE::EncryptedMap, JOSE::JWK]]
|
690
|
+
def self.box_encrypt(plain_text, box_keys, jwe = nil)
|
691
|
+
other_public_key, my_private_key = box_keys
|
692
|
+
return from(other_public_key).box_encrypt(plain_text, my_private_jwk, jwe)
|
693
|
+
end
|
694
|
+
|
695
|
+
# Key Agreement encryption of `plain_text` by generating an ephemeral private key based on `other_public_jwk` curve.
|
696
|
+
#
|
697
|
+
# If no private key has been specified in `my_private_key`, it generates an ephemeral private key based on other public key curve.
|
698
|
+
#
|
699
|
+
# @see JOSE::JWK.box_decrypt
|
700
|
+
# @see JOSE::JWE.block_encrypt
|
701
|
+
# @param [String] plain_text
|
702
|
+
# @param [JOSE::JWK] my_private_jwk
|
703
|
+
# @param [JOSE::JWE] jwe
|
704
|
+
# @return [JOSE::EncryptedMap, [JOSE::EncryptedMap, JOSE::JWK]]
|
250
705
|
def box_encrypt(plain_text, my_private_jwk = nil, jwe = nil)
|
251
706
|
generated_jwk = nil
|
252
707
|
other_public_jwk = self
|
@@ -280,10 +735,32 @@ module JOSE
|
|
280
735
|
end
|
281
736
|
end
|
282
737
|
|
738
|
+
# Derives a key (typically just returns a binary representation of the key).
|
739
|
+
#
|
740
|
+
# @param [*Object] args
|
741
|
+
# @return [String]
|
283
742
|
def derive_key(*args)
|
284
743
|
return kty.derive_key(*args)
|
285
744
|
end
|
286
745
|
|
746
|
+
# Generates a new {JOSE::JWK JOSE::JWK} based on another {JOSE::JWK JOSE::JWK} or from initialization params provided.
|
747
|
+
#
|
748
|
+
# Passing another {JOSE::JWK JOSE::JWK} results in different behavior depending on the `"kty"`:
|
749
|
+
#
|
750
|
+
# * `"EC"` - uses the same named curve to generate a new key
|
751
|
+
# * `"oct"` - uses the byte size to generate a new key
|
752
|
+
# * `"OKP"` - uses the named curve to generate a new key
|
753
|
+
# * `"RSA"` - uses the same modulus and exponent sizes to generate a new key
|
754
|
+
#
|
755
|
+
# The following initialization params may also be used:
|
756
|
+
#
|
757
|
+
# * `[:ec, "P-256" | "P-384" | "P-521"]` - generates an `"EC"` key using the `"P-256"`, `"P-384"`, or `"P-521"` curves
|
758
|
+
# * `[:oct, bytes]` - generates an `"oct"` key made of a random `bytes` number of bytes
|
759
|
+
# * `[:okp, :Ed25519 | :Ed25519ph | :Ed448 | :Ed448ph | :X25519 | :X448]` - generates an `"OKP"` key using the specified curve
|
760
|
+
# * `[:rsa, modulus_size] | [:rsa, modulus_size, exponent_size]` - generates an `"RSA"` key using the `modulus_size` and `exponent_size`
|
761
|
+
#
|
762
|
+
# @param [Array] params
|
763
|
+
# @return [JOSE::JWK]
|
287
764
|
def self.generate_key(params)
|
288
765
|
if params.is_a?(Array) and (params.length == 2 or params.length == 3)
|
289
766
|
case params[0]
|
@@ -322,14 +799,24 @@ module JOSE
|
|
322
799
|
end
|
323
800
|
end
|
324
801
|
|
802
|
+
# Generates a new key based on the current one.
|
803
|
+
#
|
804
|
+
# @return [JOSE::JWK]
|
325
805
|
def generate_key
|
326
806
|
return JOSE::JWK.new(nil, *kty.generate_key(fields))
|
327
807
|
end
|
328
808
|
|
809
|
+
# Merges map on right into map on left.
|
810
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] left
|
811
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] right
|
812
|
+
# @return [JOSE::JWK]
|
329
813
|
def self.merge(left, right)
|
330
814
|
return from(left).merge(right)
|
331
815
|
end
|
332
816
|
|
817
|
+
# Merges object into current map.
|
818
|
+
# @param [JOSE::Map, Hash, String, JOSE::JWK] object
|
819
|
+
# @return [JOSE::JWK]
|
333
820
|
def merge(object)
|
334
821
|
object = case object
|
335
822
|
when JOSE::Map, Hash
|
@@ -344,10 +831,23 @@ module JOSE
|
|
344
831
|
return JOSE::JWK.from_map(self.to_map.merge(object))
|
345
832
|
end
|
346
833
|
|
834
|
+
# Computes the shared secret between two keys.
|
835
|
+
#
|
836
|
+
# Currently only works for `"EC"` keys and `"OKP"` keys with `"crv"` set to `"X25519"` or `"X448"`.
|
837
|
+
#
|
838
|
+
# @param [JOSE::JWK] your_jwk
|
839
|
+
# @param [JOSE::JWK] my_jwk
|
840
|
+
# @return [String]
|
347
841
|
def self.shared_secret(your_jwk, my_jwk)
|
348
842
|
return from(your_jwk).shared_secret(from(my_jwk))
|
349
843
|
end
|
350
844
|
|
845
|
+
# Computes the shared secret between two keys.
|
846
|
+
#
|
847
|
+
# Currently only works for `"EC"` keys and `"OKP"` keys with `"crv"` set to `"X25519"` or `"X448"`.
|
848
|
+
#
|
849
|
+
# @param [JOSE::JWK] other_jwk
|
850
|
+
# @return [String]
|
351
851
|
def shared_secret(other_jwk)
|
352
852
|
other_jwk = from(other_jwk) if not other_jwk.is_a?(JOSE::JWK)
|
353
853
|
raise ArgumentError, "key types must match" if other_jwk.kty.class != kty.class
|
@@ -355,54 +855,144 @@ module JOSE
|
|
355
855
|
return kty.derive_key(other_jwk)
|
356
856
|
end
|
357
857
|
|
858
|
+
# Signs the `plain_text` using the `jwk` and the default signer algorithm `jws` for the key type.
|
859
|
+
#
|
860
|
+
# @see JOSE::JWS.sign
|
861
|
+
# @param [JOSE::JWK] jwk
|
862
|
+
# @param [String] plain_text
|
863
|
+
# @param [JOSE::JWS] jws
|
864
|
+
# @param [JOSE::Map] header
|
865
|
+
# @return [JOSE::SignedMap]
|
358
866
|
def self.sign(jwk, plain_text, jws = nil, header = nil)
|
359
867
|
return from(jwk).sign(plain_text, jws, header)
|
360
868
|
end
|
361
869
|
|
870
|
+
# Signs the `plain_text` using the `jwk` and the default signer algorithm `jws` for the key type.
|
871
|
+
#
|
872
|
+
# @see JOSE::JWS.sign
|
873
|
+
# @param [String] plain_text
|
874
|
+
# @param [JOSE::JWS] jws
|
875
|
+
# @param [JOSE::Map] header
|
876
|
+
# @return [JOSE::SignedMap]
|
362
877
|
def sign(plain_text, jws = nil, header = nil)
|
363
878
|
jws ||= signer
|
364
879
|
return JOSE::JWS.sign(self, plain_text, jws, header)
|
365
880
|
end
|
366
881
|
|
882
|
+
# Returns a signer map for the key type.
|
883
|
+
#
|
884
|
+
# @param [JOSE::JWK] jwk
|
885
|
+
# @return [JOSE::Map]
|
367
886
|
def self.signer(jwk)
|
368
887
|
return from(jwk).signer
|
369
888
|
end
|
370
889
|
|
890
|
+
# Returns a signer map for the key type.
|
891
|
+
#
|
892
|
+
# @return [JOSE::Map]
|
371
893
|
def signer
|
372
894
|
return kty.signer(fields)
|
373
895
|
end
|
374
896
|
|
897
|
+
# Returns the unique thumbprint for a {JOSE::JWK JOSE::JWK} using the `digest_type`.
|
898
|
+
#
|
899
|
+
# !!!ruby
|
900
|
+
# # let's define two different keys that will have the same thumbprint
|
901
|
+
# jwk1 = JOSE::JWK.from_oct("secret")
|
902
|
+
# jwk2 = JOSE::JWK.from({ "use" => "sig", "k" => "c2VjcmV0", "kty" => "oct" })
|
903
|
+
#
|
904
|
+
# JOSE::JWK.thumbprint(jwk1)
|
905
|
+
# # => "DWBh0SEIAPYh1x5uvot4z3AhaikHkxNJa3Ada2fT-Cg"
|
906
|
+
# JOSE::JWK.thumbprint(jwk2)
|
907
|
+
# # => "DWBh0SEIAPYh1x5uvot4z3AhaikHkxNJa3Ada2fT-Cg"
|
908
|
+
# JOSE::JWK.thumbprint('MD5', jwk1)
|
909
|
+
# # => "Kldz8k5PQm7y1E3aNBlMiA"
|
910
|
+
# JOSE::JWK.thumbprint('MD5', jwk2)
|
911
|
+
# # => "Kldz8k5PQm7y1E3aNBlMiA"
|
912
|
+
#
|
913
|
+
# @see https://tools.ietf.org/html/rfc7638 RFC 7638 - JSON Web Key (JWK) Thumbprint
|
914
|
+
# @param [String] digest_type
|
915
|
+
# @param [JOSE::JWK] jwk
|
916
|
+
# @return [String]
|
917
|
+
def self.thumbprint(digest_type, jwk = nil)
|
918
|
+
if jwk.nil?
|
919
|
+
jwk = digest_type
|
920
|
+
digest_type = nil
|
921
|
+
end
|
922
|
+
return from(jwk).thumbprint(digest_type)
|
923
|
+
end
|
924
|
+
|
925
|
+
# Returns the unique thumbprint for a {JOSE::JWK JOSE::JWK} using the `digest_type`.
|
926
|
+
#
|
927
|
+
# @see JOSE::JWK.thumbprint
|
928
|
+
# @see https://tools.ietf.org/html/rfc7638 RFC 7638 - JSON Web Key (JWK) Thumbprint
|
929
|
+
# @param [String] digest_type
|
930
|
+
# @return [String]
|
931
|
+
def thumbprint(digest_type = nil)
|
932
|
+
digest_type ||= 'SHA256'
|
933
|
+
thumbprint_binary = JOSE.encode(to_thumbprint_map)
|
934
|
+
return JOSE.urlsafe_encode64(OpenSSL::Digest.new(digest_type).digest(thumbprint_binary))
|
935
|
+
end
|
936
|
+
|
937
|
+
# Returns a verifier algorithm list for the key type.
|
938
|
+
#
|
939
|
+
# @param [JOSE::JWK] jwk
|
940
|
+
# @return [Array<String>]
|
941
|
+
def self.verifier(jwk)
|
942
|
+
if jwk.is_a?(Array)
|
943
|
+
return from(jwk).map { |obj| obj.verifier }
|
944
|
+
else
|
945
|
+
return from(jwk).verifier
|
946
|
+
end
|
947
|
+
end
|
948
|
+
|
949
|
+
# Returns a verifier algorithm list for the key type.
|
950
|
+
#
|
951
|
+
# @return [Array<String>]
|
952
|
+
def verifier
|
953
|
+
return kty.verifier(fields)
|
954
|
+
end
|
955
|
+
|
956
|
+
# Verifies the `signed` using the `jwk`.
|
957
|
+
#
|
958
|
+
# @see JOSE::JWS.verify
|
959
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap] signed
|
960
|
+
# @param [JOSE::JWK] jwk
|
961
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
375
962
|
def self.verify(signed, jwk)
|
376
963
|
return from(jwk).verify(signed)
|
377
964
|
end
|
378
965
|
|
966
|
+
# Verifies the `signed` using the `jwk`.
|
967
|
+
#
|
968
|
+
# @see JOSE::JWS.verify
|
969
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap] signed
|
970
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
379
971
|
def verify(signed)
|
380
972
|
return JOSE::JWS.verify(self, signed)
|
381
973
|
end
|
382
974
|
|
975
|
+
# Verifies the `signed` using the `jwk` and whitelists the `"alg"` using `allow`.
|
976
|
+
#
|
977
|
+
# @see JOSE::JWS.verify_strict
|
978
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap] signed
|
979
|
+
# @param [Array<String>] allow
|
980
|
+
# @param [JOSE::JWK] jwk
|
981
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
383
982
|
def self.verify_strict(signed, allow, jwk)
|
384
983
|
return from(jwk).verify_strict(signed, allow)
|
385
984
|
end
|
386
985
|
|
986
|
+
# Verifies the `signed` using the `jwk` and whitelists the `"alg"` using `allow`.
|
987
|
+
#
|
988
|
+
# @see JOSE::JWS.verify_strict
|
989
|
+
# @param [JOSE::SignedBinary, JOSE::SignedMap] signed
|
990
|
+
# @param [Array<String>] allow
|
991
|
+
# @return [[Boolean, String, JOSE::JWS]]
|
387
992
|
def verify_strict(signed, allow)
|
388
993
|
return JOSE::JWS.verify_strict(self, allow, signed)
|
389
994
|
end
|
390
995
|
|
391
|
-
# See https://tools.ietf.org/html/rfc7638
|
392
|
-
def self.thumbprint(digest_type, jwk = nil)
|
393
|
-
if jwk.nil?
|
394
|
-
jwk = digest_type
|
395
|
-
digest_type = nil
|
396
|
-
end
|
397
|
-
return from(jwk).thumbprint(digest_type)
|
398
|
-
end
|
399
|
-
|
400
|
-
def thumbprint(digest_type = nil)
|
401
|
-
digest_type ||= 'SHA256'
|
402
|
-
thumbprint_binary = JOSE.encode(to_thumbprint_map)
|
403
|
-
return JOSE.urlsafe_encode64(OpenSSL::Digest.new(digest_type).digest(thumbprint_binary))
|
404
|
-
end
|
405
|
-
|
406
996
|
private
|
407
997
|
|
408
998
|
def self.from_fields(jwk, modules)
|
@@ -451,5 +1041,6 @@ module JOSE
|
|
451
1041
|
end
|
452
1042
|
|
453
1043
|
require 'jose/jwk/pem'
|
1044
|
+
require 'jose/jwk/openssh_key'
|
454
1045
|
require 'jose/jwk/set'
|
455
1046
|
require 'jose/jwk/kty'
|