symmetric-encryption 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +201 -54
- data/examples/symmetric-encryption.yml +64 -19
- data/lib/symmetric/cipher.rb +184 -0
- data/lib/symmetric/encryption.rb +197 -182
- data/lib/symmetric/version.rb +1 -1
- data/lib/symmetric-encryption.rb +1 -0
- data/test/attr_encrypted_test.rb +6 -18
- data/test/cipher_test.rb +71 -0
- data/test/config/symmetric-encryption.yml +13 -9
- data/test/config/{test.iv → test_new.iv} +0 -0
- data/test/config/{test.key → test_new.key} +0 -0
- data/test/config/test_secondary_1.iv +1 -0
- data/test/config/test_secondary_1.key +2 -0
- data/test/encryption_test.rb +16 -6
- metadata +9 -6
- data/symmetric-encryption-0.1.2.gem +0 -0
data/README.md
CHANGED
@@ -133,44 +133,101 @@ Create a configuration file in config/symmetric-encryption.yml per the following
|
|
133
133
|
# Symmetric Encryption for Ruby
|
134
134
|
#
|
135
135
|
---
|
136
|
-
#
|
137
|
-
#
|
136
|
+
# For the development and test environments the test symmetric encryption keys
|
137
|
+
# can be placed directly in the source code.
|
138
|
+
# And therefore no RSA private key is required
|
138
139
|
development: &development_defaults
|
139
|
-
cipher: aes-256-cbc
|
140
140
|
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
141
141
|
symmetric_iv: 1234567890ABCDEF
|
142
|
+
encoding: base64
|
143
|
+
cipher: aes-128-cbc
|
142
144
|
|
143
145
|
test:
|
144
146
|
<<: *development_defaults
|
145
147
|
|
146
|
-
|
148
|
+
production:
|
147
149
|
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
148
150
|
# source code, we only hold a RSA key that is used to unlock the file
|
149
151
|
# containing the actual symmetric encryption key
|
150
152
|
#
|
151
|
-
#
|
153
|
+
# Sample RSA Key, DO NOT use this RSA key, generate a new one using
|
152
154
|
# openssl genrsa 2048
|
153
155
|
private_rsa_key: |
|
154
156
|
-----BEGIN RSA PRIVATE KEY-----
|
155
|
-
|
156
|
-
|
157
|
-
|
157
|
+
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
158
|
+
6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA
|
159
|
+
qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf
|
160
|
+
IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy
|
161
|
+
fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4
|
162
|
+
WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e
|
163
|
+
ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI
|
164
|
+
k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ
|
165
|
+
0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h
|
166
|
+
Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL
|
167
|
+
ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9
|
168
|
+
nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+
|
169
|
+
nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj
|
170
|
+
M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll
|
171
|
+
SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf
|
172
|
+
suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR
|
173
|
+
hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun
|
174
|
+
MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5
|
175
|
+
ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll
|
176
|
+
ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd
|
177
|
+
/XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi
|
178
|
+
VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4
|
179
|
+
h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn
|
180
|
+
PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz
|
181
|
+
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
158
182
|
-----END RSA PRIVATE KEY-----
|
159
183
|
|
160
|
-
#
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
184
|
+
# List Symmetric Key files in the order of current / latest first
|
185
|
+
files:
|
186
|
+
-
|
187
|
+
# Filename containing Symmetric Encryption Key encrypted using the
|
188
|
+
# RSA public key derived from the private key above
|
189
|
+
symmetric_key_filename: /etc/rails/.rails.key
|
190
|
+
symmetric_iv_filename: /etc/rails/.rails.iv
|
191
|
+
|
192
|
+
# By adding a version indicator all encrypted data will include
|
193
|
+
# an additional first Byte that includes this version number to
|
194
|
+
# assist with speeding up decryption when adding new encryption keys
|
195
|
+
# and to support old data decryption using older keys
|
196
|
+
#
|
197
|
+
# By not specifying a version, or setting it to 0 will disable version
|
198
|
+
# identification prior to decrypting data
|
199
|
+
# During decryption these Keys will be tried in the order listed in the
|
200
|
+
# configuration file starting with the first in the list
|
201
|
+
# Slower since a decryption attempt is made for every key until the
|
202
|
+
# correct key is located. However, all encrypted data does not require
|
203
|
+
# the 1 Byte version header prefix
|
204
|
+
#
|
205
|
+
# Default: 0
|
206
|
+
version: 0
|
207
|
+
|
208
|
+
# Set the way the encrypted data is encoded:
|
209
|
+
# base64
|
210
|
+
# Encrypted data is returned in base64 encoding format
|
211
|
+
# Symmetric::Encryption.decrypt will also base64 decode any data prior
|
212
|
+
# to decrypting it
|
213
|
+
# binary
|
214
|
+
# Encrypted data is returned as raw binary
|
215
|
+
# Although smaller than base64 it cannot be stored in MySQL text columns
|
216
|
+
# It can only be held in binary columns such as BINARY or BLOB
|
217
|
+
# Default: base64
|
218
|
+
encoding: base64
|
219
|
+
|
220
|
+
# Encryption cipher
|
221
|
+
# Recommended values:
|
222
|
+
# aes-256-cbc
|
223
|
+
# 256 AES CBC Algorithm. Very strong
|
224
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
225
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
226
|
+
# aes-128-cbc
|
227
|
+
# 128 AES CBC Algorithm. Less strong.
|
228
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
229
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
230
|
+
cipher: aes-256-cbc
|
174
231
|
|
175
232
|
This configuration file should be checked into the source code control system.
|
176
233
|
It does Not include the Symmetric Encryption keys. They will be generated in the
|
@@ -211,6 +268,7 @@ attempting to encrypt or decrypt any data
|
|
211
268
|
Symmetric::Encryption.load!('config/symmetric-encryption.yml', 'production')
|
212
269
|
|
213
270
|
Parameters:
|
271
|
+
|
214
272
|
* Filename of the configuration file created above
|
215
273
|
* Name of the environment to load the configuration for
|
216
274
|
|
@@ -220,16 +278,17 @@ To manually generate the symmetric encryption keys, run the code below
|
|
220
278
|
Symmetric::Encryption.generate_symmetric_key_files('config/symmetric-encryption.yml', 'production')
|
221
279
|
|
222
280
|
Parameters:
|
281
|
+
|
223
282
|
* Filename of the configuration file created above
|
224
283
|
* Name of the environment to load the configuration for
|
225
284
|
|
226
285
|
## Supporting Multiple Encryption Keys
|
227
286
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
automatically.
|
287
|
+
According to the PCI Compliace documentation: "Cryptographic keys must be changed on an annual basis."
|
288
|
+
|
289
|
+
During the transition period of moving from one encryption key to another
|
290
|
+
symmetric-encryption supports multiple Symmetric Encryption keys. If decryption
|
291
|
+
with the current key fails, any previous keys will also be tried automatically.
|
233
292
|
|
234
293
|
By default the latest key is used for encrypting data. Another key can be specified
|
235
294
|
for encryption so that old data can be looked in queries, etc.
|
@@ -239,61 +298,149 @@ use the same RSA Private key for gaining access to the Symmetric Encryption Keys
|
|
239
298
|
|
240
299
|
### Configuring multiple Symmetric Encryption keys
|
241
300
|
|
242
|
-
|
243
|
-
|
244
301
|
Create a configuration file in config/symmetric-encryption.yml per the following example:
|
245
302
|
|
246
303
|
#
|
247
304
|
# Symmetric Encryption for Ruby
|
248
305
|
#
|
249
306
|
---
|
250
|
-
#
|
251
|
-
#
|
307
|
+
# For the development and test environments the test symmetric encryption keys
|
308
|
+
# can be placed directly in the source code.
|
309
|
+
# And therefore no RSA private key is required
|
252
310
|
development: &development_defaults
|
253
|
-
cipher: aes-256-cbc
|
254
311
|
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
255
312
|
symmetric_iv: 1234567890ABCDEF
|
313
|
+
encoding: base64
|
314
|
+
cipher: aes-128-cbc
|
256
315
|
|
257
316
|
test:
|
258
317
|
<<: *development_defaults
|
259
318
|
|
260
|
-
|
319
|
+
production:
|
261
320
|
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
262
321
|
# source code, we only hold a RSA key that is used to unlock the file
|
263
322
|
# containing the actual symmetric encryption key
|
264
323
|
#
|
265
|
-
#
|
324
|
+
# Sample RSA Key, DO NOT use this RSA key, generate a new one using
|
266
325
|
# openssl genrsa 2048
|
267
326
|
private_rsa_key: |
|
268
327
|
-----BEGIN RSA PRIVATE KEY-----
|
269
|
-
|
270
|
-
|
271
|
-
|
328
|
+
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
329
|
+
6DcuFTFcNSSSxG9n4y7tKi755be8N0uwCCuOzvXqfWmXYjbLwK3Ib2vm0btpHyvA
|
330
|
+
qxgqeJOOCxKdW/cUFLWn0tACUcEjVCNfWEGaFyvkOUuR7Ub9KfhbW9cZO3BxZMUf
|
331
|
+
IPGlHl/gWyf484sXygd+S7cpDTRRzo9RjG74DwfE0MFGf9a1fTkxnSgeOJ6asTOy
|
332
|
+
fp9tEToUlbglKaYGpOGHYQ9TV5ZsyJ9jRUyb4SP5wK2eK6dHTxTcHvT03kD90Hv4
|
333
|
+
WeKIXv3WOjkwNEyMdpnJJfSDb5oquQvCNi7ZSQIDAQABAoIBAQCbzR7TUoBugU+e
|
334
|
+
ICLvpC2wOYOh9kRoFLwlyv3QnH7WZFWRZzFJszYeJ1xr5etXQtyjCnmOkGAg+WOI
|
335
|
+
k8GlOKOpAuA/PpB/leJFiYL4lBwU/PmDdTT0cdx6bMKZlNCeMW8CXGQKiFDOcMqJ
|
336
|
+
0uGtH5YD+RChPIEeFsJxnC8SyZ9/t2ra7XnMGiCZvRXIUDSEIIsRx/mOymJ7bL+h
|
337
|
+
Lbp46IfXf6ZuIzwzoIk0JReV/r+wdmkAVDkrrMkCmVS4/X1wN/Tiik9/yvbsh/CL
|
338
|
+
ztC55eSIEjATkWxnXfPASZN6oUfQPEveGH3HzNjdncjH/Ho8FaNMIAfFpBhhLPi9
|
339
|
+
nG5sbH+BAoGBAOdoUyVoAA/QUa3/FkQaa7Ajjehe5MR5k6VtaGtcxrLiBjrNR7x+
|
340
|
+
nqlZlGvWDMiCz49dgj+G1Qk1bbYrZLRX/Hjeqy5dZOGLMfgf9eKUmS1rDwAzBMcj
|
341
|
+
M9jnnJEBx8HIlNzaR6wzp3GMd0rrccs660A8URvzkgo9qNbvMLq9vyUtAoGBANll
|
342
|
+
SY1Iv9uaIz8klTXU9YzYtsfUmgXzw7K8StPdbEbo8F1J3JPJB4D7QHF0ObIaSWuf
|
343
|
+
suZqLsvWlYGuJeyX2ntlBN82ORfvUdOrdrbDlmPyj4PfFVl0AK3U3Ai374DNrjKR
|
344
|
+
hF6YFm4TLDaJhUjeV5C43kbE1N2FAMS9LYtPJ44NAoGAFDGHZ/E+aCLerddfwwun
|
345
|
+
MBS6MnftcLPHTZ1RimTrNfsBXipBw1ItWEvn5s0kCm9X24PmdNK4TnhqHYaF4DL5
|
346
|
+
ZjbQK1idEA2Mi8GGPIKJJ2x7P6I0HYiV4qy7fe/w1ZlCXE90B7PuPbtrQY9wO7Ll
|
347
|
+
ipJ45X6I1PnyfOcckn8yafUCgYACtPAlgjJhWZn2v03cTbqA9nHQKyV/zXkyUIXd
|
348
|
+
/XPLrjrP7ouAi5A8WuSChR/yx8ECRgrEM65Be3qBEtoGCB4AS1G0NcigM6qhKBFi
|
349
|
+
VS0aMXr3+V8argcUIwJaWW/x+p2go48yXlJpLHPweeXe8mXEt4iM+QZte6p2yKQ4
|
350
|
+
h9PGQQKBgQCqSydmXBnXGIVTp2sH/2GnpxLYnDBpcJE0tM8bJ42HEQQgRThIChsn
|
351
|
+
PnGA91G9MVikYapgI0VYBHQOTsz8rTIUzsKwXG+TIaK+W84nxH5y6jUkjqwxZmAz
|
352
|
+
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
272
353
|
-----END RSA PRIVATE KEY-----
|
273
354
|
|
274
|
-
#
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
355
|
+
# List Symmetric Key files in the order of current / latest first
|
356
|
+
files:
|
357
|
+
-
|
358
|
+
# Filename containing Symmetric Encryption Key encrypted using the
|
359
|
+
# RSA public key derived from the private key above
|
360
|
+
symmetric_key_filename: /etc/rails/.rails.key
|
361
|
+
symmetric_iv_filename: /etc/rails/.rails.iv
|
362
|
+
|
363
|
+
# By adding a version indicator all encrypted data will include
|
364
|
+
# an additional first Byte that includes this version number to
|
365
|
+
# assist with speeding up decryption when adding new encryption keys
|
366
|
+
# and to support old data decryption using older keys
|
367
|
+
#
|
368
|
+
# By not specifying a version, or setting it to 0 will disable version
|
369
|
+
# identification prior to decrypting data
|
370
|
+
# During decryption these Keys will be tried in the order listed in the
|
371
|
+
# configuration file starting with the first in the list
|
372
|
+
# Slower since a decryption attempt is made for every key until the
|
373
|
+
# correct key is located. However, all encrypted data does not require
|
374
|
+
# the 1 Byte version header prefix
|
375
|
+
#
|
376
|
+
# Default: 0
|
377
|
+
version: 0
|
378
|
+
|
379
|
+
# Set the way the encrypted data is encoded:
|
380
|
+
# base64
|
381
|
+
# Encrypted data is returned in base64 encoding format
|
382
|
+
# Symmetric::Encryption.decrypt will also base64 decode any data prior
|
383
|
+
# to decrypting it
|
384
|
+
# binary
|
385
|
+
# Encrypted data is returned as raw binary
|
386
|
+
# Although smaller than base64 it cannot be stored in MySQL text columns
|
387
|
+
# It can only be held in binary columns such as BINARY or BLOB
|
388
|
+
# Default: base64
|
389
|
+
encoding: base64
|
390
|
+
|
391
|
+
# Encryption cipher
|
392
|
+
# Recommended values:
|
393
|
+
# aes-256-cbc
|
394
|
+
# 256 AES CBC Algorithm. Very strong
|
395
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
396
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
397
|
+
# aes-128-cbc
|
398
|
+
# 128 AES CBC Algorithm. Less strong.
|
399
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
400
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
401
|
+
cipher: aes-256-cbc
|
402
|
+
|
403
|
+
-
|
404
|
+
# OPTIONAL:
|
405
|
+
#
|
406
|
+
# Any previous Symmetric Encryption Keys
|
407
|
+
#
|
408
|
+
# Only used when old data still exists that requires old decryption keys
|
409
|
+
# to be used
|
410
|
+
symmetric_key_filename: /etc/rails/.rails_old.key
|
411
|
+
symmetric_iv_filename: /etc/rails/.rails_old.iv
|
412
|
+
version: 0
|
413
|
+
encoding: base64
|
414
|
+
cipher: aes-256-cbc
|
415
|
+
|
416
|
+
## Possible Future Enhancements
|
417
|
+
|
418
|
+
Submit an issue ticket to request any of the following features:
|
419
|
+
|
420
|
+
* Ability to entirely disable encryption for a specific environment.
|
421
|
+
Symmetric::Encryption.encrypt() would return the supplied data without encrypting it and
|
422
|
+
Symmetric::Encryption.decrypt() would return the supplied data without decrypting it
|
423
|
+
|
424
|
+
* Support for automatically compressing data prior to encrypting it when the
|
425
|
+
data exceeds some predefined size. And automatically decompressing the data
|
426
|
+
during decryption
|
427
|
+
|
428
|
+
* Make attr_encrypted auto-detect the encrypted column type and Base64 encode
|
429
|
+
when type is CHAR and store as binary when type is BINARY or BLOB
|
430
|
+
|
431
|
+
* Create rake task / generator to generate a sample configuration file
|
432
|
+
with a new RSA Private key already in it
|
433
|
+
|
434
|
+
* Ability to change Symmetric::Encryption configuration options from custom
|
435
|
+
Rails initializers, rather than having everything in the config file.
|
436
|
+
For example config.symmetric_encryption.cipher = 'aes-128-cbc'
|
290
437
|
|
291
438
|
Meta
|
292
439
|
----
|
293
440
|
|
294
441
|
* Code: `git clone git://github.com/ClarityServices/symmetric-encryption.git`
|
295
442
|
* Home: <https://github.com/ClarityServices/symmetric-encryption>
|
296
|
-
*
|
443
|
+
* Issues: <http://github.com/ClarityServices/symmetric-encryption/issues>
|
297
444
|
* Gems: <http://rubygems.org/gems/symmetric-encryption>
|
298
445
|
|
299
446
|
This project uses [Semantic Versioning](http://semver.org/).
|
@@ -2,25 +2,24 @@
|
|
2
2
|
# Symmetric Encryption for Ruby
|
3
3
|
#
|
4
4
|
---
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# For the development and test environments the test symmetric encryption keys
|
6
|
+
# can be placed directly in the source code.
|
7
|
+
# And therefore no RSA private key is required
|
7
8
|
development: &development_defaults
|
8
|
-
cipher: aes-256-cbc
|
9
9
|
symmetric_key: 1234567890ABCDEF1234567890ABCDEF
|
10
|
-
symmetric_iv:
|
10
|
+
symmetric_iv: 1234567890ABCDEF
|
11
|
+
cipher: aes-128-cbc
|
11
12
|
|
12
13
|
test:
|
13
14
|
<<: *development_defaults
|
14
15
|
|
15
|
-
|
16
|
+
production:
|
16
17
|
# Since the key to encrypt and decrypt with must NOT be stored along with the
|
17
18
|
# source code, we only hold a RSA key that is used to unlock the file
|
18
19
|
# containing the actual symmetric encryption key
|
19
20
|
#
|
20
|
-
#
|
21
|
+
# Sample RSA Key, DO NOT use this RSA key, generate a new one using
|
21
22
|
# openssl genrsa 2048
|
22
|
-
|
23
|
-
# Sample RSA Key, do not use this one as is, generate a new one
|
24
23
|
private_rsa_key: |
|
25
24
|
-----BEGIN RSA PRIVATE KEY-----
|
26
25
|
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
@@ -50,17 +49,63 @@ release: &release_defaults
|
|
50
49
|
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
51
50
|
-----END RSA PRIVATE KEY-----
|
52
51
|
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
# List Symmetric Key Ciphers in the order of current / latest first
|
53
|
+
ciphers:
|
54
|
+
# Filename containing Symmetric Encryption Key encrypted using the
|
55
|
+
# RSA 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
58
|
|
59
|
-
|
60
|
-
|
59
|
+
# Encryption cipher
|
60
|
+
# Recommended values:
|
61
|
+
# aes-256-cbc
|
62
|
+
# 256 AES CBC Algorithm. Very strong
|
63
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
64
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
65
|
+
# aes-128-cbc
|
66
|
+
# 128 AES CBC Algorithm. Less strong.
|
67
|
+
# Ruby 1.8.7 MRI Approximately 100,000 encryptions or decryptions per second
|
68
|
+
# JRuby 1.6.7 with Ruby 1.8.7 Approximately 22,000 encryptions or decryptions per second
|
69
|
+
cipher: aes-256-cbc
|
61
70
|
|
62
|
-
|
63
|
-
|
71
|
+
# FUTURE ENHANCEMENT:
|
72
|
+
#
|
73
|
+
# By adding a version indicator all encrypted data will include
|
74
|
+
# an additional first Byte that includes this version number to
|
75
|
+
# assist with speeding up decryption when adding new encryption keys
|
76
|
+
# and to support old data decryption using older keys
|
77
|
+
#
|
78
|
+
# By not specifying a version, or setting it to 0 will disable version
|
79
|
+
# identification prior to decrypting data
|
80
|
+
# During decryption these Keys will be tried in the order listed in the
|
81
|
+
# configuration file starting with the first in the list
|
82
|
+
# Slower since a decryption attempt is made for every key until the
|
83
|
+
# correct key is located. However, all encrypted data does not require
|
84
|
+
# the 1 Byte version header prefix
|
85
|
+
#
|
86
|
+
# Default: 0
|
87
|
+
#version: 0
|
64
88
|
|
65
|
-
|
66
|
-
|
89
|
+
# FUTURE ENHANCEMENT:
|
90
|
+
#
|
91
|
+
# Set the way the encrypted data is encoded:
|
92
|
+
# base64
|
93
|
+
# Encrypted data is returned in base64 encoding format
|
94
|
+
# Symmetric::Encryption.decrypt will also base64 decode any data prior
|
95
|
+
# to decrypting it
|
96
|
+
# binary
|
97
|
+
# Encrypted data is returned as raw binary
|
98
|
+
# Although smaller than base64 it cannot be stored in MySQL text columns
|
99
|
+
# It can only be held in binary columns such as BINARY or BLOB
|
100
|
+
# Default: base64
|
101
|
+
#encoding: base64
|
102
|
+
|
103
|
+
# OPTIONAL:
|
104
|
+
#
|
105
|
+
# Any previous Symmetric Encryption Keys
|
106
|
+
#
|
107
|
+
# Only used when old data still exists that requires old decryption keys
|
108
|
+
# to be used
|
109
|
+
- symmetric_key_filename: /etc/rails/.rails_old.key
|
110
|
+
symmetric_iv_filename: /etc/rails/.rails_old.iv
|
111
|
+
cipher: aes-256-cbc
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'zlib'
|
4
|
+
|
5
|
+
module Symmetric
|
6
|
+
|
7
|
+
# Hold all information related to encryption keys
|
8
|
+
# as well as encrypt and decrypt data using those keys
|
9
|
+
#
|
10
|
+
# Cipher is thread safe so that the same instance can be called by multiple
|
11
|
+
# threads at the same time without needing an instance of Cipher per thread
|
12
|
+
class Cipher
|
13
|
+
# Cipher to use for encryption and decryption
|
14
|
+
attr_reader :cipher
|
15
|
+
|
16
|
+
# Future Use:
|
17
|
+
# attr_accessor :encoding, :version
|
18
|
+
|
19
|
+
# Generate a new Symmetric Key pair
|
20
|
+
#
|
21
|
+
# Returns a hash containing a new random symmetric_key pair
|
22
|
+
# consisting of a :key and :iv.
|
23
|
+
# The cipher is also included for compatibility with the Cipher initializer
|
24
|
+
def self.random_key_pair(cipher = 'aes-256-cbc', generate_iv = true)
|
25
|
+
openssl_cipher = OpenSSL::Cipher.new(cipher)
|
26
|
+
openssl_cipher.encrypt
|
27
|
+
|
28
|
+
{
|
29
|
+
:key => openssl_cipher.random_key,
|
30
|
+
:iv => generate_iv ? openssl_cipher.random_iv : nil,
|
31
|
+
:cipher => cipher
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# Create a Symmetric::Key for encryption and decryption purposes
|
36
|
+
#
|
37
|
+
# Parameters:
|
38
|
+
# :key
|
39
|
+
# The Symmetric Key to use for encryption and decryption
|
40
|
+
# :iv
|
41
|
+
# Optional. The Initialization Vector to use with Symmetric Key
|
42
|
+
# :cipher
|
43
|
+
# Optional. Encryption Cipher to use
|
44
|
+
# Default: aes-256-cbc
|
45
|
+
def initialize(parms={})
|
46
|
+
raise "Missing mandatory parameter :key" unless @key = parms[:key]
|
47
|
+
@iv = parms[:iv]
|
48
|
+
@cipher = parms[:cipher] || 'aes-256-cbc'
|
49
|
+
end
|
50
|
+
|
51
|
+
# AES Symmetric Encryption of supplied string
|
52
|
+
# Returns result as a Base64 encoded string
|
53
|
+
# Returns nil if the supplied str is nil
|
54
|
+
# Returns "" if it is a string and it is empty
|
55
|
+
def encrypt(str)
|
56
|
+
return str if str.nil? || (str.is_a?(String) && str.empty?)
|
57
|
+
::Base64.encode64(crypt(:encrypt, str))
|
58
|
+
end
|
59
|
+
|
60
|
+
# AES Symmetric Decryption of supplied string
|
61
|
+
# Returns decrypted string
|
62
|
+
# Returns nil if the supplied str is nil
|
63
|
+
# Returns "" if it is a string and it is empty
|
64
|
+
def decrypt(str)
|
65
|
+
return str if str.nil? || (str.is_a?(String) && str.empty?)
|
66
|
+
crypt(:decrypt, ::Base64.decode64(str))
|
67
|
+
end
|
68
|
+
|
69
|
+
# The minimum length for an encrypted string
|
70
|
+
def min_encrypted_length
|
71
|
+
@min_encrypted_length ||= encrypt('1').length
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns [true|false] a best effort determination as to whether the supplied
|
75
|
+
# string is encrypted or not, without incurring the penalty of actually
|
76
|
+
# decrypting the supplied data
|
77
|
+
# Parameters:
|
78
|
+
# encrypted_data: Encrypted string
|
79
|
+
def encrypted?(encrypted_data)
|
80
|
+
# Simple checks first
|
81
|
+
return false if (encrypted_data.length < min_encrypted_length) || (!encrypted_data.end_with?("\n"))
|
82
|
+
# For now have to decrypt it fully
|
83
|
+
begin
|
84
|
+
decrypt(encrypted_data) ? true : false
|
85
|
+
rescue
|
86
|
+
false
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return a new random key using the configured cipher
|
91
|
+
# Useful for generating new symmetric keys
|
92
|
+
def random_key
|
93
|
+
::OpenSSL::Cipher::Cipher.new(@cipher).random_key
|
94
|
+
end
|
95
|
+
|
96
|
+
protected
|
97
|
+
|
98
|
+
# Some of these methods are for future use to handle binary data, etc..
|
99
|
+
|
100
|
+
# Binary encrypted data includes this magic header so that we can quickly
|
101
|
+
# identify binary data versus base64 encoded data that does not have this header
|
102
|
+
unless defined? MAGIC_HEADER
|
103
|
+
MAGIC_HEADER = '@EnC'
|
104
|
+
MAGIC_HEADER_SIZE = MAGIC_HEADER.size
|
105
|
+
end
|
106
|
+
|
107
|
+
# AES Symmetric Encryption of supplied string
|
108
|
+
# Returns result as a binary encrypted string
|
109
|
+
# Returns nil if the supplied str is nil or empty
|
110
|
+
# Parameters
|
111
|
+
# compress => Whether to compress the supplied string using zip before
|
112
|
+
# encrypting
|
113
|
+
# true | false
|
114
|
+
# Default false
|
115
|
+
def self.encrypt_binary(str, compress=false)
|
116
|
+
return nil if str.nil? || (str.is_a?(String) && str.empty?)
|
117
|
+
# Bit Layout
|
118
|
+
# 15 => Compressed?
|
119
|
+
# 0..14 => Version number of encryption key/algorithm currently 0
|
120
|
+
flags = 0 # Same as 0b0000_0000_0000_0000
|
121
|
+
# If the data is to be compressed before being encrypted, set the flag and
|
122
|
+
# compress using zlib. Only compress if data is greater than 15 chars
|
123
|
+
str = str.to_s unless str.is_a?(String)
|
124
|
+
if compress && str.length > 15
|
125
|
+
flags |= 0b1000_0000_0000_0000
|
126
|
+
begin
|
127
|
+
ostream = StringIO.new
|
128
|
+
gz = ::Zlib::GzipWriter.new(ostream)
|
129
|
+
gz.write(str)
|
130
|
+
str = ostream.string
|
131
|
+
ensure
|
132
|
+
gz.close
|
133
|
+
end
|
134
|
+
end
|
135
|
+
return nil unless encrypted = self.crypt(:encrypt, str)
|
136
|
+
# Resulting buffer consists of:
|
137
|
+
# '@EnC'
|
138
|
+
# unsigned short (32 bits) in little endian format for flags above
|
139
|
+
# 'actual encrypted buffer data'
|
140
|
+
"#{MAGIC_HEADER}#{[flags].pack('v')}#{encrypted}"
|
141
|
+
end
|
142
|
+
|
143
|
+
# AES Symmetric Decryption of supplied Binary string
|
144
|
+
# Returns decrypted string
|
145
|
+
# Returns nil if the supplied str is nil
|
146
|
+
# Returns "" if it is a string and it is empty
|
147
|
+
def self.decrypt_binary(str)
|
148
|
+
return str if str.nil? || (str.is_a?(String) && str.empty?)
|
149
|
+
str = str.to_s unless str.is_a?(String)
|
150
|
+
encrypted = if str.starts_with? MAGIC_HEADER
|
151
|
+
# Remove header and extract flags
|
152
|
+
header, flags = str.unpack(@@unpack ||= "A#{MAGIC_HEADER_SIZE}v")
|
153
|
+
# Uncompress if data is compressed and remove header
|
154
|
+
if flags & 0b1000_0000_0000_0000
|
155
|
+
begin
|
156
|
+
gz = ::Zlib::GzipReader.new(StringIO.new(str[MAGIC_HEADER_SIZE,-1]))
|
157
|
+
gz.read
|
158
|
+
ensure
|
159
|
+
gz.close
|
160
|
+
end
|
161
|
+
else
|
162
|
+
str[MAGIC_HEADER_SIZE,-1]
|
163
|
+
end
|
164
|
+
else
|
165
|
+
::Base64.decode64(str)
|
166
|
+
end
|
167
|
+
crypt(:decrypt, encrypted)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Creates a new OpenSSL::Cipher with every call so that this call
|
171
|
+
# is thread-safe
|
172
|
+
def crypt(cipher_method, string) #:nodoc:
|
173
|
+
openssl_cipher = ::OpenSSL::Cipher.new(self.cipher)
|
174
|
+
openssl_cipher.send(cipher_method)
|
175
|
+
raise "Encryption.key must be set before calling Encryption encrypt or decrypt" unless @key
|
176
|
+
openssl_cipher.key = @key
|
177
|
+
openssl_cipher.iv = @iv if @iv
|
178
|
+
result = openssl_cipher.update(string.to_s)
|
179
|
+
result << openssl_cipher.final
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|