ejson 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 45c8c29f86387553314be3e8738a55173944bdf1
4
+ data.tar.gz: 3353be24350564b401c3cfc631bd09d7ab98d1d1
5
+ SHA512:
6
+ metadata.gz: 52504a192b550a1d4391617b5695a87f14e974299db5760cb845f14612b36faf54e2087b0184ce7cf3556a7c5ec1d2d75ad569cc6ea18cc501c70b8a26d3145c
7
+ data.tar.gz: bb65c77a6500b0d550337e51fe7af6704c9c6c81b022e86dee2e968706100318427aea9f872895d0efc328bc38d4c3364299f1e496a8ddbce597dfbe2cd88904
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ejson.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Burke Libbey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # EJSON
2
+
3
+ EJSON is a small library to manage encrypted secrets using PKCS7 (asymmetric)
4
+ encryption. It provides a simple command interface to manage and update secrets
5
+ in a JSON file where keys are cleartext and values are encrypted.
6
+
7
+ ## Installation
8
+
9
+ It's on rubygems. Just `gem install ejson` or add it to your `Gemfile`.
10
+
11
+ ## Usage
12
+
13
+ #### 1) Create a `secrets.ejson`:
14
+
15
+ echo '{"a": "b"}' > config/secrets.production.ejson
16
+
17
+ Keys in this file will remain in cleartext, while values will all be encrypted.
18
+ It can be arbitrarily nested.
19
+
20
+ #### 2) Encrypt the file:
21
+
22
+ ejson
23
+
24
+ This updates `config/secrets.ejson` in place, encrypting any newly-added or
25
+ modified values that are not yet encrypted. `ejson` is short-hand for `ejson encrypt`.
26
+
27
+ #### 3) Decrypt the file:
28
+
29
+ ejson decrypt -k ~/.keys/ejson.priv.pem -p config/ejson.pub.pem secrets.production.ejson > secrets.production.json
30
+
31
+ Unlike encrypt, decrypt doesn't update the file in-place; it prints the
32
+ decrypted contents to stdout. It also requires access to the private key
33
+ created in step 1.
34
+
35
+ #### See `ejson help` for more information.
36
+
37
+ ## Custom keypair:
38
+
39
+ We use a single keypair internally; the default public key is fetched from S3
40
+ on each run. However, you can generate your own keypair like so:
41
+
42
+ mkdir config && cd config
43
+ openssl req -x509 -nodes -days 100000 -newkey rsa:2048 -keyout privatekey.pem -out publickey.pem -subj '/'
44
+
45
+ `publickey.pem` and `privatekey.pem` are created. Move `privatekey.pem`
46
+ somewhere more secure.
47
+
48
+ mkdir -p ~/.keys
49
+ mv config/privatekey.pem ~/.keys/ejson.pem
50
+
51
+ Then you can encrypt like:
52
+
53
+ ejson encrypt -p config/publickey.pem secrets.ejson
54
+
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ task default: :test
4
+
5
+ task :test do
6
+ exec "ruby -I./lib test/ejson_test.rb"
7
+ end
data/bin/ejson ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ejson/cli'
4
+ EJSON::CLI.start
data/ejson.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ejson/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ejson"
8
+ spec.version = Ejson::VERSION
9
+ spec.authors = ["Burke Libbey"]
10
+ spec.email = ["burke.libbey@shopify.com"]
11
+ spec.summary = %q{Asymmetric keywise encryption for JSON}
12
+ spec.description = %q{Secret management by encrypting values in a JSON hash with a public/private keypair}
13
+ spec.homepage = "https://github.com/Shopify/ejson"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "thor", "~> 0.18"
22
+ end
data/lib/ejson.rb ADDED
@@ -0,0 +1,60 @@
1
+ require 'json'
2
+ require 'forwardable'
3
+ require 'ejson/encryption'
4
+
5
+ class EJSON
6
+ extend Forwardable
7
+ def_delegators :@encryption, :load_string, :dump_string
8
+
9
+ def initialize(public_key_file, private_key_file = nil)
10
+ @encryption = Encryption.new(public_key_file, private_key_file)
11
+ end
12
+
13
+ def load(json_text)
14
+ Data.new(JSON.load(json_text), @encryption)
15
+ end
16
+
17
+ class Data
18
+ extend Forwardable
19
+ def_delegators :@data, :[]=
20
+
21
+ attr_reader :encryption
22
+ def initialize(data, encryption)
23
+ @data, @encryption = data, encryption
24
+ end
25
+
26
+ def dump
27
+ JSON.pretty_generate(encrypt_all(@data))
28
+ end
29
+
30
+ def encrypt_all(data=@data)
31
+ case data
32
+ when Hash
33
+ Hash[ data.map { |k,v| [k, encrypt_all(v)] } ]
34
+ when Array
35
+ data.map { |d| encrypt_all(d) }
36
+ when String
37
+ encryption.dump(data)
38
+ else
39
+ data
40
+ end
41
+ end
42
+
43
+ def decrypt_all(data=@data)
44
+ case data
45
+ when Hash
46
+ Hash[ data.map { |k,v| [k, decrypt_all(v)] } ]
47
+ when Array
48
+ data.map { |d| decrypt_all(d) }
49
+ when String
50
+ encryption.load(data)
51
+ else
52
+ data
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+
data/lib/ejson/cli.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'thor'
2
+ require 'json'
3
+ require 'ejson'
4
+ require 'net/http'
5
+
6
+ class EJSON
7
+
8
+ class CLI < Thor
9
+ class_option "privkey", type: :string, aliases: "-k", desc: "Path to PKCS7 private key in PEM format"
10
+ class_option "pubkey", type: :string, aliases: "-p", desc: "Path or URL to PKCS7 public key in PEM format", default: "https://s3.amazonaws.com/shopify-ops/ejson-publickey.pem"
11
+
12
+ default_task :encrypt
13
+
14
+ desc "decrypt [file]", "decrypt some data from file to stdout"
15
+ def decrypt(file)
16
+ ciphertext = File.read(file)
17
+ ej = EJSON.new(pubkey, options[:privkey])
18
+ puts JSON.pretty_generate(ej.load(ciphertext).decrypt_all)
19
+ rescue EJSON::Encryption::PrivateKeyMissing => e
20
+ fatal("can't decrypt data without private key (specify path with -k)", e)
21
+ rescue EJSON::Encryption::ExpectedEncryptedString => e
22
+ fatal("can't decrypt data with cleartext strings (use ejson recrypt first)", e)
23
+ end
24
+
25
+ desc "encrypt [file=**/*.ejson]", "encrypt an ejson file in place (encrypt any unencrypted values)"
26
+ def encrypt(file="**/*.ejson")
27
+ ej = EJSON.new(pubkey)
28
+ fpaths = Dir.glob(file)
29
+ if fpaths.empty?
30
+ fatal("no ejson files found!", nil)
31
+ end
32
+ fpaths.each do |fpath|
33
+ data = ej.load(File.read(fpath))
34
+ dump = data.dump
35
+ File.open(fpath, "w") { |f| f.puts dump }
36
+ puts "Wrote #{dump.size+1} bytes to #{fpath}"
37
+ end
38
+ rescue OpenSSL::X509::CertificateError => e
39
+ fatal("invalid certificate", e)
40
+ end
41
+
42
+ desc "version", "show version information"
43
+ def version
44
+ puts "ejson version #{EJSON::VERSION}"
45
+ end
46
+
47
+ private
48
+
49
+ def fatal(str, err=str)
50
+ raise err if defined?(Minitest)
51
+ msg = $stderr.tty? ? "\x1b[31m#{str}\x1b[0m" : str
52
+ $stderr.puts msg
53
+ exit 1
54
+ end
55
+
56
+ def get_input(file)
57
+ return File.read(file) if file
58
+ $stdin.read
59
+ end
60
+
61
+ def pubkey
62
+ @pubkey ||= _pubkey
63
+ end
64
+
65
+ def _pubkey
66
+ if options[:pubkey] =~ %r{https://}
67
+ uri = URI.parse(options[:pubkey])
68
+ http = Net::HTTP.new(uri.host, uri.port)
69
+ http.use_ssl = true
70
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
71
+ req = Net::HTTP::Get.new(URI.parse(options[:pubkey]).request_uri)
72
+ resp = http.request(req)
73
+ resp.value # raises on code >399
74
+ f = Tempfile.new("pubkey")
75
+ f.write resp.body
76
+ f.close
77
+ at_exit { f.unlink }
78
+ f.path
79
+ else
80
+ options[:pubkey]
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+
@@ -0,0 +1,61 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ class EJSON
5
+
6
+ class Encryption
7
+ PrivateKeyMissing = Class.new(StandardError)
8
+ ExpectedEncryptedString = Class.new(StandardError)
9
+
10
+ def initialize(public_key_file, private_key_file)
11
+ @public_key_x509 = load_public_key(public_key_file)
12
+ if private_key_file
13
+ @private_key_rsa = load_private_key(private_key_file)
14
+ end
15
+ end
16
+
17
+ ENCRYPTED = /\AENC\[(.*)\]\n*\z/m
18
+
19
+ def load(str)
20
+ if str =~ ENCRYPTED
21
+ decrypt_string($1)
22
+ else
23
+ raise ExpectedEncryptedString
24
+ end
25
+ end
26
+
27
+ def dump(str)
28
+ if str =~ ENCRYPTED
29
+ str
30
+ else
31
+ "ENC[#{encrypt_string(str)}]"
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def encrypt_string(plaintext)
38
+ cipher = OpenSSL::Cipher::AES.new(256, :CBC)
39
+ bin = OpenSSL::PKCS7.encrypt([@public_key_x509], plaintext, cipher, OpenSSL::PKCS7::BINARY).to_der
40
+ Base64.encode64(bin).tr("\n",'')
41
+ end
42
+
43
+ def decrypt_string(ciphertext)
44
+ raise PrivateKeyMissing unless @private_key_rsa
45
+ bin = Base64.decode64(ciphertext)
46
+ pkcs7 = OpenSSL::PKCS7.new(bin)
47
+ pkcs7.decrypt(@private_key_rsa, @public_key_x509)
48
+ end
49
+
50
+ def load_public_key(public_key_file)
51
+ public_key_pem = File.read(public_key_file)
52
+ OpenSSL::X509::Certificate.new(public_key_pem)
53
+ end
54
+
55
+ def load_private_key(private_key_file)
56
+ private_key_pem = File.read(private_key_file)
57
+ OpenSSL::PKey::RSA.new(private_key_pem)
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ class Ejson
2
+ VERSION = "0.2.2"
3
+ end
@@ -0,0 +1,85 @@
1
+ require 'minitest/autorun'
2
+ require 'tempfile'
3
+
4
+ require 'ejson/cli'
5
+
6
+ class CLITest < Minitest::Unit::TestCase
7
+
8
+ def test_ejson
9
+ f = Tempfile.create("encrypt")
10
+
11
+ f.puts JSON.dump({a: "b"})
12
+ f.close
13
+
14
+ encrypt f.path
15
+
16
+ first_run = JSON.load(File.read(f.path))
17
+ assert_match(/\AENC\[MIIB.*\]\z/, first_run["a"])
18
+
19
+ File.open(f.path, "w") { |f2|
20
+ f2.puts JSON.dump(first_run.merge({new_key: "new_value"}))
21
+ }
22
+
23
+ encrypt f.path
24
+
25
+ second_run = JSON.load(File.read(f.path))
26
+
27
+ assert_equal first_run["a"], second_run["a"]
28
+ assert_match(/\AENC\[MIIB.*\]\z/, second_run["new_key"])
29
+
30
+ val = JSON.parse(decrypt(f.path))
31
+ assert_equal({"a" => "b", "new_key" => "new_value"}, val)
32
+ ensure
33
+ File.unlink(f.path)
34
+ end
35
+
36
+ def test_default_key_exists
37
+ f = Tempfile.create("encrypt")
38
+
39
+ f.puts JSON.dump({a: "b"})
40
+ f.close
41
+
42
+ runcli "encrypt", f.path # no pubkey specified
43
+
44
+ first_run = JSON.load(File.read(f.path))
45
+ # We don't have the decryption key to this, and it may change over time,
46
+ # so just make sure it was encrypted.
47
+ assert_match(/\AENC\[MIIB.*\]\z/, first_run["a"])
48
+ ensure
49
+ File.unlink(f.path)
50
+ end
51
+
52
+ def test_library_is_picky
53
+ f = Tempfile.create("decrypt")
54
+ f.puts JSON.dump({a: "b"})
55
+ f.close
56
+ assert_raises(EJSON::Encryption::ExpectedEncryptedString) {
57
+ decrypt(f.path)
58
+ }
59
+ ensure
60
+ File.unlink(f.path)
61
+ end
62
+
63
+ private
64
+
65
+ def encrypt(path)
66
+ runcli "encrypt", "-p", pubkey, path
67
+ end
68
+
69
+ def decrypt(path)
70
+ runcli "decrypt", "-p", pubkey, "-k", privkey, path
71
+ end
72
+
73
+ def runcli(*args)
74
+ sio = StringIO.new
75
+ _stdout, $stdout = $stdout, sio
76
+ EJSON::CLI.start(args)
77
+ sio.string.chomp
78
+ ensure
79
+ $stdout = _stdout
80
+ end
81
+
82
+ def pubkey ; File.expand_path("../publickey.pem", __FILE__); end
83
+ def privkey ; File.expand_path("../privatekey.pem", __FILE__); end
84
+
85
+ end
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEogIBAAKCAQEAunMnKCNM9NRDvaANZ3wSBTM7EA4fl3ix/QjwCp3k118n7+Jf
3
+ 1DpdBAjqbx0jWVUZHjKan4t4ZHkCTKW4BXGzdEWVkFebYtYdxCm9DAiHFV53RscI
4
+ gVmzrxxpC8Lxa7RApam1iGPK7TXXlkMe+2d3Jij1hZdCHCYT48/DDBRtIEcXCZRg
5
+ nFkoSuvGbGwMBCxVhLTnvveIF5IgBV3la3vPJFfY62Hafuinpsy/hL4108pbrQcq
6
+ oiyb3OgflTe6JrBmA29qqWoCLAW+6XRgcHsOUfTYiBx6XFHQ42SfzQhu+FY57a38
7
+ Q3TkZLOb6abE6t5lsBqnJoE+dL/2WjLkhxQ/jQIDAQABAoIBAErqjBg/nuNdCt79
8
+ mYU0QBVg0WGRGzaEo5fVaIYLjXDQZj6oCfM/hDJj1rbQ0WxKmi4dDS4AH17XlInx
9
+ qHBfkEiu0PrPiLr857bzQme8YXK/o1OIE63NujopQzgbm1+4bKVj/HISDu6jTL2u
10
+ uJsxpplpqcWE0mZ3ElTeHTQUXQizdz12DPn5vgXvtVS0yFFBErE7aAKorRRFoLPq
11
+ OcajLIMODMo6huUAJW7uYR6PT6uwbt3Phc+VLQtdXpCCfLtDJK427G/5uJ/2vnY/
12
+ rLVWConeNZcUDWRuc34agiXA4yjErXdKXJ/yiEdz2pKtAerf+N9VCoIBLBala3oL
13
+ 2BeMYY0CgYEA848omruvavfp4dJimdEHj0yTsFFggGHefoJWHG8Cvgyv9avISiAk
14
+ T5gB01pSjpTTGL+Ba2aeSPKgKZcGNjp/F5xzeZzuP2tMxT8bs2FR3y9tGmLnhvF8
15
+ JkKtlHilrGLt4pWh9eF+jiLDDmJE1N3fCU8TtIva+a/M60ZORA5ZSrcCgYEAw/k3
16
+ sJs72xhUhjrNdpzww5dpHYxmoOxhCTyKG1H0r4sQ0Jv9w4K3mrphp/WRygA2LylO
17
+ iCEc+56JJ6l4A6rdSFyV+YRtcdU7TKi9sarMDVBttqWsDkr/bnIIHE+QQhsScEBH
18
+ e7rHE+1NP5rPfDB7ibBJ3QCOCpjHbkhUGAZIU9sCgYBa176RWAepoiY98DaOoIRt
19
+ UmaTkQapW9ec4Ag2OsGPGTRYMWZXH33rogqsRjgcri2+QU+IO5I2KyjJ2maau17D
20
+ 87quVXYXeXH87/jpAxeCYzIScWlhz5g6vQv5ILbKgWuw45axGxYU9apDJyv9KXQT
21
+ CMeUw8U88/E+n855W9C6KQKBgGtfKU7+zl2tR+o/X4FEXXmchIAnA7fZqxTHcZek
22
+ YJ6pX+4b+X5cKUKCKa0/k8AMO6O9SwS0t894vgbYCCRiQlk6OQV7tAcxYAsRTNWC
23
+ Ecidr27p+IngN3EI0z7HrO87K/AKl9/HpvlZBAD8Tf/qBFWdG+sVOb2+lU3sHP8I
24
+ uioPAoGAVWyvmQsABl3ydPrvVl4zk3AHm3Bnouy1vM1RchQzXp6OD/u5mNGJ37Ks
25
+ zIgipj/z9xf4J3ii5w0Qnxusq9Zy/6xKO1pgIEhiJUAYcm5jHGC51n7Y+d1NUPe1
26
+ gnJAzuwmvbefN800tmZJtWv7dWSc7whoFsdR1rNwrlZXUoNJzPk=
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,18 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIC5jCCAc6gAwIBAgIJAKY1nRwN7afeMA0GCSqGSIb3DQEBBQUAMAAwIBcNMTQw
3
+ MzE0MTc1ODQzWhgPMjI4NzEyMjgxNzU4NDNaMAAwggEiMA0GCSqGSIb3DQEBAQUA
4
+ A4IBDwAwggEKAoIBAQC6cycoI0z01EO9oA1nfBIFMzsQDh+XeLH9CPAKneTXXyfv
5
+ 4l/UOl0ECOpvHSNZVRkeMpqfi3hkeQJMpbgFcbN0RZWQV5ti1h3EKb0MCIcVXndG
6
+ xwiBWbOvHGkLwvFrtEClqbWIY8rtNdeWQx77Z3cmKPWFl0IcJhPjz8MMFG0gRxcJ
7
+ lGCcWShK68ZsbAwELFWEtOe+94gXkiAFXeVre88kV9jrYdp+6KemzL+EvjXTylut
8
+ ByqiLJvc6B+VN7omsGYDb2qpagIsBb7pdGBwew5R9NiIHHpcUdDjZJ/NCG74Vjnt
9
+ rfxDdORks5vppsTq3mWwGqcmgT50v/ZaMuSHFD+NAgMBAAGjYTBfMB0GA1UdDgQW
10
+ BBTzh5Sf8+voPm2ZI6iHFQ+gItYb6zAwBgNVHSMEKTAngBTzh5Sf8+voPm2ZI6iH
11
+ FQ+gItYb66EEpAIwAIIJAKY1nRwN7afeMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
12
+ AQEFBQADggEBACr61pBSpsz931OUztFcDjm07u+vatM5XE5qAH18BZeKbXzCRz4j
13
+ 9cAEYIYrtBdJZj3RLDzKB3Hn38BeuXebb4UlbzPJwIj1klHKH90Nwl84XWBCJqPX
14
+ rsGv7C5EOLAR1w/hxT+K2JC2LDJz9hkuDU1hjX1MeRpnJwXzPbHBQvgYb4lIgaOb
15
+ ikSlcTpkdG5fGafanyZwxDeQsy4tHAUP/jRQ4/Xzkzp0GzuX+v9d+2FXJLyoh6vJ
16
+ Fze3kL1+RH13Fn5NkdTAHpkX9AX0BuLKWslY3I/lusn3x9kPxVQTmTF2WQTVdKDg
17
+ FDvqWYvyLcuiWcfNUdy3EOpKLBKyyan3DZ8=
18
+ -----END CERTIFICATE-----
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ejson
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
5
+ platform: ruby
6
+ authors:
7
+ - Burke Libbey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: thor
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.18'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.18'
27
+ description: Secret management by encrypting values in a JSON hash with a public/private
28
+ keypair
29
+ email:
30
+ - burke.libbey@shopify.com
31
+ executables:
32
+ - ejson
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - Gemfile
38
+ - LICENSE.txt
39
+ - README.md
40
+ - Rakefile
41
+ - bin/ejson
42
+ - ejson.gemspec
43
+ - lib/ejson.rb
44
+ - lib/ejson/cli.rb
45
+ - lib/ejson/encryption.rb
46
+ - lib/ejson/version.rb
47
+ - test/ejson_test.rb
48
+ - test/privatekey.pem
49
+ - test/publickey.pem
50
+ homepage: https://github.com/Shopify/ejson
51
+ licenses:
52
+ - MIT
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.2.0
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Asymmetric keywise encryption for JSON
74
+ test_files:
75
+ - test/ejson_test.rb
76
+ - test/privatekey.pem
77
+ - test/publickey.pem
78
+ has_rdoc: