strongbox 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.9.3
4
+ - 1.8.7
5
+
6
+ gemfile:
7
+ - gemfiles/2.3.gemfile
8
+ - gemfiles/3.0.gemfile
9
+ - gemfiles/3.1.gemfile
10
+ - gemfiles/3.2.gemfile
data/README.md ADDED
@@ -0,0 +1,271 @@
1
+ # Strongbox
2
+
3
+ Strongbox provides [Public Key
4
+ Encryption](http://en.wikipedia.org/wiki/Public-key_cryptography) for
5
+ ActiveRecord. By using a public key, sensitive information can be
6
+ encrypted and stored automatically. Once stored a password is required
7
+ to access the information.
8
+
9
+ Because the largest amount of data that can practically be encrypted
10
+ with a public key is 245 bytes, by default Strongbox uses a two layer
11
+ approach. First it encrypts the attribute using symmetric encryption
12
+ with a randomly generated key and initialization vector (IV) (which
13
+ can just be thought of as a second key), then it encrypts those with
14
+ the public key.
15
+
16
+ Strongbox stores the encrypted attribute in a database column by the
17
+ same name, i.e. if you tell Strongbox to encrypt "secret" then it will
18
+ be store in `secret` in the database, just as the unencrypted
19
+ attribute would be. If symmetric encryption is used (the default) two
20
+ additional columns `secret_key` and `secret_iv` are needed as well.
21
+
22
+ The attribute is automatically encrypted simply by setting it:
23
+
24
+ ```ruby
25
+ user.secret = "Shhhhhhh..."
26
+ ```
27
+
28
+ and decrypted by calling the `decrypt` method with the private key password.
29
+
30
+ ```ruby
31
+ plain_text = user.secret.decrypt 'letmein'
32
+ ```
33
+
34
+ ## Environment
35
+
36
+ Strongbox is tested against Rails 2.3 and 3.x using Ruby 1.8.7, 1.9.2,
37
+ and 1.9.3.
38
+
39
+ ## Installation
40
+
41
+ Include the gem in your Gemfile:
42
+
43
+ ```ruby
44
+ gem "strongbox"
45
+ ```
46
+
47
+ Still using 2.x without a Gemfile? Put the following in
48
+ `config/environment.rb`:
49
+
50
+ ```ruby
51
+ config.gem "strongbox"
52
+ ```
53
+
54
+ ## Quick Start
55
+
56
+ In your model:
57
+
58
+ ```ruby
59
+ class User < ActiveRecord::Base
60
+ encrypt_with_public_key :secret,
61
+ :key_pair => Rails.root.join('config','keypair.pem')
62
+ end
63
+ ```
64
+
65
+ In your migrations:
66
+
67
+ ```ruby
68
+ class AddSecretColumnsToUser < ActiveRecord::Migration
69
+ def change
70
+ add_column :users, :secret, :binary
71
+ add_column :users, :secret_key, :binary
72
+ add_column :users, :secret_iv, :binary
73
+ end
74
+ end
75
+ ```
76
+
77
+ Generate a key pair:
78
+
79
+ (Choose a strong password.)
80
+
81
+ ```shell
82
+ openssl genrsa -des3 -out config/private.pem 2048
83
+ openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
84
+ cat config/private.pem config/public.pem >> config/keypair.pem
85
+ ```
86
+
87
+ In your views and forms you don't need to do anything special to
88
+ encrypt data. To decrypt call:
89
+
90
+ ```ruby
91
+ user.secret.decrypt 'password'
92
+ ```
93
+
94
+ ## Usage
95
+
96
+ The `encrypt_with_public_key` method sets up the attribute it's called
97
+ on for automatic encryption. It's simplest form is:
98
+
99
+ ```ruby
100
+ class User < ActiveRecord::Base
101
+ encrypt_with_public_key :secret,
102
+ :key_pair => Rails.root.join('config','keypair.pem')
103
+ end
104
+ ```
105
+
106
+ Which will encrypt the attribute `secret`. The attribute will be
107
+ encrypted using symmetric encryption with an automatically generated
108
+ key and IV encrypted using the public key. This requires three columns
109
+ in the database `secret`, `secret_key`, and `secret_iv` (see below).
110
+
111
+ Options to `encrypt_with_public_key` are:
112
+
113
+ * `:public_key` - Public key. Overrides :key_pair. See Key Formats below.
114
+
115
+ * `:private_key` - Private key. Overrides :key_pair.
116
+
117
+ * `:key_pair` - Key pair, containing both the public and private keys.
118
+
119
+ * `:symmetric` `:always`/`:never` - Encrypt the date using symmetric encryption. The public key is used to encrypt an automatically generated key and IV. This allows for large amounts of data to be encrypted. The size of data that can be encrypted directly with the public is limit to key size (in bytes) - 11. So a 2048 key can encrypt *245 bytes*. Defaults to `:always`.
120
+
121
+ * `:symmetric_cipher` - Cipher to use for symmetric encryption. Defaults to `aes-256-cbc`. Other ciphers support by OpenSSL may be used.
122
+
123
+ * `:base64` `true`/`false` - Use Base64 encoding to convert encrypted data to text. Use when binary save data storage is not available. Defaults to `false`.
124
+
125
+ * `:padding` - Method used to pad data encrypted with the public key. Defaults to `RSA_PKCS1_PADDING`. The default should be fine unless you are dealing with legacy data.
126
+
127
+ * `:ensure_required_columns` - Make sure the required database column(s) exist. Defaults to `true`, set to `false` if you want to encrypt/decrypt data stored outside of the database.
128
+
129
+ For example, encrypting a small attribute, providing only the public
130
+ key for extra security, and Base64 encoding the encrypted data:
131
+
132
+ ```ruby
133
+ class User < ActiveRecord::Base
134
+ validates_length_of :pin_code, :is => 4
135
+ encrypt_with_public_key :pin_code,
136
+ :symmetric => :never,
137
+ :base64 => true,
138
+ :public_key => Rails.root.join('config','public.pem')
139
+ end
140
+ ```
141
+
142
+ Strongbox can encrypt muliple attributes. `encrypt_with_public_key`
143
+ accepts a list of attributes, assuming they will use the same options:
144
+
145
+ ```ruby
146
+ class User < ActiveRecord::Base
147
+ encrypt_with_public_key :secret, :double_secret,
148
+ :key_pair => Rails.root.join('config','keypair.pem')
149
+ end
150
+ ```
151
+
152
+ If you need different options, call `encrypt_with_public_key` for each
153
+ attribute:
154
+
155
+ ```ruby
156
+ class User < ActiveRecord::Base
157
+ encrypt_with_public_key :secret,
158
+ :key_pair => Rails.root.join('config','keypair.pem')
159
+ encrypt_with_public_key :double_secret,
160
+ :key_pair => Rails.root.join('config','another_key.pem')
161
+ end
162
+ ```
163
+
164
+ ## Key Formats
165
+
166
+ `:public_key`, `:private_key`, and `:key_pair` can be in one of the
167
+ following formats:
168
+
169
+ * A string containing path to a file. This is the default interpretation of a string.
170
+ * A string contanting a key in PEM format, needs to match this the regex `/^-+BEGIN .* KEY-+$/`
171
+ * A symbol naming a method to call. Can return any of the other valid key formats.
172
+ * A instance of `OpenSSL::PKey::RSA`. Must be unlocked to be used as the private key.
173
+
174
+ ## Key Generation
175
+
176
+ ### In the shell
177
+
178
+ Generate a key pair:
179
+
180
+ ```shell
181
+ openssl genrsa -des3 -out config/private.pem 2048
182
+ Generating RSA private key, 2048 bit long modulus
183
+ ......+++
184
+ .+++
185
+ e is 65537 (0x10001)
186
+ Enter pass phrase for config/private.pem:
187
+ Verifying - Enter pass phrase for config/private.pem:
188
+ ```
189
+
190
+ and extract the the public key:
191
+
192
+ ```shell
193
+ openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
194
+ Enter pass phrase for config/private.pem:
195
+ writing RSA key
196
+ ```
197
+
198
+ If you are going to leave the private key installed it's easiest to
199
+ create a single key pair file:
200
+
201
+ ```shell
202
+ cat config/private.pem config/public.pem >> config/keypair.pem
203
+ ```
204
+
205
+ Or, for added security, store the private key file else where, leaving
206
+ only the public key.
207
+
208
+ ### In code
209
+
210
+ ```ruby
211
+ require 'openssl'
212
+ rsa_key = OpenSSL::PKey::RSA.new(2048)
213
+ cipher = OpenSSL::Cipher::Cipher.new('des3')
214
+ private_key = rsa_key.to_pem(cipher,'password')
215
+ public_key = rsa_key.public_key.to_pem
216
+ key_pair = private_key + public_key
217
+ ```
218
+
219
+ `private_key`, `public_key`, and `key_pair` are strings, store as you see fit.
220
+
221
+ ## Table Creation
222
+
223
+ In it's default configuration Strongbox requires three columns, one
224
+ the encrypted data, one for the encrypted symmetric key, and one for
225
+ the encrypted symmetric IV. If symmetric encryption is disabled then
226
+ only the columns for the data being encrypted is needed.
227
+
228
+ If your underlying database allows, use the **binary** column type. If
229
+ you must store your data in text format be sure to enable Base64
230
+ encoding and to use the *text* column type. If you use a *string*
231
+ column and encrypt anything greater than 186 bytes (245 bytes if you
232
+ don't enable Base64 encoding) **your data will be lost**.
233
+
234
+ ## Nil and Blank Attributes
235
+
236
+ By default, attributes set to nil will remain encrypted to protect all
237
+ information about the attribute. However, attributes may be set back
238
+ to true nil explicitly:
239
+
240
+ ```ruby
241
+ # Outside the model
242
+ @object[:secret] = nil # or ''
243
+ # Inside the model
244
+ self[:secret] = '' # or nil
245
+ ```
246
+
247
+ A setting to allow nil and blank attributes by default will be forth
248
+ coming.
249
+
250
+ ## Security Caveats
251
+
252
+ If you don't encrypt your data, then an attacker only needs to steal
253
+ that data to get your secrets.
254
+
255
+ If encrypt your data using symmetric encrypts and a stored key, then
256
+ the attacker needs the data and the key stored on the server.
257
+
258
+ If you use public key encryption, the attacker needs the data, the
259
+ private key, and the password. This means the attacker has to sniff
260
+ the password somehow, so that's what you need to protect against.
261
+
262
+ ## Authors
263
+
264
+ Spike Ilacqua
265
+
266
+ ## Thanks
267
+
268
+ Strongbox's implementation drew inspiration from Thoughtbot's
269
+ [Paperclip gem](https://github.com/thoughtbot/paperclip).
270
+
271
+ Thanks to everyone who's contributed!
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ Bundler::GemHelper.install_tasks
3
3
 
4
4
  require 'rake'
5
5
  require 'rake/testtask'
6
- require 'rake/rdoctask'
6
+ require 'rdoc/task'
7
7
 
8
8
  $LOAD_PATH << File.join(File.dirname(__FILE__), 'lib')
9
9
  require 'strongbox'
@@ -36,4 +36,4 @@ end
36
36
  desc "Build the gem"
37
37
  task :build => :gemspec do
38
38
  Gem::Builder.new($spec).build
39
- end
39
+ end
@@ -0,0 +1 @@
1
+ *.lock
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 2.3.15"
4
+ gem "strongbox", :path=>"../"
5
+
6
+ gemspec :path=>"../"
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.0.19"
4
+ gem "strongbox", :path=>"../"
5
+
6
+ gemspec :path=>"../"
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.1.10"
4
+ gem "strongbox", :path=>"../"
5
+
6
+ gemspec :path=>"../"
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "~> 3.2.10"
4
+ gem "strongbox", :path=>"../"
5
+
6
+ gemspec :path=>"../"
data/lib/strongbox.rb CHANGED
@@ -5,7 +5,7 @@ require 'strongbox/lock'
5
5
 
6
6
  module Strongbox
7
7
 
8
- VERSION = "0.6.0"
8
+ VERSION = "0.7.0"
9
9
 
10
10
  RSA_PKCS1_PADDING = OpenSSL::PKey::RSA::PKCS1_PADDING
11
11
  RSA_SSLV23_PADDING = OpenSSL::PKey::RSA::SSLV23_PADDING
@@ -143,7 +143,9 @@ private
143
143
 
144
144
  return key if key.is_a?(OpenSSL::PKey::RSA)
145
145
 
146
- if key !~ /^-+BEGIN .* KEY-+$/
146
+ if key.respond_to?(:read)
147
+ key = key.read
148
+ elsif key !~ /^-+BEGIN .* KEY-+$/
147
149
  key = File.read(key)
148
150
  end
149
151
  return OpenSSL::PKey::RSA.new(key,password)
data/strongbox.gemspec CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |s|
18
18
  s.files = `git ls-files`.split("\n")
19
19
  s.test_files = `git ls-files -- test/*`.split("\n")
20
20
  s.add_runtime_dependency 'activerecord'
21
- s.add_development_dependency 'thoughtbot-shoulda'
22
- s.add_development_dependency 'sqlite3'
21
+ s.add_development_dependency 'thoughtbot-shoulda', '>= 2.9.0'
22
+ s.add_development_dependency 'sqlite3', '~> 1.3.7'
23
+ s.add_development_dependency 'rake', '>= 10.0.0'
24
+ s.add_development_dependency 'rdoc', '>= 2.4.0'
23
25
  end
@@ -0,0 +1,77 @@
1
+ require 'test/test_helper'
2
+
3
+ class MethodKeyTest < Test::Unit::TestCase
4
+ context 'With an attribute containing a string for the key pair' do
5
+ setup do
6
+ @password = 'boost facile'
7
+ rebuild_model :key_pair => :key_pair_attribute
8
+ Dummy.class_eval do
9
+ attr_accessor :key_pair_attribute
10
+ end
11
+
12
+ @dummy = Dummy.new
13
+ @dummy.key_pair_attribute = File.read(File.join(FIXTURES_DIR,'keypair.pem'))
14
+ @dummy.secret = 'Shhhh'
15
+ end
16
+
17
+ should_encypted_and_decrypt
18
+ end
19
+
20
+ context 'With a methods returning the key pair' do
21
+ setup do
22
+ @password = 'boost facile'
23
+ rebuild_model :key_pair => :key_pair_method
24
+ Dummy.class_eval do
25
+ def key_pair_method
26
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
27
+ end
28
+ end
29
+
30
+ @dummy = Dummy.new
31
+ @dummy.secret = 'Shhhh'
32
+ end
33
+
34
+ should_encypted_and_decrypt
35
+ end
36
+
37
+ context 'With attributes containing strings for the keys' do
38
+ setup do
39
+ @password = 'boost facile'
40
+ rsa_key = OpenSSL::PKey::RSA.new(2048)
41
+ cipher = OpenSSL::Cipher::Cipher.new('des3')
42
+ rebuild_model :public_key => :public_key_attribute,
43
+ :private_key => :private_key_attribute
44
+ Dummy.class_eval do
45
+ attr_accessor :public_key_attribute, :private_key_attribute
46
+ end
47
+ @dummy = Dummy.new
48
+ @dummy.public_key_attribute = rsa_key.public_key.to_pem
49
+ @dummy.private_key_attribute = rsa_key.to_pem(cipher,@password)
50
+ @dummy.secret = 'Shhhh'
51
+ end
52
+
53
+ should_encypted_and_decrypt
54
+ end
55
+
56
+ context 'With methods returning the keys' do
57
+ setup do
58
+ @password = 'boost facile'
59
+ rebuild_model :public_key => :public_key_method,
60
+ :private_key => :private_key_method
61
+ Dummy.class_eval do
62
+ def public_key_method
63
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
64
+ end
65
+
66
+ def private_key_method
67
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
68
+ end
69
+ end
70
+
71
+ @dummy = Dummy.new
72
+ @dummy.secret = 'Shhhh'
73
+ end
74
+
75
+ should_encypted_and_decrypt
76
+ end
77
+ end
@@ -0,0 +1,57 @@
1
+ require 'test/test_helper'
2
+
3
+ class ProcKeyTest < Test::Unit::TestCase
4
+ context 'With a Proc returning a string for a key pair' do
5
+ setup do
6
+ @password = 'boost facile'
7
+ rebuild_model :key_pair => Proc.new {
8
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
9
+ }
10
+ @dummy = Dummy.new
11
+ @dummy.secret = 'Shhhh'
12
+ end
13
+
14
+ should_encypted_and_decrypt
15
+ end
16
+
17
+ context 'With a Proc returning a key object' do
18
+ setup do
19
+ @password = 'boost facile'
20
+ @private_key = OpenSSL::PKey::RSA.new(2048)
21
+ rebuild_model :key_pair => Proc.new { @private_key }
22
+ @dummy = Dummy.new
23
+ @dummy.secret = 'Shhhh'
24
+ end
25
+
26
+ should_encypted_and_decrypt
27
+ end
28
+
29
+ context 'With Procs returning public and private key strings' do
30
+ setup do
31
+ @password = 'boost facile'
32
+ @key_pair = File.read(File.join(FIXTURES_DIR,'keypair.pem'))
33
+
34
+ rebuild_model :public_key => Proc.new { @key_pair },
35
+ :private_key => Proc.new { @key_pair }
36
+ @dummy = Dummy.new
37
+ @dummy.secret = 'Shhhh'
38
+ end
39
+
40
+ should_encypted_and_decrypt
41
+ end
42
+
43
+ context 'With Procs returning public and private key objects' do
44
+ setup do
45
+ @password = 'boost facile'
46
+ @private_key = OpenSSL::PKey::RSA.new(2048)
47
+ @public_key = @private_key.public_key
48
+
49
+ rebuild_model :public_key => Proc.new { @public_key },
50
+ :private_key => Proc.new { @private_key }
51
+ @dummy = Dummy.new
52
+ @dummy.secret = 'Shhhh'
53
+ end
54
+
55
+ should_encypted_and_decrypt
56
+ end
57
+ end
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  require 'test/test_helper'
3
3
 
4
- class StrongboxMultiPlyTest < Test::Unit::TestCase
4
+ class StrongboxMultiplyTest < Test::Unit::TestCase
5
5
  context 'A Class with two secured fields' do
6
6
  setup do
7
7
  @password = 'boost facile'
@@ -46,7 +46,7 @@ class StrongboxTest < Test::Unit::TestCase
46
46
  end
47
47
  end
48
48
 
49
- should 'impliment to_json' do
49
+ should 'implement to_json' do
50
50
  assert_nothing_raised do
51
51
  @dummy.secret.to_json
52
52
  end
@@ -156,11 +156,11 @@ class StrongboxTest < Test::Unit::TestCase
156
156
  end
157
157
 
158
158
  context 'when a private key is not provided' do
159
- setup do
159
+ setup do
160
160
  @password = 'boost facile'
161
161
  rebuild_class(:public_key => File.join(FIXTURES_DIR,'keypair.pem'))
162
162
  @dummy = Dummy.new(:secret => 'Shhhh')
163
- end
163
+ end
164
164
 
165
165
  should 'raise on decrypt with a password' do
166
166
  assert_raises(Strongbox::StrongboxError) do
data/test/test_helper.rb CHANGED
@@ -2,12 +2,10 @@ ROOT = File.join(File.dirname(__FILE__), '..')
2
2
  RAILS_ROOT = ROOT
3
3
  $LOAD_PATH << File.join(ROOT, 'lib')
4
4
 
5
- require 'rubygems'
6
5
  require 'test/unit'
7
6
  require 'sqlite3'
8
7
  require 'active_record'
9
8
  require 'logger'
10
- gem 'thoughtbot-shoulda', ">= 2.9.0"
11
9
  require 'shoulda'
12
10
  begin require 'redgreen'; rescue LoadError; end
13
11
 
@@ -55,12 +53,12 @@ end
55
53
 
56
54
  def assert_has_errors_on(model,attribute)
57
55
  # Rails 2.X && Rails 3.X
58
- !model.errors[attribute].empty?
56
+ assert !model.errors[attribute].empty?
59
57
  end
60
58
 
61
59
  def assert_does_not_have_errors_on(model,attribute)
62
60
  # Rails 2.X Rails 3.X
63
- model.errors[attribute].nil? || model.errors[attribute].empty?
61
+ assert model.errors[attribute].nil? || model.errors[attribute].empty?
64
62
  end
65
63
 
66
64
  def generate_key_pair(password = nil,size = 2048)
@@ -26,7 +26,6 @@ class ValidationsTest < Test::Unit::TestCase
26
26
 
27
27
  context 'using validates_length_of' do
28
28
  setup do
29
- rebuild_class(:key_pair => File.join(FIXTURES_DIR,'keypair.pem'))
30
29
  Dummy.send(:validates_length_of,
31
30
  :secret,
32
31
  :in => 5..10,
@@ -64,7 +63,6 @@ class ValidationsTest < Test::Unit::TestCase
64
63
  if defined?(ActiveModel::Validations) # Rails 3
65
64
  context 'using validates for length' do
66
65
  setup do
67
- rebuild_class(:key_pair => File.join(FIXTURES_DIR,'keypair.pem'))
68
66
  Dummy.send(:validates,
69
67
  :secret,
70
68
  :length => {:minimum => 4, :maximum => 16})
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strongbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-18 00:00:00.000000000Z
12
+ date: 2013-02-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70366421924900 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,29 +21,76 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70366421924900
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: thoughtbot-shoulda
27
- requirement: &70366421905620 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
31
36
  - !ruby/object:Gem::Version
32
- version: '0'
37
+ version: 2.9.0
33
38
  type: :development
34
39
  prerelease: false
35
- version_requirements: *70366421905620
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 2.9.0
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: sqlite3
38
- requirement: &70366421904420 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.7
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.7
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
39
65
  none: false
40
66
  requirements:
41
67
  - - ! '>='
42
68
  - !ruby/object:Gem::Version
43
- version: '0'
69
+ version: 10.0.0
44
70
  type: :development
45
71
  prerelease: false
46
- version_requirements: *70366421904420
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 10.0.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rdoc
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 2.4.0
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 2.4.0
47
94
  description: ! " Strongbox provides Public Key Encryption for ActiveRecord. By
48
95
  using a\n public key sensitive information can be encrypted and stored automatically.\n
49
96
  \ Once stored a password is required to access the information. dependencies\n
@@ -54,10 +101,16 @@ extensions: []
54
101
  extra_rdoc_files: []
55
102
  files:
56
103
  - .gitignore
104
+ - .travis.yml
57
105
  - Gemfile
58
106
  - LICENSE
59
- - README.textile
107
+ - README.md
60
108
  - Rakefile
109
+ - gemfiles/.gitignore
110
+ - gemfiles/2.3.gemfile
111
+ - gemfiles/3.0.gemfile
112
+ - gemfiles/3.1.gemfile
113
+ - gemfiles/3.2.gemfile
61
114
  - init.rb
62
115
  - lib/strongbox.rb
63
116
  - lib/strongbox/lock.rb
@@ -66,7 +119,9 @@ files:
66
119
  - test/database.yml
67
120
  - test/fixtures/encrypted
68
121
  - test/fixtures/keypair.pem
122
+ - test/method_key_test.rb
69
123
  - test/missing_attributes_test.rb
124
+ - test/proc_key_test.rb
70
125
  - test/strongbox_multiply_test.rb
71
126
  - test/strongbox_test.rb
72
127
  - test/test_helper.rb
@@ -91,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
146
  version: '0'
92
147
  requirements: []
93
148
  rubyforge_project:
94
- rubygems_version: 1.8.10
149
+ rubygems_version: 1.8.24
95
150
  signing_key:
96
151
  specification_version: 3
97
152
  summary: Secures ActiveRecord fields with public key encryption.
@@ -99,7 +154,9 @@ test_files:
99
154
  - test/database.yml
100
155
  - test/fixtures/encrypted
101
156
  - test/fixtures/keypair.pem
157
+ - test/method_key_test.rb
102
158
  - test/missing_attributes_test.rb
159
+ - test/proc_key_test.rb
103
160
  - test/strongbox_multiply_test.rb
104
161
  - test/strongbox_test.rb
105
162
  - test/test_helper.rb
data/README.textile DELETED
@@ -1,182 +0,0 @@
1
- h1. Strongbox
2
-
3
- Strongbox provides Public Key Encryption for ActiveRecord. By using a public key sensitive information can be encrypted and stored automatically. Once stored a password is required to access the information.
4
-
5
- Because the largest amount of data that can practically be encrypted with a public key is 245 byte, by default Strongbox uses a two layer approach. First it encrypts the attribute using symmetric encryption with a randomly generated key and initialization vector (IV) (which can just be through to as a second key), then it encrypts those with the public key.
6
-
7
- Strongbox stores the encrypted attribute in a database column by the same name, i.e. if you tell Strongbox to encrypt "secret" then it will be store in "secret" in the database, just as the unencrypted attribute would be. If symmetric encryption is used (the default) two additional columns "secret_key" and "secret_iv" are needed as well.
8
-
9
- The attribute is automatically encrypted simply by setting it:
10
-
11
- user.secret = "Shhhhhhh..."
12
-
13
- and decrypted by calling the "decrypt" method with the private key password.
14
-
15
- plain_text = user.secret.decrypt 'letmein'
16
-
17
- h2. Quick Start
18
-
19
- In your model:
20
-
21
- bc. class User < ActiveRecord::Base
22
- encrypt_with_public_key :secret,
23
- :key_pair => File.join(RAILS_ROOT,'config','keypair.pem')
24
- end
25
-
26
- In your migrations:
27
-
28
- bc. class AddSecretColumnsToUser < ActiveRecord::Migration
29
- def self.up
30
- add_column :users, :secret, :binary
31
- add_column :users, :secret_key, :binary
32
- add_column :users, :secret_iv, :binary
33
- end
34
- def self.down
35
- remove_column :users, :secret
36
- remove_column :users, :secret_key
37
- remove_column :users, :secret_iv
38
- end
39
- end
40
-
41
- Generate a key pair:
42
-
43
- (Choose a strong password.)
44
-
45
- bc. openssl genrsa -des3 -out config/private.pem 2048
46
- openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
47
- cat config/private.pem config/public.pem >> config/keypair.pem
48
-
49
- In your views and forms you don't need to do anything special to encrypt data. To decrypt call:
50
-
51
- bc. user.secret.decrypt 'password'
52
-
53
- h2. Gem installation (Rails 2.1+)
54
-
55
- In config/environment.rb:
56
-
57
- bc. config.gem "strongbox"
58
-
59
- h2. Usage
60
-
61
- _encrypt_with_public_key_ sets up the attribute it's called on for automatic encryption. It's simplest form is:
62
-
63
- bc. class User < ActiveRecord::Base
64
- encrypt_with_public_key :secret,
65
- :key_pair => File.join(RAILS_ROOT,'config','keypair.pem')
66
- end
67
-
68
- Which will encrypt the attribute "secret". The attribute will be encrypted using symmetric encryption with an automatically generated key and IV encrypted using the public key. This requires three columns in the database "secret", "secret_key", and "secret_iv" (see below).
69
-
70
- Options to encrypt_with_public_key are:
71
-
72
- :public_key - Public key. Overrides :key_pair. See Key Formats below.
73
-
74
- :private_key - Private key. Overrides :key_pair.
75
-
76
- :key_pair - Key pair, containing both the public and private keys.
77
-
78
- :symmetric :always/:never - Encrypt the date using symmetric encryption. The public key is used to encrypt an automatically generated key and IV. This allows for large amounts of data to be encrypted. The size of data that can be encrypted directly with the public is limit to key size (in bytes) - 11. So a 2048 key can encrypt *245 bytes*. Defaults to *:always*.
79
-
80
- :symmetric_cipher - Cipher to use for symmetric encryption. Defaults to *'aes-256-cbc'*. Other ciphers support by OpenSSL may be used.
81
-
82
- :base64 true/false - Use Base64 encoding to convert encrypted data to text. Use when binary save data storage is not available. Defaults to *false*.
83
-
84
- :padding - Method used to pad data encrypted with the public key. Defaults to *RSA_PKCS1_PADDING*. The default should be fine unless you are dealing with legacy data.
85
-
86
- :ensure_required_columns - Make sure the required database column(s) exist. Defaults to *true*, set to false if you want to encrypt/decrypt data stored outside of the database.
87
-
88
- For example, encrypting a small attribute, providing only the public key for extra security, and Base64 encoding the encrypted data:
89
-
90
- bc. class User < ActiveRecord::Base
91
- validates_length_of :pin_code, :is => 4
92
- encrypt_with_public_key :pin_code,
93
- :symmetric => :never,
94
- :base64 => true,
95
- :public_key => File.join(RAILS_ROOT,'config','public.pem')
96
- end
97
-
98
- Strongbox can encrypt muliple attributes. _encrypt_with_public_key_ accepts a list of attributes, assuming they will use the same options:
99
-
100
- bc. class User < ActiveRecord::Base
101
- encrypt_with_public_key :secret, :double_secret,
102
- :key_pair => File.join(RAILS_ROOT,'config','keypair.pem')
103
- end
104
-
105
- If you need different options, call _encrypt_with_public_key_ for each attribute:
106
-
107
- bc. class User < ActiveRecord::Base
108
- encrypt_with_public_key :secret,
109
- :key_pair => File.join(RAILS_ROOT,'config','keypair.pem')
110
- encrypt_with_public_key :double_secret,
111
- :key_pair => File.join(RAILS_ROOT,'config','another_key.pem')
112
- end
113
-
114
- h2 Key Formats
115
-
116
- _:public_key_, _:private_key_, and _:key_pair_ can be in one of the following formats:
117
-
118
- * A string containing path to a file. This is the default interpretation of a string.
119
- * A string contanting a key in PEM format, needs to match this the regex /^-+BEGIN .* KEY-+$/
120
- * A symbol naming a method to call. Can return any of the other valid key formats.
121
- * A instance of OpenSSL::PKey::RSA. Must be unlocked to be used as the private key.
122
-
123
- h2. Key Generation
124
-
125
- h3. In the shell
126
-
127
- Generate a key pair:
128
-
129
- bc. openssl genrsa -des3 -out config/private.pem 2048
130
- Generating RSA private key, 2048 bit long modulus
131
- ......+++
132
- .+++
133
- e is 65537 (0x10001)
134
- Enter pass phrase for config/private.pem:
135
- Verifying - Enter pass phrase for config/private.pem:
136
-
137
- and extract the the public key:
138
-
139
- bc. openssl rsa -in config/private.pem -out config/public.pem -outform PEM -pubout
140
- Enter pass phrase for config/private.pem:
141
- writing RSA key
142
-
143
- If you are going to leave the private key installed it's easiest to create a single key pair file:
144
-
145
- bc. cat config/private.pem config/public.pem >> config/keypair.pem
146
-
147
- Or, for added security, store the private key file else where, leaving only the public key.
148
-
149
- h3. In code
150
-
151
- bc. require 'openssl'
152
- rsa_key = OpenSSL::PKey::RSA.new(2048)
153
- cipher = OpenSSL::Cipher::Cipher.new('des3')
154
- private_key = rsa_key.to_pem(cipher,'password')
155
- public_key = rsa_key.public_key.to_pem
156
- key_pair = private_key + public_key
157
-
158
- _private_key_, _public_key_, and _key_pair_ are strings, store as you see fit.
159
-
160
- h2. Table Creation
161
-
162
- In it's default configuration Strongbox requires three columns, one the encrypted data, one for the encrypted symmetric key, and one for the encrypted symmetric IV. If symmetric encryption is disabled then only the columns for the data being encrypted is needed.
163
-
164
- If your underlying database allows, use the *binary* column type. If you must store your data in text format be sure to enable Base64 encoding and to use the *text* column type. If you use a _string_ column and encrypt anything greater than 186 bytes (245 bytes if you don't enable Base64 encoding) *your data will be lost*.
165
-
166
-
167
- h2. Security Caveats
168
-
169
- If you don't encrypt your data, then an attacker only needs to steal that data to get your secrets.
170
-
171
- If encrypt your data using symmetric encrypts and a stored key, then the attacker needs the data and the key stored on the server.
172
-
173
- If you use public key encryption, the attacker needs the data, the private key, and the password. This means the attacker has to sniff the password somehow, so that's what you need to protect against.
174
-
175
- h2. Authors
176
-
177
- Spike Ilacqua
178
-
179
- h2. Thanks
180
-
181
- Strongbox's implementation drew inspiration from Thoughtbot's Paperclip gem http://www.thoughtbot.com/projects/paperclip
182
-