symmetric-encryption 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|