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 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
- # 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
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
- release: &release_defaults
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
- # To generate a new RSA private key:
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
- paste RSA key generated above here
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
- # 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
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
- 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.
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
- # 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
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
- release: &release_defaults
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
- # To generate a new RSA private key:
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
- paste RSA key generated above here
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
- # 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
-
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
- * Bugs: <http://github.com/ClarityServices/symmetric-encryption/issues>
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
- # 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
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: 1234567890ABCDEF
10
+ symmetric_iv: 1234567890ABCDEF
11
+ cipher: aes-128-cbc
11
12
 
12
13
  test:
13
14
  <<: *development_defaults
14
15
 
15
- release: &release_defaults
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
- # To generate a new RSA private key:
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
- # 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
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
- # Use aes-256-cbc encryption
60
- cipher: aes-256-cbc
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
- hotfix:
63
- <<: *release_defaults
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
- production:
66
- <<: *release_defaults
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