Module: SymmetricEncryption
- Defined in:
- lib/symmetric_encryption/railtie.rb,
lib/symmetric_encryption.rb,
lib/symmetric_encryption/cipher.rb,
lib/symmetric_encryption/reader.rb,
lib/symmetric_encryption/writer.rb,
lib/symmetric_encryption/version.rb,
lib/symmetric_encryption/symmetric_encryption.rb,
lib/rails/generators/symmetric_encryption/config/config_generator.rb,
lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb
Overview
Encrypt using 256 Bit AES CBC symmetric key and initialization vector The symmetric key is protected using the private key below and must be distributed separately from the application
Defined Under Namespace
Modules: Generators Classes: Cipher, Railtie, Reader, Writer
Constant Summary collapse
- VERSION =
"2.2.0"
- MAGIC_HEADER =
'@EnC'
- MAGIC_HEADER_SIZE =
MAGIC_HEADER.size
- MAGIC_HEADER_UNPACK =
"a#{MAGIC_HEADER_SIZE}v"
- @@cipher =
Defaults
nil
- @@secondary_ciphers =
[]
Class Method Summary collapse
-
.cipher(version = nil) ⇒ Object
Returns the Primary Symmetric Cipher being used If a version is supplied Returns the primary cipher if no match was found and version == 0 Returns nil if no match was found and version != 0.
-
.cipher=(cipher) ⇒ Object
Set the Primary Symmetric Cipher to be used.
-
.decrypt(str) ⇒ Object
AES Symmetric Decryption of supplied string Returns decrypted string Returns nil if the supplied str is nil Returns “” if it is a string and it is empty.
-
.encrypt(str, random_iv = false, compress = false) ⇒ Object
AES Symmetric Encryption of supplied string Returns result as a Base64 encoded string Returns nil if the supplied str is nil Returns “” if it is a string and it is empty.
-
.encrypted?(encrypted_data) ⇒ Boolean
Returns [true|false] as to whether the data could be decrypted Parameters: encrypted_data: Encrypted string.
-
.generate_symmetric_key_files(filename = nil, environment = nil) ⇒ Object
Generate new random symmetric keys for use with this Encryption library.
-
.load!(filename = nil, environment = nil) ⇒ Object
Load the Encryption Configuration from a YAML file filename: Name of file to read.
-
.random_password ⇒ Object
Generate a 22 character random password.
-
.secondary_ciphers ⇒ Object
Returns the Primary Symmetric Cipher being used.
-
.secondary_ciphers=(secondary_ciphers) ⇒ Object
Set the Secondary Symmetric Ciphers Array to be used.
-
.try_decrypt(str) ⇒ Object
Invokes decrypt Returns decrypted String Return nil if it fails to decrypt a String.
Class Method Details
.cipher(version = nil) ⇒ Object
Returns the Primary Symmetric Cipher being used If a version is supplied
Returns the primary cipher if no match was found and version == 0
Returns nil if no match was found and version != 0
33 34 35 36 37 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 33 def self.cipher(version = nil) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher return @@cipher if version.nil? || (@@cipher.version == version) secondary_ciphers.find {|c| c.version == version} || (@@cipher if version == 0) end |
.cipher=(cipher) ⇒ Object
Set the Primary Symmetric Cipher to be used
Example: For testing purposes the following test cipher can be used:
SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new(
:key => '1234567890ABCDEF1234567890ABCDEF',
:iv => '1234567890ABCDEF',
:cipher => 'aes-128-cbc'
)
24 25 26 27 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 24 def self.cipher=(cipher) raise "Cipher must be similar to SymmetricEncryption::Ciphers" unless cipher.nil? || (cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt)) @@cipher = cipher end |
.decrypt(str) ⇒ Object
AES Symmetric Decryption of supplied string
Returns decrypted string
Returns nil if the supplied str is nil
Returns "" if it is a string and it is empty
Note: If secondary ciphers are supplied in the configuration file the
first key will be used to decrypt 'str'. If it fails each cipher in the
order supplied will be tried.
It is slow to try each cipher in turn, so should be used during migrations
only
Raises: OpenSSL::Cipher::CipherError when ‘str’ was not encrypted using the supplied key and iv
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 67 def self.decrypt(str) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher # Decode and then decrypt supplied string begin @@cipher.decrypt(str) rescue OpenSSL::Cipher::CipherError => exc @@secondary_ciphers.each do |cipher| begin return cipher.decrypt(str) rescue OpenSSL::Cipher::CipherError end end raise exc end end |
.encrypt(str, random_iv = false, compress = false) ⇒ Object
AES Symmetric Encryption of supplied string
Returns result as a Base64 encoded string
Returns nil if the supplied str is nil
Returns "" if it is a string and it is empty
Parameters
str [String]
String to be encrypted. If str is not a string, #to_s will be called on it
to convert it to a string
random_iv [true|false]
Whether the encypted value should use a random IV every time the
field is encrypted.
It is recommended to set this to true where feasible. If the encrypted
value could be used as part of a SQL where clause, or as part
of any lookup, then it must be false.
Setting random_iv to true will result in a different encrypted output for
the same input string.
Note: Only set to true if the field will never be used as part of
the where clause in an SQL query.
Note: When random_iv is true it will add a 8 byte header, plus the bytes
to store the random IV in every returned encrypted string, prior to the
encoding if any.
Default: false
Highly Recommended where feasible: true
compress [true|false]
Whether to compress str before encryption
Should only be used for large strings since compression overhead and
the overhead of adding the 'magic' header may exceed any benefits of
compression
Note: Adds a 6 byte header prior to encoding, only if :random_iv is false
Default: false
117 118 119 120 121 122 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 117 def self.encrypt(str, random_iv=false, compress=false) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher # Encrypt and then encode the supplied string @@cipher.encrypt(str, random_iv, compress) end |
.encrypted?(encrypted_data) ⇒ Boolean
Returns [true|false] as to whether the data could be decrypted
Parameters:
encrypted_data: Encrypted string
144 145 146 147 148 149 150 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 144 def self.encrypted?(encrypted_data) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher # For now have to decrypt it fully result = try_decrypt(encrypted_data) !(result.nil? || result == '') end |
.generate_symmetric_key_files(filename = nil, environment = nil) ⇒ Object
Generate new random symmetric keys for use with this Encryption library
Note: Only the current Encryption key settings are used
Creates Symmetric Key .key
and initilization vector .iv
which is encrypted with the above Public key
Existing key files will be renamed if present
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 186 def self.generate_symmetric_key_files(filename=nil, environment=nil) config = read_config(filename, environment) cipher_cfg = config[:ciphers].first key_filename = cipher_cfg[:key_filename] iv_filename = cipher_cfg[:iv_filename] cipher_name = cipher_cfg[:cipher_name] || cipher_cfg[:cipher] raise "The configuration file must contain a 'private_rsa_key' parameter to generate symmetric keys" unless config[:private_rsa_key] rsa_key = OpenSSL::PKey::RSA.new(config[:private_rsa_key]) # Generate a new Symmetric Key pair key_pair = SymmetricEncryption::Cipher.random_key_pair(cipher_name || 'aes-256-cbc', !iv_filename.nil?) # Save symmetric key after encrypting it with the private RSA key, backing up existing files if present File.rename(key_filename, "#{key_filename}.#{Time.now.to_i}") if File.exist?(key_filename) File.open(key_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:key]) ) } if iv_filename File.rename(iv_filename, "#{iv_filename}.#{Time.now.to_i}") if File.exist?(iv_filename) File.open(iv_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:iv]) ) } end puts("Generated new Symmetric Key for encryption. Please copy #{key_filename} and #{iv_filename} to the other web servers in #{environment}.") end |
.load!(filename = nil, environment = nil) ⇒ Object
Load the Encryption Configuration from a YAML file
filename:
Name of file to read.
Mandatory for non-Rails apps
Default: Rails.root/config/symmetric-encryption.yml
environment:
Which environments config to load. Usually: production, development, etc.
Default: Rails.env
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 160 def self.load!(filename=nil, environment=nil) config = read_config(filename, environment) # Check for hard coded key, iv and cipher if config[:key] @@cipher = Cipher.new(config) @@secondary_ciphers = [] else private_rsa_key = config[:private_rsa_key] @@cipher, *@@secondary_ciphers = config[:ciphers].collect do |cipher_conf| cipher_from_encrypted_files(private_rsa_key, cipher_conf) end end true end |
.random_password ⇒ Object
Generate a 22 character random password
211 212 213 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 211 def self.random_password Base64.encode64(OpenSSL::Cipher.new('aes-128-cbc').random_key)[0..-4] end |
.secondary_ciphers ⇒ Object
Returns the Primary Symmetric Cipher being used
49 50 51 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 49 def self.secondary_ciphers @@secondary_ciphers end |
.secondary_ciphers=(secondary_ciphers) ⇒ Object
Set the Secondary Symmetric Ciphers Array to be used
40 41 42 43 44 45 46 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 40 def self.secondary_ciphers=(secondary_ciphers) raise "secondary_ciphers must be a collection" unless secondary_ciphers.respond_to? :each secondary_ciphers.each do |cipher| raise "secondary_ciphers can only consist of SymmetricEncryption::Ciphers" unless cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt) end @@secondary_ciphers = secondary_ciphers end |
.try_decrypt(str) ⇒ Object
Invokes decrypt
Returns decrypted String
Return nil if it fails to decrypt a String
Useful for example when decoding passwords encrypted using a key from a different environment. I.e. We cannot decode production passwords in the test or development environments but still need to be able to load YAML config files that contain encrypted development and production passwords
132 133 134 135 136 137 138 139 |
# File 'lib/symmetric_encryption/symmetric_encryption.rb', line 132 def self.try_decrypt(str) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher begin decrypt(str) rescue OpenSSL::Cipher::CipherError nil end end |