envcrypt 0.0.0 → 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 +4 -4
- data/.bundle/config +2 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +25 -0
- data/README.md +27 -25
- data/bin/envcrypt +60 -0
- data/envcrypt.gemspec +2 -2
- data/lib/envcrypt/envcrypt.rb +125 -0
- data/lib/envcrypt/version.rb +3 -0
- data/lib/envcrypt.rb +5 -0
- data/spec/envcrypt_spec.rb +92 -0
- metadata +16 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04c0530b0a65f7f72d719fd7c6caf06935b13fef
|
4
|
+
data.tar.gz: e9f9b03c19787d5079c1309c72fc45afd9d16509
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ff8940058084df79cd6b99aaf77db8d4031269b7bce2d7d2694744f4990a8c3a29008f11f74e91cceff693df2825fa6cad45e23a50a2168201b48fdee5e4fd1a
|
7
|
+
data.tar.gz: 3105d2ad9e7483e506a3091a65e89a820f70cdcc1624cfc2b949b909a6369bd1e28903d44fa0dbb2edc5bcbbd1077af9537ad91814f12a028de02c863976c007
|
data/.bundle/config
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p353
|
data/Gemfile
CHANGED
data/Gemfile.lock
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.2.5)
|
5
|
+
rspec (2.14.1)
|
6
|
+
rspec-core (~> 2.14.0)
|
7
|
+
rspec-expectations (~> 2.14.0)
|
8
|
+
rspec-mocks (~> 2.14.0)
|
9
|
+
rspec-core (2.14.8)
|
10
|
+
rspec-expectations (2.14.5)
|
11
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
12
|
+
rspec-mocks (2.14.6)
|
13
|
+
tomparse (0.4.2)
|
14
|
+
yard (0.8.7.4)
|
15
|
+
yard-tomdoc (0.7.1)
|
16
|
+
tomparse (>= 0.4.0)
|
17
|
+
yard
|
18
|
+
|
19
|
+
PLATFORMS
|
20
|
+
ruby
|
21
|
+
|
22
|
+
DEPENDENCIES
|
23
|
+
rspec (~> 2.14)
|
24
|
+
yard
|
25
|
+
yard-tomdoc
|
data/README.md
CHANGED
@@ -1,50 +1,53 @@
|
|
1
1
|
Envcrypt
|
2
2
|
=========
|
3
3
|
|
4
|
-
|
4
|
+
Envcrypt provides an easy way to securely encrypt and decrypt secrets
|
5
5
|
(passwords) that need to be stored for use in automated processes.
|
6
6
|
|
7
7
|
**Status:** Just have a README! Working on the rest.
|
8
8
|
|
9
9
|
## Use
|
10
10
|
|
11
|
-
Encrypt a secret
|
12
|
-
````ruby
|
13
|
-
$ envcrypt -p mypassword
|
11
|
+
Encrypt a secret via
|
14
12
|
|
15
|
-
|
16
|
-
|
13
|
+
````ruby
|
14
|
+
$ envcrypt -s Orange
|
15
|
+
Encrypted Secret: zTbH59gpFIIuXGYRuK9pHQ==
|
16
|
+
ENVCRYPT_KEY='eWa7QqyF6eE/bEthGO4BgA==$9OZzJ6xIgEcovfEOHIhVb9Gaw5/FeSgDmTErws1+API=$ccRiLqJjyL6MypWHOGfpcQ=='
|
17
|
+
WARNING: It is critical that the key and encryption password be stored separately!
|
17
18
|
````
|
18
19
|
|
19
20
|
Set the key as an environment variable (bash example)
|
21
|
+
|
20
22
|
````bash
|
21
|
-
export ENVCRYPT_KEY=
|
23
|
+
$ export ENVCRYPT_KEY='eWa7QqyF6eE/bEthGO4BgA==$9OZzJ6xIgEcovfEOHIhVb9Gaw5/FeSgDmTErws1+API=$ccRiLqJjyL6MypWHOGfpcQ=='
|
22
24
|
````
|
23
25
|
|
24
|
-
|
26
|
+
Go ahead and test decryption from the command line
|
25
27
|
````ruby
|
26
|
-
|
27
|
-
|
28
|
-
encrypted_pwd = "xxx"
|
29
|
-
decrypted_pwd = Envcrypt::decrypt(encrypted_pwd, key: ENV['ENVCRYPT_KEY'])
|
28
|
+
$ envcrypt -d 'zTbH59gpFIIuXGYRuK9pHQ=='
|
29
|
+
Decrypted: Orange
|
30
30
|
````
|
31
31
|
|
32
|
-
The second argument to decrypt is **optional**. The default `key` is
|
33
|
-
`ENV['ENVCRYPT_KEY']`, but you have to option to set it explicitly if you want to
|
34
|
-
get it from somewhere else.
|
35
32
|
|
36
|
-
|
33
|
+
Decrypt the password in Ruby code
|
37
34
|
|
38
|
-
|
39
|
-
|
35
|
+
````ruby
|
36
|
+
require 'envcrypt'
|
37
|
+
|
38
|
+
encrypted_pwd = "zTbH59gpFIIuXGYRuK9pHQ=="
|
39
|
+
crypt = Envcrypter.new(key: ENV['ENVCRYPT_KEY']) #key is optional (default: ENV['ENVCRYPT_KEY'])
|
40
|
+
decrypted_pwd = crypt.decrypt(encrypted_pwd)
|
41
|
+
````
|
40
42
|
|
41
43
|
##### Using existing keys to encrypt secrets
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
+
By default a new encryption key is created for each use command line
|
46
|
+
`envcrypt` tool. Secrets can also be encrypted using existing keys if
|
47
|
+
you want to use one key to encrypt multiple secrets.
|
45
48
|
|
46
49
|
````ruby
|
47
|
-
$ envcrypt -p
|
50
|
+
$ envcrypt -p Orange -k $ENVCRYPT_KEY
|
48
51
|
````
|
49
52
|
|
50
53
|
|
@@ -56,10 +59,9 @@ automate an interface with the web API. If an attacker somehow gains
|
|
56
59
|
access to the database or file, I'm screwed if I store the password as
|
57
60
|
plaintext or use some simple obfuscation. Envcrypt allows me to store
|
58
61
|
an encrypted version of the password and decrypt it only when needed.
|
59
|
-
The trick is to
|
60
|
-
|
61
|
-
|
62
|
-
variables.
|
62
|
+
The trick is to store the decryption key in an environment variable.
|
63
|
+
These can be set from the command line before launching the automated
|
64
|
+
process, in a locked down .bashrc file, or as Heroku config variables.
|
63
65
|
|
64
66
|
Of course, if an attacker was able to get a hold of *both* the password
|
65
67
|
and the decryption keys, you're screwed, but security is all about making
|
data/bin/envcrypt
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "envcrypt"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
module EnvcryptCL
|
7
|
+
extend self
|
8
|
+
|
9
|
+
def run
|
10
|
+
set_options
|
11
|
+
|
12
|
+
if @options[:pwd]
|
13
|
+
ENV['ENVCRYPT_KEY'] = @options[:key]
|
14
|
+
crypt = Envcrypt::Envcrypter.new
|
15
|
+
encrypted = crypt.encrypt(@options[:pwd])
|
16
|
+
|
17
|
+
puts "Encrypted Secret: #{encrypted}"
|
18
|
+
puts "ENVCRYPT_KEY=\'#{crypt.key}\'"
|
19
|
+
puts "WARNING: It is critical that the key and encryption password be stored separately!"
|
20
|
+
end
|
21
|
+
|
22
|
+
if @options[:encrypted_pwd]
|
23
|
+
crypt = Envcrypt::Envcrypter.new
|
24
|
+
decrypted = crypt.decrypt(@options[:encrypted_pwd])
|
25
|
+
|
26
|
+
puts "Decrypted: #{decrypted}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def set_options
|
32
|
+
@options = {}
|
33
|
+
|
34
|
+
OptionParser.new do |opts|
|
35
|
+
opts.banner = "Usage: envcrypt -s <SECRET>"
|
36
|
+
|
37
|
+
opts.on("-h","--help", "Show this message") do
|
38
|
+
puts opts
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
@options[:pwd] = nil
|
43
|
+
opts.on("-s","--secret <SECRET>","Generates an encrypted version of SECRET") do |opt|
|
44
|
+
@options[:pwd] = opt
|
45
|
+
end
|
46
|
+
|
47
|
+
@options[:encrypted_pwd] = nil
|
48
|
+
opts.on("-d","--decrypt <ENCRYPTED SECRET>","Decrypts the encrypted secret using the ENVCRYPT_KEY environment variable") do |opt|
|
49
|
+
@options[:encrypted_pwd] = opt
|
50
|
+
end
|
51
|
+
|
52
|
+
@options[:key] = nil
|
53
|
+
opts.on("-k","--key <KEY>","Uses the specified key to encrypt; e.g., -k $ENVCRYPT_KEY") do |opt|
|
54
|
+
@options[:key] = opt
|
55
|
+
end
|
56
|
+
end.parse!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
EnvcryptCL.run
|
data/envcrypt.gemspec
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
3
|
|
4
|
-
|
4
|
+
require 'envcrypt/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "envcrypt"
|
8
|
-
s.version =
|
8
|
+
s.version = Envcrypt::VERSION
|
9
9
|
s.authors = ["Sterling Paramore"]
|
10
10
|
s.email = ["gnilrets@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/gnilrets"
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# Public: Envcrypt module contains all of the methods/classes to
|
2
|
+
# perform encryption and decryption of passwords
|
3
|
+
#
|
4
|
+
# Examples
|
5
|
+
#
|
6
|
+
# mypassword = "secret"
|
7
|
+
# crypt = Envcrypter.new
|
8
|
+
# encrypted_pwd = crypt.encrypt(mypassword)
|
9
|
+
# decrypted_pwd = crypt.decrypt(encrypted_pwd)
|
10
|
+
module Envcrypt
|
11
|
+
class Envcrypter
|
12
|
+
|
13
|
+
# Public: Returns the key used to encrypt/decrypt secrets
|
14
|
+
attr_reader :key
|
15
|
+
|
16
|
+
# Public: Initialize an Envcrypter object
|
17
|
+
#
|
18
|
+
# key - A string representing the key to be used for encryption
|
19
|
+
# and decryption (default: ENV['ENVCRYPT_KEY'])
|
20
|
+
def initialize(key: ENV['ENVCRYPT_KEY'])
|
21
|
+
@key = key == nil ? generate_key : key
|
22
|
+
@de_cipher = nil
|
23
|
+
@en_cipher = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Generates a random key
|
27
|
+
#
|
28
|
+
# Returns the key
|
29
|
+
def generate_key
|
30
|
+
@key = "#{SecureRandom.base64}$#{SecureRandom.base64(32)}$#{SecureRandom.base64}"
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
# Public: Encrypts a secret
|
35
|
+
#
|
36
|
+
# secret - the secret string to be encrypted
|
37
|
+
#
|
38
|
+
# Returns the encrypted string
|
39
|
+
def encrypt(secret)
|
40
|
+
cipher = create_cipher(:encrypt)
|
41
|
+
|
42
|
+
encrypted = cipher.update secret
|
43
|
+
encrypted << cipher.final
|
44
|
+
Base64.encode64(encrypted).chomp
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Decrypts a secret
|
48
|
+
#
|
49
|
+
# secret - the encrypted string to be decrypted
|
50
|
+
#
|
51
|
+
# Returns the plain text decrypted string
|
52
|
+
def decrypt(encrypted)
|
53
|
+
cipher = create_cipher(:decrypt)
|
54
|
+
plaintxt = cipher.update Base64.decode64(encrypted)
|
55
|
+
plaintxt << cipher.final
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# Internal: Parses a key string into three components used by the
|
63
|
+
# encryption ciphers. The components are stored as private
|
64
|
+
# instance variables. Keeping it all in a single string
|
65
|
+
# simplifies storing the key in environment variables.
|
66
|
+
#
|
67
|
+
# key - the key to be parsed
|
68
|
+
#
|
69
|
+
# Returns the key parsed into three components (iv,pwd,salt)
|
70
|
+
def parse_key(key)
|
71
|
+
parsed = key.split('$')
|
72
|
+
if parsed.length != 3
|
73
|
+
raise "Bad key format - generate a new one"
|
74
|
+
else
|
75
|
+
parsed
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
# Internal: Create an encryption cipher
|
81
|
+
#
|
82
|
+
# mode - Set the mode to either :encrypt or :decrypt
|
83
|
+
#
|
84
|
+
# Returns a cipher to be used for encryption or decryption
|
85
|
+
def create_cipher(mode)
|
86
|
+
create_cipher_simple(mode)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Internal: Create a simple encryption cipher
|
90
|
+
#
|
91
|
+
# mode - Set the mode to either :encrypt or :decrypt
|
92
|
+
#
|
93
|
+
# Returns a cipher to be used for encryption or decryption
|
94
|
+
def create_cipher_simple(mode)
|
95
|
+
iv,pwd,salt = parse_key(@key)
|
96
|
+
|
97
|
+
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
98
|
+
cipher.send(mode)
|
99
|
+
|
100
|
+
cipher.iv = iv
|
101
|
+
cipher.key = pwd
|
102
|
+
cipher
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
# Future: Create a cipher using the more secure pbkdf2_mac method
|
107
|
+
#
|
108
|
+
# This one is more secure but doesn't work on Heroku
|
109
|
+
# Would like to optionally detect OpenSSL version
|
110
|
+
# and use this if possible
|
111
|
+
def create_cipher_pbkdf2(mode)
|
112
|
+
iv,pwd,salt = parse_key(@key)
|
113
|
+
|
114
|
+
cipher = OpenSSL::Cipher.new 'AES-128-CBC'
|
115
|
+
cipher.send(mode)
|
116
|
+
cipher.iv = iv
|
117
|
+
|
118
|
+
digest = OpenSSL::Digest::SHA256.new
|
119
|
+
key_len = cipher.key_len
|
120
|
+
iter = 20000
|
121
|
+
cipher.key = OpenSSL::PKCS5.pbkdf2_hmac(pwd, salt, iter, key_len, digest)
|
122
|
+
cipher
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/envcrypt.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
$LOAD_PATH << '../lib'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
require 'envcrypt'
|
7
|
+
|
8
|
+
include Envcrypt
|
9
|
+
|
10
|
+
|
11
|
+
describe "Envcrypt" do
|
12
|
+
describe "generating a key" do
|
13
|
+
let(:encrypter) { Envcrypter.new() }
|
14
|
+
|
15
|
+
it "should have a length of 94" do
|
16
|
+
expect(encrypter.key.length).to eq 94
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should parse into 3 components delimited by $" do
|
20
|
+
expect(encrypter.key.split("$").length).to eq 3
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "encrypting and decrypting a password" do
|
25
|
+
let(:password) { "mysecret" }
|
26
|
+
|
27
|
+
describe "with a generated key" do
|
28
|
+
it "should encrypt and decrypt properly" do
|
29
|
+
crypt = Envcrypter.new()
|
30
|
+
|
31
|
+
encrypted = crypt.encrypt(password)
|
32
|
+
plaintxt = crypt.decrypt(encrypted)
|
33
|
+
|
34
|
+
expect(plaintxt).to eq password
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
describe "with a supplied environment variable" do
|
40
|
+
before do
|
41
|
+
ENV['ENVCRYPT_KEY'] = "UnY9w3T5Qk3Q5JshOp/2HA==$8swxKYQxgyXaCyvMb+wP2HwqalpiSc3K4MpCvOpD2QY=$RK2cUDUHNBmI7miJcd6W4g=="
|
42
|
+
@crypt = Envcrypter.new()
|
43
|
+
end
|
44
|
+
|
45
|
+
after { ENV['ENVCRYPT_KEY'] = nil }
|
46
|
+
|
47
|
+
it "should have set the correct key" do
|
48
|
+
expect(@crypt.key).to eq ENV['ENVCRYPT_KEY']
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should still encrypt and decrypt properly" do
|
52
|
+
encrypted = @crypt.encrypt(password)
|
53
|
+
plaintxt = @crypt.decrypt(encrypted)
|
54
|
+
|
55
|
+
expect(plaintxt).to eq password
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
describe "with a different password" do
|
61
|
+
it "should encrypt to a different string" do
|
62
|
+
crypt = Envcrypter.new()
|
63
|
+
|
64
|
+
encrypted = crypt.encrypt(password)
|
65
|
+
encrypted_different = crypt.encrypt("#{password}plus")
|
66
|
+
|
67
|
+
expect(encrypted).not_to eq encrypted_different
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "with the same password but different key" do
|
72
|
+
before do
|
73
|
+
@crypt = Envcrypter.new()
|
74
|
+
@crypt2 = Envcrypter.new()
|
75
|
+
|
76
|
+
@encrypted = @crypt.encrypt(password)
|
77
|
+
@encrypted2 = @crypt2.encrypt(password)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should encrypt to a different string" do
|
81
|
+
expect(@encrypted).not_to eq @encrypted2
|
82
|
+
end
|
83
|
+
|
84
|
+
it "and they should still decrypt to the same password" do
|
85
|
+
plaintxt = @crypt.decrypt(@encrypted)
|
86
|
+
plaintxt2 = @crypt2.decrypt(@encrypted2)
|
87
|
+
|
88
|
+
expect(plaintxt).to eq plaintxt2
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
metadata
CHANGED
@@ -1,26 +1,37 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: envcrypt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sterling Paramore
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Simple secure encryption/decryption of secret data (passwords)
|
14
14
|
email:
|
15
15
|
- gnilrets@gmail.com
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- envcrypt
|
17
18
|
extensions: []
|
18
19
|
extra_rdoc_files: []
|
19
20
|
files:
|
21
|
+
- .bundle/config
|
22
|
+
- .gitignore
|
23
|
+
- .rspec
|
24
|
+
- .ruby-version
|
20
25
|
- Gemfile
|
26
|
+
- Gemfile.lock
|
21
27
|
- LICENSE
|
22
28
|
- README.md
|
29
|
+
- bin/envcrypt
|
23
30
|
- envcrypt.gemspec
|
31
|
+
- lib/envcrypt.rb
|
32
|
+
- lib/envcrypt/envcrypt.rb
|
33
|
+
- lib/envcrypt/version.rb
|
34
|
+
- spec/envcrypt_spec.rb
|
24
35
|
homepage: https://github.com/gnilrets
|
25
36
|
licenses:
|
26
37
|
- MIT
|
@@ -45,4 +56,5 @@ rubygems_version: 2.2.2
|
|
45
56
|
signing_key:
|
46
57
|
specification_version: 4
|
47
58
|
summary: Simple secure encryption/decryption of secret data
|
48
|
-
test_files:
|
59
|
+
test_files:
|
60
|
+
- spec/envcrypt_spec.rb
|