jsign 0.0.1
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.
- data/lib/jsign.rb +79 -0
- data/lib/jsign.rb~ +96 -0
- data/lib/jsign/version.rb +5 -0
- metadata +62 -0
data/lib/jsign.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
require 'base64'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
|
8
|
+
module Jsign
|
9
|
+
|
10
|
+
class << self
|
11
|
+
|
12
|
+
def check_for_keyfile!(key_filename)
|
13
|
+
raise "Key #{key_filename.inspect} doesn't exist!" unless
|
14
|
+
File.file?(key_filename)
|
15
|
+
end
|
16
|
+
private :check_for_keyfile!
|
17
|
+
|
18
|
+
|
19
|
+
# Given a json serialisation of a hash, return another json
|
20
|
+
# serialisation which includes a key "_jsign" and a value which is
|
21
|
+
# the Base64-encoded signature of the *original* json. Well,
|
22
|
+
# nearly - any internal whitespace after the last value in the
|
23
|
+
# original json is stripped.
|
24
|
+
#
|
25
|
+
# @param [String] json The json serialisation to sign.
|
26
|
+
# @param [String] key_filename The filename of the private key to
|
27
|
+
# sign with.
|
28
|
+
# @return [String] another json string containing the signature,
|
29
|
+
# which can be passed to Jsign.verify.
|
30
|
+
def sign(json, key_filename)
|
31
|
+
check_for_keyfile!(key_filename)
|
32
|
+
|
33
|
+
# Remove any whitespace inside the last bracket.
|
34
|
+
stripped_json = json.sub(/\s+}\Z/, "}")
|
35
|
+
sig = ""
|
36
|
+
# Use openssl -sha256 to do the signing. This will probably
|
37
|
+
# be configurable in a future version, but it'll do for now
|
38
|
+
digest = OpenSSL::Digest::SHA256.new
|
39
|
+
key = OpenSSL::PKey::RSA.new(File.read(key_filename))
|
40
|
+
sig = key.sign(digest, stripped_json)
|
41
|
+
|
42
|
+
# Use the urlsafe version so we don't get newline literals
|
43
|
+
enc_sig = Base64.urlsafe_encode64(sig).strip
|
44
|
+
return stripped_json.sub(/}\Z/, %Q[,"_jsign":"#{enc_sig}"}])
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
# Given a string of json and the filename of a public key,
|
49
|
+
# see if the json string contains a signature which the public
|
50
|
+
# key validates. This doesn't actually do any JSON decoding.
|
51
|
+
#
|
52
|
+
# @param [String] json The putative JSON string
|
53
|
+
# @param [String] pubkey_filename The filename of the public key
|
54
|
+
# @return [Boolean] true if the public key validates the
|
55
|
+
# signature, false otherwise.
|
56
|
+
def verify(json, pubkey_filename)
|
57
|
+
check_for_keyfile!(pubkey_filename)
|
58
|
+
|
59
|
+
# Note that we mirror the whitespace stripping here:
|
60
|
+
# this is so that we have a chance to survive being
|
61
|
+
# deserialised and reserialised.
|
62
|
+
if m = json.match(/\A(.+?)\s*,"_jsign":"([^"]+)"}/)
|
63
|
+
enc_sig = m[2]
|
64
|
+
|
65
|
+
sig = Base64.urlsafe_decode64(enc_sig)
|
66
|
+
reconstructed_json = m[1]+"}"
|
67
|
+
|
68
|
+
digest = OpenSSL::Digest::SHA256.new
|
69
|
+
key = OpenSSL::PKey::RSA.new(File.read(pubkey_filename))
|
70
|
+
return key.verify(digest, sig, reconstructed_json)
|
71
|
+
|
72
|
+
else
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end # class << self
|
79
|
+
end # module Jsign
|
data/lib/jsign.rb~
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
require 'base64'
|
5
|
+
|
6
|
+
|
7
|
+
module Jsign
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def check_for_openssl!
|
12
|
+
raise "Can't find the openssl command!" unless
|
13
|
+
system "which openssl > /dev/null"
|
14
|
+
end
|
15
|
+
private :check_for_openssl!
|
16
|
+
|
17
|
+
def check_for_keyfile!(key_filename)
|
18
|
+
raise "Key #{key_filename.inspect} doesn't exist!" unless
|
19
|
+
File.file?(key_filename)
|
20
|
+
end
|
21
|
+
private :check_for_keyfile!
|
22
|
+
|
23
|
+
|
24
|
+
# Given a json serialisation of a hash, return another json
|
25
|
+
# serialisation which includes a key "_jsign" and a value which is
|
26
|
+
# the Base64-encoded signature of the *original* json. Well,
|
27
|
+
# nearly - any internal whitespace after the last value in the
|
28
|
+
# original json is stripped.
|
29
|
+
#
|
30
|
+
# @param [String] json The json serialisation to sign.
|
31
|
+
# @param [String] key_filename The filename of the private key to
|
32
|
+
# sign with.
|
33
|
+
# @return [String] another json string containing the signature,
|
34
|
+
# which can be passed to Jsign.verify.
|
35
|
+
def sign(json, key_filename)
|
36
|
+
check_for_openssl!
|
37
|
+
check_for_keyfile!(key_filename)
|
38
|
+
|
39
|
+
# Remove any whitespace inside the last bracket.
|
40
|
+
stripped_json = json.sub(/\s+}\Z/, "}")
|
41
|
+
sig = ""
|
42
|
+
# Use openssl -sha256 to do the signing. This will probably
|
43
|
+
# be configurable in a future version, but it'll do for now
|
44
|
+
IO.popen("openssl dgst -sha256 -sign #{key_filename}","r+"){|f|
|
45
|
+
f.write stripped_json
|
46
|
+
f.close_write
|
47
|
+
sig = f.read.strip
|
48
|
+
}
|
49
|
+
# Use the urlsafe version so we don't get newline literals
|
50
|
+
enc_sig = Base64.urlsafe_encode64(sig).strip
|
51
|
+
return stripped_json.sub(/}\Z/, %Q[,"_jsign":"#{enc_sig}"}])
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Given a string of json and the filename of a public key,
|
56
|
+
# see if the json string contains a signature which the public
|
57
|
+
# key validates. This doesn't actually do any JSON decoding.
|
58
|
+
#
|
59
|
+
# @param [String] json The putative JSON string
|
60
|
+
# @param [String] pubkey_filename The filename of the public key
|
61
|
+
# @return [Boolean] true if the public key validates the
|
62
|
+
# signature, false otherwise.
|
63
|
+
def verify(json, pubkey_filename)
|
64
|
+
check_for_openssl!
|
65
|
+
check_for_keyfile!(pubkey_filename)
|
66
|
+
|
67
|
+
# Note that we mirror the whitespace stripping here:
|
68
|
+
# this is so that we have a chance to survive being
|
69
|
+
# deserialised and reserialised.
|
70
|
+
if m = json.match(/\A(.+?)\s*,"_jsign":"([^"]+)"}/)
|
71
|
+
# Write the signature out to a tempfile. We can't
|
72
|
+
# use -hex with openssl because it doesn't seem to
|
73
|
+
# support verifying with it.
|
74
|
+
Tempfile.open("jsign") do |tempfile|
|
75
|
+
enc_sig = m[2]
|
76
|
+
tempfile.write(Base64.urlsafe_decode64(enc_sig))
|
77
|
+
tempfile.close
|
78
|
+
reconstructed_json = m[1]+"}"
|
79
|
+
IO.popen(%W{openssl dgst -sha256
|
80
|
+
-verify #{pubkey_filename}
|
81
|
+
-signature #{tempfile.path}}, "r+"){|f|
|
82
|
+
f.write( reconstructed_json )
|
83
|
+
f.close_write
|
84
|
+
msg = f.read().strip
|
85
|
+
return msg == "Verified OK"
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
else
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
end # class << self
|
96
|
+
end # module Jsign
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jsign
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Alex Young
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-06-05 00:00:00.000000000 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &21390960 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *21390960
|
26
|
+
description: Jsign uses OpenSSL to sign JSON serialisations. The signed objects are
|
27
|
+
valid JSON objects themselves.
|
28
|
+
email:
|
29
|
+
- alex@blackkettle.org
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/jsign/version.rb
|
35
|
+
- lib/jsign.rb
|
36
|
+
- lib/jsign.rb~
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/regularfry/jsign
|
39
|
+
licenses: []
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
require_paths:
|
43
|
+
- lib
|
44
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
45
|
+
none: false
|
46
|
+
requirements:
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0'
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project: jsign
|
58
|
+
rubygems_version: 1.6.2
|
59
|
+
signing_key:
|
60
|
+
specification_version: 3
|
61
|
+
summary: Make and verify signed JSON objects.
|
62
|
+
test_files: []
|