jose 0.1.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 +7 -0
- data/.editorconfig +20 -0
- data/.gitignore +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +20 -0
- data/LICENSE.txt +373 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/jose.gemspec +36 -0
- data/lib/jose.rb +61 -0
- data/lib/jose/jwa.rb +21 -0
- data/lib/jose/jwa/aes_kw.rb +105 -0
- data/lib/jose/jwa/concat_kdf.rb +50 -0
- data/lib/jose/jwa/pkcs1.rb +269 -0
- data/lib/jose/jwa/pkcs7.rb +23 -0
- data/lib/jose/jwe.rb +290 -0
- data/lib/jose/jwe/alg.rb +12 -0
- data/lib/jose/jwe/alg_aes_gcm_kw.rb +98 -0
- data/lib/jose/jwe/alg_aes_kw.rb +57 -0
- data/lib/jose/jwe/alg_dir.rb +40 -0
- data/lib/jose/jwe/alg_ecdh_es.rb +123 -0
- data/lib/jose/jwe/alg_pbes2.rb +90 -0
- data/lib/jose/jwe/alg_rsa.rb +63 -0
- data/lib/jose/jwe/enc.rb +8 -0
- data/lib/jose/jwe/enc_aes_cbc_hmac.rb +80 -0
- data/lib/jose/jwe/enc_aes_gcm.rb +68 -0
- data/lib/jose/jwe/zip.rb +7 -0
- data/lib/jose/jwe/zip_def.rb +28 -0
- data/lib/jose/jwk.rb +347 -0
- data/lib/jose/jwk/kty.rb +34 -0
- data/lib/jose/jwk/kty_ec.rb +179 -0
- data/lib/jose/jwk/kty_oct.rb +104 -0
- data/lib/jose/jwk/kty_rsa.rb +185 -0
- data/lib/jose/jwk/pem.rb +19 -0
- data/lib/jose/jwk/set.rb +2 -0
- data/lib/jose/jws.rb +242 -0
- data/lib/jose/jws/alg.rb +10 -0
- data/lib/jose/jws/alg_ecdsa.rb +41 -0
- data/lib/jose/jws/alg_hmac.rb +41 -0
- data/lib/jose/jws/alg_rsa_pkcs1_v1_5.rb +41 -0
- data/lib/jose/jws/alg_rsa_pss.rb +41 -0
- data/lib/jose/jwt.rb +145 -0
- data/lib/jose/version.rb +3 -0
- metadata +162 -0
data/lib/jose/jws/alg.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class JOSE::JWS::ALG_ECDSA < Struct.new(:digest)
|
2
|
+
|
3
|
+
# JOSE::JWS callbacks
|
4
|
+
|
5
|
+
def self.from_map(fields)
|
6
|
+
case fields['alg']
|
7
|
+
when 'ES256'
|
8
|
+
return new(OpenSSL::Digest::SHA256), fields.delete('alg')
|
9
|
+
when 'ES384'
|
10
|
+
return new(OpenSSL::Digest::SHA384), fields.delete('alg')
|
11
|
+
when 'ES512'
|
12
|
+
return new(OpenSSL::Digest::SHA512), fields.delete('alg')
|
13
|
+
else
|
14
|
+
raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_map(fields)
|
19
|
+
alg = if digest == OpenSSL::Digest::SHA256
|
20
|
+
'ES256'
|
21
|
+
elsif digest == OpenSSL::Digest::SHA384
|
22
|
+
'ES384'
|
23
|
+
elsif digest == OpenSSL::Digest::SHA512
|
24
|
+
'ES512'
|
25
|
+
else
|
26
|
+
raise ArgumentError, "unhandled ECDSA digest type: #{digest.inspect}"
|
27
|
+
end
|
28
|
+
return fields.put('alg', alg)
|
29
|
+
end
|
30
|
+
|
31
|
+
# JOSE::JWS::ALG callbacks
|
32
|
+
|
33
|
+
def sign(jwk, message)
|
34
|
+
return jwk.kty.sign(message, digest)
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify(jwk, message, signature)
|
38
|
+
return jwk.kty.verify(message, digest, signature)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class JOSE::JWS::ALG_HMAC < Struct.new(:hmac)
|
2
|
+
|
3
|
+
# JOSE::JWS callbacks
|
4
|
+
|
5
|
+
def self.from_map(fields)
|
6
|
+
case fields['alg']
|
7
|
+
when 'HS256'
|
8
|
+
return new(OpenSSL::Digest::SHA256), fields.delete('alg')
|
9
|
+
when 'HS384'
|
10
|
+
return new(OpenSSL::Digest::SHA384), fields.delete('alg')
|
11
|
+
when 'HS512'
|
12
|
+
return new(OpenSSL::Digest::SHA512), fields.delete('alg')
|
13
|
+
else
|
14
|
+
raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_map(fields)
|
19
|
+
alg = if hmac == OpenSSL::Digest::SHA256
|
20
|
+
'HS256'
|
21
|
+
elsif hmac == OpenSSL::Digest::SHA384
|
22
|
+
'HS384'
|
23
|
+
elsif hmac == OpenSSL::Digest::SHA512
|
24
|
+
'HS512'
|
25
|
+
else
|
26
|
+
raise ArgumentError, "unhandled HMAC digest type: #{hmac.inspect}"
|
27
|
+
end
|
28
|
+
return fields.put('alg', alg)
|
29
|
+
end
|
30
|
+
|
31
|
+
# JOSE::JWS::ALG callbacks
|
32
|
+
|
33
|
+
def sign(jwk, message)
|
34
|
+
return jwk.kty.sign(message, hmac)
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify(jwk, message, signature)
|
38
|
+
return jwk.kty.verify(message, hmac, signature)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class JOSE::JWS::ALG_RSA_PKCS1_V1_5 < Struct.new(:digest)
|
2
|
+
|
3
|
+
# JOSE::JWS callbacks
|
4
|
+
|
5
|
+
def self.from_map(fields)
|
6
|
+
case fields['alg']
|
7
|
+
when 'RS256'
|
8
|
+
return new(OpenSSL::Digest::SHA256), fields.delete('alg')
|
9
|
+
when 'RS384'
|
10
|
+
return new(OpenSSL::Digest::SHA384), fields.delete('alg')
|
11
|
+
when 'RS512'
|
12
|
+
return new(OpenSSL::Digest::SHA512), fields.delete('alg')
|
13
|
+
else
|
14
|
+
raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_map(fields)
|
19
|
+
alg = if digest == OpenSSL::Digest::SHA256
|
20
|
+
'RS256'
|
21
|
+
elsif digest == OpenSSL::Digest::SHA384
|
22
|
+
'RS384'
|
23
|
+
elsif digest == OpenSSL::Digest::SHA512
|
24
|
+
'RS512'
|
25
|
+
else
|
26
|
+
raise ArgumentError, "unhandled RSA_PKCS1_v1_5 digest type: #{digest.inspect}"
|
27
|
+
end
|
28
|
+
return fields.put('alg', alg)
|
29
|
+
end
|
30
|
+
|
31
|
+
# JOSE::JWS::ALG callbacks
|
32
|
+
|
33
|
+
def sign(jwk, message)
|
34
|
+
return jwk.kty.sign(message, digest, padding: :rsa_pkcs1_padding)
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify(jwk, message, signature)
|
38
|
+
return jwk.kty.verify(message, digest, signature, padding: :rsa_pkcs1_padding)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class JOSE::JWS::ALG_RSA_PSS < Struct.new(:digest)
|
2
|
+
|
3
|
+
# JOSE::JWS callbacks
|
4
|
+
|
5
|
+
def self.from_map(fields)
|
6
|
+
case fields['alg']
|
7
|
+
when 'PS256'
|
8
|
+
return new(OpenSSL::Digest::SHA256), fields.delete('alg')
|
9
|
+
when 'PS384'
|
10
|
+
return new(OpenSSL::Digest::SHA384), fields.delete('alg')
|
11
|
+
when 'PS512'
|
12
|
+
return new(OpenSSL::Digest::SHA512), fields.delete('alg')
|
13
|
+
else
|
14
|
+
raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_map(fields)
|
19
|
+
alg = if digest == OpenSSL::Digest::SHA256
|
20
|
+
'PS256'
|
21
|
+
elsif digest == OpenSSL::Digest::SHA384
|
22
|
+
'PS384'
|
23
|
+
elsif digest == OpenSSL::Digest::SHA512
|
24
|
+
'PS512'
|
25
|
+
else
|
26
|
+
raise ArgumentError, "unhandled RSA_PSS digest type: #{digest.inspect}"
|
27
|
+
end
|
28
|
+
return fields.put('alg', alg)
|
29
|
+
end
|
30
|
+
|
31
|
+
# JOSE::JWS::ALG callbacks
|
32
|
+
|
33
|
+
def sign(jwk, message)
|
34
|
+
return jwk.kty.sign(message, digest, padding: :rsa_pkcs1_pss_padding)
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify(jwk, message, signature)
|
38
|
+
return jwk.kty.verify(message, digest, signature, padding: :rsa_pkcs1_pss_padding)
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/lib/jose/jwt.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
module JOSE
|
2
|
+
|
3
|
+
class JWT < Struct.new(:fields)
|
4
|
+
|
5
|
+
# Decode API
|
6
|
+
|
7
|
+
def self.from(object, modules = {})
|
8
|
+
case object
|
9
|
+
when JOSE::Map, Hash
|
10
|
+
return from_map(object, modules)
|
11
|
+
when String
|
12
|
+
return from_binary(object, modules)
|
13
|
+
when JOSE::JWT
|
14
|
+
return object
|
15
|
+
else
|
16
|
+
raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWT"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_binary(object, modules = {})
|
21
|
+
case object
|
22
|
+
when String
|
23
|
+
return from_map(JOSE.decode(object), modules)
|
24
|
+
else
|
25
|
+
raise ArgumentError, "'object' must be a String"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.from_file(file, modules = {})
|
30
|
+
return from_binary(File.binread(file), modules)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.from_map(object, modules = {})
|
34
|
+
case object
|
35
|
+
when JOSE::Map, Hash
|
36
|
+
return from_fields(JOSE::JWT.new(JOSE::Map.new(object)), modules)
|
37
|
+
else
|
38
|
+
raise ArgumentError, "'object' must be a Hash"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Encode API
|
43
|
+
|
44
|
+
def self.to_binary(jwt)
|
45
|
+
return from(jwt).to_binary
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_binary
|
49
|
+
return JOSE.encode(to_map)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.to_file(jwt, file)
|
53
|
+
return from(jwt).to_file(file)
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_file(file)
|
57
|
+
return File.binwrite(file, to_binary)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.to_map(jwt)
|
61
|
+
return from(jwt).to_map
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_map
|
65
|
+
return fields
|
66
|
+
end
|
67
|
+
|
68
|
+
# API
|
69
|
+
|
70
|
+
def self.decrypt(jwk, encrypted)
|
71
|
+
decrypted, jwe = JOSE::JWK.block_decrypt(jwk, encrypted)
|
72
|
+
return from_binary(decrypted), jwe
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.encrypt(jwt, jwk, jwe = nil)
|
76
|
+
return from(jwt).encrypt(jwk, jwe)
|
77
|
+
end
|
78
|
+
|
79
|
+
def encrypt(jwk, jwe = nil)
|
80
|
+
plain_text = to_binary
|
81
|
+
if jwe.nil?
|
82
|
+
jwk = JOSE::JWK.from(jwk)
|
83
|
+
jwe = jwk.kty.block_encryptor(jwk.fields, plain_text)
|
84
|
+
end
|
85
|
+
if jwe.is_a?(Hash)
|
86
|
+
jwe = JOSE::Map.new(jwe)
|
87
|
+
end
|
88
|
+
if jwe.is_a?(JOSE::Map) and not jwe.has_key?('typ')
|
89
|
+
jwe = jwe.put('typ', 'JWT')
|
90
|
+
end
|
91
|
+
return JOSE::JWK.block_encrypt(jwk, plain_text, jwe)
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.peek_payload(signed)
|
95
|
+
return JOSE::Map.new(JOSE.decode(JOSE::JWS.peek_payload(signed)))
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.peek_protected(signed)
|
99
|
+
return JOSE::JWS.peek_protected(signed)
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.sign(jwt, jwk, jws = nil, header = nil)
|
103
|
+
return from(jwt).sign(jwk, jws)
|
104
|
+
end
|
105
|
+
|
106
|
+
def sign(jwk, jws = nil, header = nil)
|
107
|
+
plain_text = to_binary
|
108
|
+
if jws.nil?
|
109
|
+
jwk = JOSE::JWK.from(jwk)
|
110
|
+
jws = jwk.kty.signer(jwk.fields, plain_text)
|
111
|
+
end
|
112
|
+
if jws.is_a?(Hash)
|
113
|
+
jws = JOSE::Map.new(jws)
|
114
|
+
end
|
115
|
+
if jws.is_a?(JOSE::Map) and not jws.has_key?('typ')
|
116
|
+
jws = jws.put('typ', 'JWT')
|
117
|
+
end
|
118
|
+
return JOSE::JWK.sign(jwk, plain_text, jws, header)
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.verify(jwk, signed)
|
122
|
+
verified, payload, jws = JOSE::JWK.verify(signed, jwk)
|
123
|
+
jwt = from_binary(payload)
|
124
|
+
return verified, jwt, jws
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.verify_strict(jwk, allow, signed)
|
128
|
+
verified, payload, jws = JOSE::JWK.verify(signed, allow, jwk)
|
129
|
+
jwt = payload
|
130
|
+
if verified
|
131
|
+
jwt = from_binary(payload)
|
132
|
+
end
|
133
|
+
return verified, jwt, jws
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def self.from_fields(jwt, modules)
|
139
|
+
return jwt
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
require 'jose/jws/alg'
|
data/lib/jose/version.rb
ADDED
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Bennett
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hamster
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.10'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.10'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: json
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: JSON Object Signing and Encryption
|
84
|
+
email:
|
85
|
+
- andrew@pixid.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".editorconfig"
|
91
|
+
- ".gitignore"
|
92
|
+
- ".ruby-gemset"
|
93
|
+
- ".ruby-version"
|
94
|
+
- ".travis.yml"
|
95
|
+
- CODE_OF_CONDUCT.md
|
96
|
+
- Gemfile
|
97
|
+
- LICENSE.txt
|
98
|
+
- README.md
|
99
|
+
- Rakefile
|
100
|
+
- bin/console
|
101
|
+
- bin/setup
|
102
|
+
- jose.gemspec
|
103
|
+
- lib/jose.rb
|
104
|
+
- lib/jose/jwa.rb
|
105
|
+
- lib/jose/jwa/aes_kw.rb
|
106
|
+
- lib/jose/jwa/concat_kdf.rb
|
107
|
+
- lib/jose/jwa/pkcs1.rb
|
108
|
+
- lib/jose/jwa/pkcs7.rb
|
109
|
+
- lib/jose/jwe.rb
|
110
|
+
- lib/jose/jwe/alg.rb
|
111
|
+
- lib/jose/jwe/alg_aes_gcm_kw.rb
|
112
|
+
- lib/jose/jwe/alg_aes_kw.rb
|
113
|
+
- lib/jose/jwe/alg_dir.rb
|
114
|
+
- lib/jose/jwe/alg_ecdh_es.rb
|
115
|
+
- lib/jose/jwe/alg_pbes2.rb
|
116
|
+
- lib/jose/jwe/alg_rsa.rb
|
117
|
+
- lib/jose/jwe/enc.rb
|
118
|
+
- lib/jose/jwe/enc_aes_cbc_hmac.rb
|
119
|
+
- lib/jose/jwe/enc_aes_gcm.rb
|
120
|
+
- lib/jose/jwe/zip.rb
|
121
|
+
- lib/jose/jwe/zip_def.rb
|
122
|
+
- lib/jose/jwk.rb
|
123
|
+
- lib/jose/jwk/kty.rb
|
124
|
+
- lib/jose/jwk/kty_ec.rb
|
125
|
+
- lib/jose/jwk/kty_oct.rb
|
126
|
+
- lib/jose/jwk/kty_rsa.rb
|
127
|
+
- lib/jose/jwk/pem.rb
|
128
|
+
- lib/jose/jwk/set.rb
|
129
|
+
- lib/jose/jws.rb
|
130
|
+
- lib/jose/jws/alg.rb
|
131
|
+
- lib/jose/jws/alg_ecdsa.rb
|
132
|
+
- lib/jose/jws/alg_hmac.rb
|
133
|
+
- lib/jose/jws/alg_rsa_pkcs1_v1_5.rb
|
134
|
+
- lib/jose/jws/alg_rsa_pss.rb
|
135
|
+
- lib/jose/jwt.rb
|
136
|
+
- lib/jose/version.rb
|
137
|
+
homepage: https://github.com/potatosalad/ruby-jose
|
138
|
+
licenses:
|
139
|
+
- MPL-2.0
|
140
|
+
metadata: {}
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
requirements:
|
147
|
+
- - ">="
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
|
+
requirements:
|
152
|
+
- - ">="
|
153
|
+
- !ruby/object:Gem::Version
|
154
|
+
version: '0'
|
155
|
+
requirements: []
|
156
|
+
rubyforge_project:
|
157
|
+
rubygems_version: 2.4.5.1
|
158
|
+
signing_key:
|
159
|
+
specification_version: 4
|
160
|
+
summary: JSON Object Signing and Encryption
|
161
|
+
test_files: []
|
162
|
+
has_rdoc:
|