symmetric-encryption 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +278 -7
- data/examples/symmetric-encryption.yml +66 -0
- data/lib/symmetric-encryption.rb +4 -1
- data/lib/symmetric/encryption.rb +24 -1
- data/lib/symmetric/railtie.rb +1 -1
- data/lib/symmetric/railties/symmetric_encrypted_validator.rb +18 -0
- data/lib/symmetric/version.rb +1 -1
- data/symmetric-encryption-0.1.2.gem +0 -0
- data/test/attr_encrypted_test.rb +156 -0
- data/test/config/database.yml +7 -0
- data/test/config/symmetric-encryption.yml +43 -0
- data/test/config/test.iv +4 -0
- data/test/config/test.key +2 -0
- data/test/encryption_test.rb +21 -82
- metadata +11 -5
- data/symmetric-encryption-0.1.0.gem +0 -0
- data/symmetric-encryption-0.1.1.gem +0 -0
data/README.md
CHANGED
@@ -3,7 +3,7 @@ symmetric-encryption
|
|
3
3
|
|
4
4
|
* http://github.com/ClarityServices/symmetric-encryption
|
5
5
|
|
6
|
-
|
6
|
+
## Introduction
|
7
7
|
|
8
8
|
Any project that wants to meet PCI compliance has to ensure that the data is encrypted
|
9
9
|
whilst in flight and at rest. Amongst many other other requirements all passwords
|
@@ -12,24 +12,288 @@ in configuration files have to be encrypted
|
|
12
12
|
This Gem helps achieve compliance by supporting encryption of data in a simple
|
13
13
|
and consistent way
|
14
14
|
|
15
|
-
|
15
|
+
## Security
|
16
|
+
|
17
|
+
Many solutions that encrypt data require the encryption keys to be stored in the
|
18
|
+
applications source code or leave it up to the developer to secure the keys on
|
19
|
+
the application servers. symmetric-encryption takes care of securing the
|
20
|
+
symmetric encryption keys.
|
21
|
+
|
22
|
+
The following steps are used to secure the symmetric encryption keys using symmetric-encryption:
|
23
|
+
|
24
|
+
* Symmetric Encryption keys are stored in files that are not part of the application,
|
25
|
+
its source code, or even stored in its source control system. These files can be
|
26
|
+
created, managed and further secured by System Administrators. This prevents
|
27
|
+
developers having or needing to have access to the symmetric encryption keys
|
28
|
+
* The Operating System security features limit access to the Symmetric Encryption
|
29
|
+
key files to System Administrators and the userid under which the Rails application runs.
|
30
|
+
* The files in which the Symmetric Encryption keys are stored are futher
|
31
|
+
encrypted using RSA 2048 bit encryption
|
32
|
+
|
33
|
+
In order for anyone to decrypt the data being encrypted in the database, they
|
34
|
+
would need access to ALL of the following:
|
35
|
+
* A copy of the files containing the Symmetric Encryption Keys which are secured
|
36
|
+
by the Operating System
|
37
|
+
* The application source code containing the RSA private key to decrypt the above files
|
38
|
+
* The userid and password for the database to copy the encrypted data itself,
|
39
|
+
or an unsecured copy or export of the database contents
|
40
|
+
|
41
|
+
## Features
|
16
42
|
|
17
43
|
* Encryption of passwords in configuration files
|
18
|
-
*
|
44
|
+
* Encryption of ActiveRecord model attributes by prefixing attributes / column
|
45
|
+
names with encrypted_
|
19
46
|
* Externalization of symmetric encryption keys so that they are not in the
|
20
|
-
source code
|
47
|
+
source code, or the source code control system
|
48
|
+
* Drop in replacement for attr_encrypted. Just remove the attr_encrypted gem
|
49
|
+
* Compatible with the default Encryption algorithm in attr_encrypted
|
50
|
+
* More efficient replacement for attr_encrypted since only ActiveRecord Models
|
51
|
+
are extended with encrypted_ behavior, rather than every object in the system
|
52
|
+
* Custom validator for ActiveRecord Models
|
53
|
+
|
54
|
+
## Examples
|
55
|
+
|
56
|
+
### Encryption Example
|
57
|
+
|
58
|
+
Symmetric::Encryption.encrypt "Sensitive data"
|
59
|
+
|
60
|
+
### Decryption Example
|
61
|
+
|
62
|
+
Symmetric::Encryption.decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n"
|
63
|
+
|
64
|
+
### Validation Example
|
65
|
+
|
66
|
+
class MyModel < ActiveRecord::Base
|
67
|
+
validates :encrypted_ssn, :symmetric_encrypted => true
|
68
|
+
end
|
69
|
+
|
70
|
+
m = MyModel.new
|
71
|
+
m.valid?
|
72
|
+
# => false
|
73
|
+
m.encrypted_ssn = Symmetric::Encryption.encrypt('123456789')
|
74
|
+
m.valid?
|
75
|
+
# => true
|
76
|
+
|
77
|
+
### Encrypting Passwords in configuration files
|
78
|
+
|
79
|
+
Passwords can be encrypted in any YAML configuration file.
|
80
|
+
|
81
|
+
For example config/database.yml
|
82
|
+
|
83
|
+
production:
|
84
|
+
adapter: mysql
|
85
|
+
host: db1w
|
86
|
+
database: myapp_production
|
87
|
+
username: admin
|
88
|
+
password: <%= Symmetric::Encryption.try_decrypt "JqLJOi6dNjWI9kX9lSL1XQ==\n" %>
|
89
|
+
|
90
|
+
Note: Use Symmetric::Encryption.try_decrypt method which will return nil if it
|
91
|
+
fails to decrypt the value, which is essential when the encryption keys differ
|
92
|
+
between environments
|
93
|
+
|
94
|
+
Note: In order for the above technique to work in other YAML configuration files
|
95
|
+
the YAML file must be processed using ERB prior to passing to YAML. For example
|
21
96
|
|
22
|
-
|
97
|
+
config_file = Rails.root.join('config', 'redis.yml')
|
98
|
+
raise "redis config not found. Create a config file at: config/redis.yml" unless config_file.file?
|
99
|
+
|
100
|
+
cfg = YAML.load(ERB.new(File.new(config_file).read).result)[Rails.env]
|
101
|
+
raise("Environment #{Rails.env} not defined in redis.yml") unless cfg
|
102
|
+
|
103
|
+
### Generating encrypted passwords
|
104
|
+
|
105
|
+
The following rake task can be used to generate encrypted passwords for the
|
106
|
+
specified environment
|
107
|
+
|
108
|
+
Note: Passwords must be encrypted in the environment in which they will be used.
|
109
|
+
Since each environment should have its own symmetric encryption keys
|
110
|
+
|
111
|
+
## Install
|
23
112
|
|
24
113
|
gem install symmetric-encryption
|
25
114
|
|
115
|
+
## Configuration
|
116
|
+
|
117
|
+
### Generating the RSA Private key
|
118
|
+
|
119
|
+
To protect the files holding the Symmetric Encryption keys, symmetric-encryption uses 2048 bit RSA
|
120
|
+
encryption.
|
121
|
+
|
122
|
+
Generate the RSA Private key as follows
|
123
|
+
|
124
|
+
openssl genrsa 2048
|
125
|
+
|
126
|
+
Paste the output into the configuration created below
|
127
|
+
|
128
|
+
### Creating the configuration file
|
129
|
+
|
130
|
+
Create a configuration file in config/symmetric-encryption.yml per the following example:
|
131
|
+
|
132
|
+
#
|
133
|
+
# Symmetric Encryption for Ruby
|
134
|
+
#
|
135
|
+
---
|
136
|
+
# Just use test symmetric encryption keys in the development environment
|
137
|
+
# No private key required since we are not reading the keys from a file
|
138
|
+
development: &development_defaults
|
139
|
+
cipher: aes-256-cbc
|
140
|
+
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
141
|
+
symmetric_iv: 1234567890ABCDEF
|
142
|
+
|
143
|
+
test:
|
144
|
+
<<: *development_defaults
|
145
|
+
|
146
|
+
release: &release_defaults
|
147
|
+
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
148
|
+
# source code, we only hold a RSA key that is used to unlock the file
|
149
|
+
# containing the actual symmetric encryption key
|
150
|
+
#
|
151
|
+
# To generate a new RSA private key:
|
152
|
+
# openssl genrsa 2048
|
153
|
+
private_rsa_key: |
|
154
|
+
-----BEGIN RSA PRIVATE KEY-----
|
155
|
+
...
|
156
|
+
paste RSA key generated above here
|
157
|
+
...
|
158
|
+
-----END RSA PRIVATE KEY-----
|
159
|
+
|
160
|
+
# Filename containing Symmetric Encryption Key
|
161
|
+
# Note: The file contents must be RSA 2048 bit encrypted
|
162
|
+
# with the public key derived from the private key above
|
163
|
+
symmetric_key_filename: /etc/rails/.rails.key
|
164
|
+
symmetric_iv_filename: /etc/rails/.rails.iv
|
165
|
+
|
166
|
+
# Use aes-256-cbc encryption
|
167
|
+
cipher: aes-256-cbc
|
168
|
+
|
169
|
+
hotfix:
|
170
|
+
<<: *release_defaults
|
171
|
+
|
172
|
+
production:
|
173
|
+
<<: *release_defaults
|
174
|
+
|
175
|
+
This configuration file should be checked into the source code control system.
|
176
|
+
It does Not include the Symmetric Encryption keys. They will be generated in the
|
177
|
+
next step.
|
178
|
+
|
179
|
+
### Generating and securing the Symmetric Encryption keys
|
180
|
+
|
181
|
+
The symmetric encryption key consists of the key itself and an optional
|
182
|
+
initialization vector.
|
183
|
+
|
184
|
+
To generate the keys run the following Rake task in each environment:
|
185
|
+
|
186
|
+
RAILS_ENV=release rake symmetric_encryption:generate_symmetric_keys
|
187
|
+
|
188
|
+
Replace 'release' as necessary for each environment.
|
189
|
+
|
190
|
+
Make sure that the current user has read and write access to the folder listed
|
191
|
+
in the configuration option symmetric_key_filename above.
|
192
|
+
|
193
|
+
Once the Symmetric Encryption keys have been generated, secure them further by
|
194
|
+
making the files read-only to the Rails user and not readable by any other user
|
195
|
+
|
196
|
+
chmod ...
|
197
|
+
|
198
|
+
When running multiple Rails servers in a particular environment copy the same
|
199
|
+
key files to every server in that environment. I.e. All Rails servers in each
|
200
|
+
environment must run the same encryption keys.
|
201
|
+
|
202
|
+
Note: The generate step above must only be run once in each environment
|
203
|
+
|
204
|
+
## Using in non-Rails environments
|
205
|
+
|
206
|
+
symmetric-encryption can also be used in non-Rails environment. At application
|
207
|
+
startup, run the code below to initialize symmetric-encryption prior to
|
208
|
+
attempting to encrypt or decrypt any data
|
209
|
+
|
210
|
+
require 'symmetric-encryption'
|
211
|
+
Symmetric::Encryption.load!('config/symmetric-encryption.yml', 'production')
|
212
|
+
|
213
|
+
Parameters:
|
214
|
+
* Filename of the configuration file created above
|
215
|
+
* Name of the environment to load the configuration for
|
216
|
+
|
217
|
+
To manually generate the symmetric encryption keys, run the code below
|
218
|
+
|
219
|
+
require 'symmetric-encryption'
|
220
|
+
Symmetric::Encryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production')
|
221
|
+
|
222
|
+
Parameters:
|
223
|
+
* Filename of the configuration file created above
|
224
|
+
* Name of the environment to load the configuration for
|
225
|
+
|
226
|
+
## Supporting Multiple Encryption Keys
|
227
|
+
|
228
|
+
For complete PCI compliance it is necessary to change the Symmetric Encryption
|
229
|
+
keys every year. During the transition period of moving from one encryption
|
230
|
+
key to another symmetric-encryption supports multiple Symmetric Encryption keys.
|
231
|
+
If decryption with the current key fails, any previous keys will also be tried
|
232
|
+
automatically.
|
233
|
+
|
234
|
+
By default the latest key is used for encrypting data. Another key can be specified
|
235
|
+
for encryption so that old data can be looked in queries, etc.
|
236
|
+
|
237
|
+
Since just the Symmetric Encryption keys are being changed, we can still continue to
|
238
|
+
use the same RSA Private key for gaining access to the Symmetric Encryption Keys
|
239
|
+
|
240
|
+
### Configuring multiple Symmetric Encryption keys
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
Create a configuration file in config/symmetric-encryption.yml per the following example:
|
245
|
+
|
246
|
+
#
|
247
|
+
# Symmetric Encryption for Ruby
|
248
|
+
#
|
249
|
+
---
|
250
|
+
# Just use test symmetric encryption keys in the development environment
|
251
|
+
# No private key required since we are not reading the keys from a file
|
252
|
+
development: &development_defaults
|
253
|
+
cipher: aes-256-cbc
|
254
|
+
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
255
|
+
symmetric_iv: 1234567890ABCDEF
|
256
|
+
|
257
|
+
test:
|
258
|
+
<<: *development_defaults
|
259
|
+
|
260
|
+
release: &release_defaults
|
261
|
+
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
262
|
+
# source code, we only hold a RSA key that is used to unlock the file
|
263
|
+
# containing the actual symmetric encryption key
|
264
|
+
#
|
265
|
+
# To generate a new RSA private key:
|
266
|
+
# openssl genrsa 2048
|
267
|
+
private_rsa_key: |
|
268
|
+
-----BEGIN RSA PRIVATE KEY-----
|
269
|
+
...
|
270
|
+
paste RSA key generated above here
|
271
|
+
...
|
272
|
+
-----END RSA PRIVATE KEY-----
|
273
|
+
|
274
|
+
# Filename containing Symmetric Encryption Key
|
275
|
+
# Note: The file contents must be RSA 2048 bit encrypted
|
276
|
+
# with the public key derived from the private key above
|
277
|
+
symmetric_key_filename: /etc/rails/.rails.key
|
278
|
+
symmetric_iv_filename: /etc/rails/.rails.iv
|
279
|
+
|
280
|
+
# Use aes-256-cbc encryption
|
281
|
+
cipher: aes-256-cbc
|
282
|
+
|
283
|
+
hotfix:
|
284
|
+
<<: *release_defaults
|
285
|
+
|
286
|
+
production:
|
287
|
+
<<: *release_defaults
|
288
|
+
|
289
|
+
|
290
|
+
|
26
291
|
Meta
|
27
292
|
----
|
28
293
|
|
29
294
|
* Code: `git clone git://github.com/ClarityServices/symmetric-encryption.git`
|
30
295
|
* Home: <https://github.com/ClarityServices/symmetric-encryption>
|
31
|
-
*
|
32
|
-
* Bugs: <http://github.com/reidmorrison/symmetric-encryption/issues>
|
296
|
+
* Bugs: <http://github.com/ClarityServices/symmetric-encryption/issues>
|
33
297
|
* Gems: <http://rubygems.org/gems/symmetric-encryption>
|
34
298
|
|
35
299
|
This project uses [Semantic Versioning](http://semver.org/).
|
@@ -55,3 +319,10 @@ distributed under the License is distributed on an "AS IS" BASIS,
|
|
55
319
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
56
320
|
See the License for the specific language governing permissions and
|
57
321
|
limitations under the License.
|
322
|
+
|
323
|
+
Compliance
|
324
|
+
----------
|
325
|
+
|
326
|
+
Although this library has assisted Clarity in meeting PCI Compliance it in no
|
327
|
+
way guarantees that PCI Compliance will be met by anyone using this library
|
328
|
+
for encryption purposes.
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#
|
2
|
+
# Symmetric Encryption for Ruby
|
3
|
+
#
|
4
|
+
---
|
5
|
+
# Just use test symmetric encryption keys in the development environment
|
6
|
+
# No private key required since we are not reading the keys from a file
|
7
|
+
development: &development_defaults
|
8
|
+
cipher: aes-256-cbc
|
9
|
+
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
10
|
+
symmetric_iv: 1234567890ABCDEF
|
11
|
+
|
12
|
+
test:
|
13
|
+
<<: *development_defaults
|
14
|
+
|
15
|
+
release: &release_defaults
|
16
|
+
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
17
|
+
# source code, we only hold a RSA key that is used to unlock the file
|
18
|
+
# containing the actual symmetric encryption key
|
19
|
+
#
|
20
|
+
# To generate a new RSA private key:
|
21
|
+
# openssl genrsa 2048
|
22
|
+
|
23
|
+
# Sample RSA Key, do not use this one as is, generate a new one
|
24
|
+
private_rsa_key: |
|
25
|
+
-----BEGIN RSA PRIVATE KEY-----
|
26
|
+
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
27
|
+
6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA
|
28
|
+
qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf
|
29
|
+
IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy
|
30
|
+
fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4
|
31
|
+
WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e
|
32
|
+
ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI
|
33
|
+
k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ
|
34
|
+
0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h
|
35
|
+
Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL
|
36
|
+
ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9
|
37
|
+
nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+
|
38
|
+
nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj
|
39
|
+
M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll
|
40
|
+
SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf
|
41
|
+
suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR
|
42
|
+
hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun
|
43
|
+
MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5
|
44
|
+
ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll
|
45
|
+
ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd
|
46
|
+
/XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi
|
47
|
+
VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4
|
48
|
+
h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn
|
49
|
+
PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz
|
50
|
+
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
51
|
+
-----END RSA PRIVATE KEY-----
|
52
|
+
|
53
|
+
# Filename containing Symmetric Encryption Key
|
54
|
+
# Note: The file contents must be RSA 2048 bit encrypted
|
55
|
+
# with the public key derived from the private key above
|
56
|
+
symmetric_key_filename: /etc/rails/.rails.key
|
57
|
+
symmetric_iv_filename: /etc/rails/.rails.iv
|
58
|
+
|
59
|
+
# Use aes-256-cbc encryption
|
60
|
+
cipher: aes-256-cbc
|
61
|
+
|
62
|
+
hotfix:
|
63
|
+
<<: *release_defaults
|
64
|
+
|
65
|
+
production:
|
66
|
+
<<: *release_defaults
|
data/lib/symmetric-encryption.rb
CHANGED
@@ -2,5 +2,8 @@ require 'symmetric/version'
|
|
2
2
|
require 'symmetric/encryption'
|
3
3
|
if defined?(Rails)
|
4
4
|
require 'symmetric/railtie'
|
5
|
-
|
5
|
+
end
|
6
|
+
if defined?(ActiveRecord::Base)
|
7
|
+
require 'symmetric/extensions/active_record/base'
|
8
|
+
require 'symmetric/railties/symmetric_encrypted_validator'
|
6
9
|
end
|
data/lib/symmetric/encryption.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'base64'
|
2
2
|
require 'openssl'
|
3
3
|
require 'zlib'
|
4
|
+
require 'yaml'
|
4
5
|
|
5
6
|
module Symmetric
|
6
7
|
|
@@ -16,6 +17,27 @@ module Symmetric
|
|
16
17
|
MAGIC_HEADER_SIZE = MAGIC_HEADER.size
|
17
18
|
end
|
18
19
|
|
20
|
+
# The minimum length for an encrypted string
|
21
|
+
def self.min_encrypted_length
|
22
|
+
@@min_encrypted_length ||= encrypt('1').length
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns [true|false] a best effort determination as to whether the supplied
|
26
|
+
# string is encrypted or not, without incurring the penalty of actually
|
27
|
+
# decrypting the supplied data
|
28
|
+
# Parameters:
|
29
|
+
# encrypted_data: Encrypted string
|
30
|
+
def self.encrypted?(encrypted_data)
|
31
|
+
# Simple checks first
|
32
|
+
return false if (encrypted_data.length < min_encrypted_length) || (!encrypted_data.end_with?("\n"))
|
33
|
+
# For now have to decrypt it fully
|
34
|
+
begin
|
35
|
+
decrypt(encrypted_data) ? true : false
|
36
|
+
rescue
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
19
41
|
# Set the Symmetric Cipher to be used
|
20
42
|
def self.cipher=(cipher)
|
21
43
|
@@cipher = cipher
|
@@ -55,8 +77,9 @@ module Symmetric
|
|
55
77
|
self.key = symmetric_key
|
56
78
|
self.iv = symmetric_iv
|
57
79
|
else
|
58
|
-
|
80
|
+
load_keys(config['symmetric_key_filename'], config['symmetric_iv_filename'], config['private_rsa_key'])
|
59
81
|
end
|
82
|
+
true
|
60
83
|
end
|
61
84
|
|
62
85
|
# Load the symmetric key to use for encrypting and decrypting data
|
data/lib/symmetric/railtie.rb
CHANGED
@@ -28,7 +28,7 @@ module Symmetric #:nodoc:
|
|
28
28
|
#
|
29
29
|
# Loaded before Active Record initializes since database.yml can have encrypted
|
30
30
|
# passwords in it
|
31
|
-
initializer "load
|
31
|
+
initializer "load symmetric encryption keys" , :before=>"active_record.initialize_database" do
|
32
32
|
config_file = Rails.root.join("config", "symmetric-encryption.yml")
|
33
33
|
if config_file.file?
|
34
34
|
::Symmetric::Encryption.load!(config_file, Rails.env)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Add an ActiveModel Validator
|
2
|
+
#
|
3
|
+
# Example:
|
4
|
+
# class MyModel < ActiveRecord::Base
|
5
|
+
# validates :encrypted_ssn, :symmetric_encrypted => true
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# m = MyModel.new
|
9
|
+
# m.valid?
|
10
|
+
# # => false
|
11
|
+
# m.encrypted_ssn = Symmetric::Encryption.encrypt('123456789')
|
12
|
+
# m.valid?
|
13
|
+
# # => true
|
14
|
+
class SymmetricEncryptedValidator < ActiveModel::EachValidator
|
15
|
+
def validate_each(record, attribute, value)
|
16
|
+
record.errors.add(attribute, "must be a value encrypted using Symmetric::Encryption.encrypt") unless Symmetric::Encryption.encrypted?(value)
|
17
|
+
end
|
18
|
+
end
|
data/lib/symmetric/version.rb
CHANGED
Binary file
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# Allow examples to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'logger'
|
6
|
+
require 'erb'
|
7
|
+
require 'test/unit'
|
8
|
+
require 'shoulda'
|
9
|
+
require 'active_record'
|
10
|
+
require 'symmetric-encryption'
|
11
|
+
|
12
|
+
# #TODO Need to supply the model and migrations for this test
|
13
|
+
# Adding to existing AR model User
|
14
|
+
# Unit Test for Symmetric::Encryption
|
15
|
+
#
|
16
|
+
|
17
|
+
#ROOT = File.join(File.dirname(__FILE__), '..')
|
18
|
+
|
19
|
+
#['/lib', '/db'].each do |folder|
|
20
|
+
# $:.unshift File.join(ROOT, folder)
|
21
|
+
#end
|
22
|
+
|
23
|
+
ActiveRecord::Base.logger = Logger.new($stderr)
|
24
|
+
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
25
|
+
ActiveRecord::Base.establish_connection('test')
|
26
|
+
|
27
|
+
#ActiveRecord::Base.connection.create_database 'symmetric_encryption_test', :charset => :utf8
|
28
|
+
#require 'db/schema'
|
29
|
+
|
30
|
+
#The file db/schema.rb contains, for example:
|
31
|
+
|
32
|
+
ActiveRecord::Schema.define :version => 0 do
|
33
|
+
create_table :users, :force => true do |t|
|
34
|
+
t.string :encrypted_bank_account_number
|
35
|
+
t.string :encrypted_social_security_number
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class User < ActiveRecord::Base
|
40
|
+
attr_encrypted :bank_account_number
|
41
|
+
attr_encrypted :social_security_number
|
42
|
+
|
43
|
+
validates :encrypted_bank_account_number, :symmetric_encrypted => true
|
44
|
+
validates :encrypted_social_security_number, :symmetric_encrypted => true
|
45
|
+
end
|
46
|
+
|
47
|
+
class AttrEncryptedTest < Test::Unit::TestCase
|
48
|
+
context 'initialized' do
|
49
|
+
|
50
|
+
setup do
|
51
|
+
Symmetric::Encryption.load!(File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml'), 'test')
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'an ActiveRecord environment' do
|
55
|
+
setup do
|
56
|
+
config_file = File.join(File.dirname(__FILE__), 'config', 'database.yml')
|
57
|
+
raise "database config not found. Create a config file at: test/config/database.yml" unless File.exists? config_file
|
58
|
+
|
59
|
+
cfg = YAML.load(ERB.new(File.new(config_file).read).result)['test']
|
60
|
+
raise("Environment 'test' not defined in test/config/database.yml") unless cfg
|
61
|
+
|
62
|
+
User.establish_connection(cfg)
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'the Symmetric::Encryption Library' do
|
66
|
+
|
67
|
+
setup do
|
68
|
+
@bank_account_number = "1234567890"
|
69
|
+
@bank_account_number_encrypted = "QUxoUU8O/mi0o9ykgXNBFg==\n"
|
70
|
+
|
71
|
+
@social_security_number = "987654321"
|
72
|
+
@social_security_number_encrypted = "Jj7dKb3B0aUCnqH/YKGvKw==\n"
|
73
|
+
|
74
|
+
@user = User.new(
|
75
|
+
# Encrypted Attribute
|
76
|
+
:bank_account_number => @bank_account_number,
|
77
|
+
# Encrypted Attribute
|
78
|
+
:social_security_number => @social_security_number
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
should "have encrypted methods" do
|
83
|
+
assert_equal true, @user.respond_to?(:encrypted_bank_account_number)
|
84
|
+
assert_equal true, @user.respond_to?(:bank_account_number)
|
85
|
+
assert_equal true, @user.respond_to?(:encrypted_social_security_number)
|
86
|
+
assert_equal true, @user.respond_to?(:social_security_number)
|
87
|
+
assert_equal false, @user.respond_to?(:encrypted_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
should "have unencrypted values" do
|
91
|
+
assert_equal @bank_account_number, @user.bank_account_number
|
92
|
+
assert_equal @social_security_number, @user.social_security_number
|
93
|
+
end
|
94
|
+
|
95
|
+
should "have encrypted values" do
|
96
|
+
assert_equal @bank_account_number_encrypted, @user.encrypted_bank_account_number
|
97
|
+
assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
|
98
|
+
end
|
99
|
+
|
100
|
+
should "encrypt" do
|
101
|
+
user = User.new
|
102
|
+
user.bank_account_number = @bank_account_number
|
103
|
+
assert_equal @bank_account_number, user.bank_account_number
|
104
|
+
assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
|
105
|
+
end
|
106
|
+
|
107
|
+
should "allow lookups using unencrypted or encrypted column name" do
|
108
|
+
@user.save!
|
109
|
+
|
110
|
+
inq = User.find_by_bank_account_number(@bank_account_number)
|
111
|
+
assert_equal @bank_account_number, inq.bank_account_number
|
112
|
+
assert_equal @bank_account_number_encrypted, inq.encrypted_bank_account_number
|
113
|
+
|
114
|
+
@user.delete
|
115
|
+
end
|
116
|
+
|
117
|
+
should "return encrypted attributes for the class" do
|
118
|
+
expect = {:social_security_number=>:encrypted_social_security_number, :bank_account_number=>:encrypted_bank_account_number}
|
119
|
+
result = User.encrypted_attributes
|
120
|
+
expect.each_pair {|k,v| assert_equal expect[k], result[k]}
|
121
|
+
end
|
122
|
+
|
123
|
+
should "return encrypted keys for the class" do
|
124
|
+
expect = [:social_security_number, :bank_account_number]
|
125
|
+
result = User.encrypted_keys
|
126
|
+
expect.each {|val| assert_equal true, result.include?(val)}
|
127
|
+
|
128
|
+
# Also check encrypted_attribute?
|
129
|
+
expect.each {|val| assert_equal true, User.encrypted_attribute?(val)}
|
130
|
+
end
|
131
|
+
|
132
|
+
should "return encrypted columns for the class" do
|
133
|
+
expect = [:encrypted_social_security_number, :encrypted_bank_account_number]
|
134
|
+
result = User.encrypted_columns
|
135
|
+
expect.each {|val| assert_equal true, result.include?(val)}
|
136
|
+
|
137
|
+
# Also check encrypted_column?
|
138
|
+
expect.each {|val| assert_equal true, User.encrypted_column?(val)}
|
139
|
+
end
|
140
|
+
|
141
|
+
should "validate encrypted data" do
|
142
|
+
assert_equal true, @user.valid?
|
143
|
+
@user.encrypted_bank_account_number = '123'
|
144
|
+
assert_equal false, @user.valid?
|
145
|
+
assert_equal ["must be a value encrypted using Symmetric::Encryption.encrypt"], @user.errors[:encrypted_bank_account_number]
|
146
|
+
@user.encrypted_bank_account_number = Symmetric::Encryption.encrypt('123')
|
147
|
+
assert_equal true, @user.valid?
|
148
|
+
@user.bank_account_number = '123'
|
149
|
+
assert_equal true, @user.valid?
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#
|
2
|
+
# Test Config
|
3
|
+
#
|
4
|
+
---
|
5
|
+
test:
|
6
|
+
# Sample RSA Key, do not use this one as is, generate a new one
|
7
|
+
private_rsa_key: |
|
8
|
+
-----BEGIN RSA PRIVATE KEY-----
|
9
|
+
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
10
|
+
6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA
|
11
|
+
qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf
|
12
|
+
IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy
|
13
|
+
fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4
|
14
|
+
WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e
|
15
|
+
ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI
|
16
|
+
k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ
|
17
|
+
0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h
|
18
|
+
Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL
|
19
|
+
ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9
|
20
|
+
nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+
|
21
|
+
nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj
|
22
|
+
M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll
|
23
|
+
SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf
|
24
|
+
suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR
|
25
|
+
hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun
|
26
|
+
MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5
|
27
|
+
ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll
|
28
|
+
ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd
|
29
|
+
/XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi
|
30
|
+
VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4
|
31
|
+
h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn
|
32
|
+
PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz
|
33
|
+
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
34
|
+
-----END RSA PRIVATE KEY-----
|
35
|
+
|
36
|
+
# Filename containing Symmetric Encryption Key
|
37
|
+
# Note: The file contents must be RSA 2048 bit encrypted
|
38
|
+
# with the public key derived from the private key above
|
39
|
+
symmetric_key_filename: test/config/test.key
|
40
|
+
symmetric_iv_filename: test/config/test.iv
|
41
|
+
|
42
|
+
# Use aes-256-cbc encryption
|
43
|
+
cipher: aes-256-cbc
|
data/test/config/test.iv
ADDED
data/test/encryption_test.rb
CHANGED
@@ -4,99 +4,38 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'test/unit'
|
6
6
|
require 'shoulda'
|
7
|
-
require 'symmetric-encryption'
|
8
|
-
require 'yaml'
|
9
|
-
|
10
|
-
# #TODO Need to supply the model and migrations for this test
|
11
|
-
class User
|
12
|
-
attr_encrypted :bank_account_number
|
13
|
-
attr_encrypted :social_security_number
|
14
|
-
end
|
15
7
|
|
16
8
|
# Unit Test for Symmetric::Encryption
|
17
9
|
#
|
18
|
-
class EncryptionTest <
|
19
|
-
context '
|
10
|
+
class EncryptionTest < Test::Unit::TestCase
|
11
|
+
context 'initialized' do
|
20
12
|
|
21
13
|
setup do
|
22
|
-
|
23
|
-
@bank_account_number_encrypted = "V8Dg6zeeIpDg4+qrn2mjlA==\n"
|
24
|
-
|
25
|
-
@social_security_number = "987654321"
|
26
|
-
@social_security_number_encrypted = "Qd0qzN6oVuATJQBTf8X6tg==\n"
|
27
|
-
|
28
|
-
@user = User.new(
|
29
|
-
# Encrypted Attribute
|
30
|
-
:bank_account_number => @bank_account_number,
|
31
|
-
# Encrypted Attribute
|
32
|
-
:social_security_number => @social_security_number
|
33
|
-
)
|
34
|
-
end
|
35
|
-
|
36
|
-
should "encrypt simple string" do
|
37
|
-
assert_equal @social_security_number_encrypted, Symmetric::Encryption.encrypt(@social_security_number)
|
38
|
-
end
|
39
|
-
|
40
|
-
should "decrypt string" do
|
41
|
-
assert_equal @social_security_number, Symmetric::Encryption.decrypt(@social_security_number_encrypted)
|
42
|
-
end
|
43
|
-
|
44
|
-
should "have encrypted methods" do
|
45
|
-
assert_equal true, @user.respond_to?(:encrypted_bank_account_number)
|
46
|
-
assert_equal true, @user.respond_to?(:encrypted_social_security_number)
|
47
|
-
assert_equal false, @user.respond_to?(:encrypted_name)
|
48
|
-
end
|
49
|
-
|
50
|
-
should "have unencrypted values" do
|
51
|
-
assert_equal @bank_account_number, @user.bank_account_number
|
52
|
-
assert_equal @social_security_number, @user.social_security_number
|
14
|
+
Symmetric::Encryption.load!(File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml'), 'test')
|
53
15
|
end
|
54
16
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
should "encrypt" do
|
61
|
-
user = User.new
|
62
|
-
user.bank_account_number = @bank_account_number
|
63
|
-
assert_equal @bank_account_number, user.bank_account_number
|
64
|
-
assert_equal @bank_account_number_encrypted, user.encrypted_bank_account_number
|
65
|
-
end
|
66
|
-
|
67
|
-
should "allow lookups using unencrypted or encrypted column name" do
|
68
|
-
user_id = @user.save!
|
69
|
-
|
70
|
-
inq = User.find_by_bank_account_number(@bank_account_number)
|
71
|
-
assert_equal @bank_account_number, inq.bank_account_number
|
72
|
-
assert_equal @bank_account_number_encrypted, inq.encrypted_bank_account_number
|
73
|
-
|
74
|
-
User.delete(user_id)
|
75
|
-
end
|
76
|
-
|
77
|
-
should "return encrypted attributes for the class" do
|
78
|
-
expect = {:social_security_number=>:encrypted_social_security_number, :bank_account_number=>:encrypted_bank_account_number, :check_bank_account_number=>:encrypted_check_bank_account_number}
|
79
|
-
result = User.encrypted_attributes
|
80
|
-
expect.each_pair {|k,v| assert_equal expect[k], result[k]}
|
81
|
-
end
|
17
|
+
context 'Symmetric::Encryption tests' do
|
18
|
+
setup do
|
19
|
+
@bank_account_number = "1234567890"
|
20
|
+
@bank_account_number_encrypted = "QUxoUU8O/mi0o9ykgXNBFg==\n"
|
82
21
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
expect.each {|val| assert_equal true, result.include?(val)}
|
22
|
+
@social_security_number = "987654321"
|
23
|
+
@social_security_number_encrypted = "Jj7dKb3B0aUCnqH/YKGvKw==\n"
|
24
|
+
end
|
87
25
|
|
88
|
-
|
89
|
-
|
90
|
-
|
26
|
+
should "encrypt simple string" do
|
27
|
+
assert_equal @social_security_number_encrypted, Symmetric::Encryption.encrypt(@social_security_number)
|
28
|
+
end
|
91
29
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
expect.each {|val| assert_equal true, result.include?(val)}
|
30
|
+
should "decrypt string" do
|
31
|
+
assert_equal @social_security_number, Symmetric::Encryption.decrypt(@social_security_number_encrypted)
|
32
|
+
end
|
96
33
|
|
97
|
-
|
98
|
-
|
34
|
+
should "determine if string is encrypted" do
|
35
|
+
assert_equal true, Symmetric::Encryption.encrypted?(@social_security_number_encrypted)
|
36
|
+
assert_equal false, Symmetric::Encryption.encrypted?(@social_security_number)
|
37
|
+
end
|
99
38
|
end
|
100
|
-
|
101
39
|
end
|
40
|
+
|
102
41
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
- 1
|
8
7
|
- 2
|
9
|
-
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Reid Morrison
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2012-
|
17
|
+
date: 2012-03-14 00:00:00 -04:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -28,9 +28,11 @@ extensions: []
|
|
28
28
|
extra_rdoc_files: []
|
29
29
|
|
30
30
|
files:
|
31
|
+
- examples/symmetric-encryption.yml
|
31
32
|
- lib/symmetric/encryption.rb
|
32
33
|
- lib/symmetric/extensions/active_record/base.rb
|
33
34
|
- lib/symmetric/railtie.rb
|
35
|
+
- lib/symmetric/railties/symmetric_encrypted_validator.rb
|
34
36
|
- lib/symmetric/railties/symmetric_encryption.rake
|
35
37
|
- lib/symmetric/version.rb
|
36
38
|
- lib/symmetric-encryption.rb
|
@@ -43,8 +45,12 @@ files:
|
|
43
45
|
- nbproject/project.xml
|
44
46
|
- Rakefile
|
45
47
|
- README.md
|
46
|
-
- symmetric-encryption-0.1.
|
47
|
-
-
|
48
|
+
- symmetric-encryption-0.1.2.gem
|
49
|
+
- test/attr_encrypted_test.rb
|
50
|
+
- test/config/database.yml
|
51
|
+
- test/config/symmetric-encryption.yml
|
52
|
+
- test/config/test.iv
|
53
|
+
- test/config/test.key
|
48
54
|
- test/encryption_test.rb
|
49
55
|
has_rdoc: true
|
50
56
|
homepage: https://github.com/ClarityServices/symmetric-encryption
|
Binary file
|
Binary file
|