jsign 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|