jose 0.3.1 → 1.0.0
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.
- 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'
|