require 'encrypted_strings/no_private_key_error' require 'encrypted_strings/no_public_key_error' module PluginAWeek #:nodoc: module EncryptedStrings # Encryption in which the keys used to encrypt/decrypt come in pairs. Also # known as public key encryption. Anything that's encrypted using the # public key can only be decrypted with the same algorithm and a matching # private key. Any message that is encrypted with the private key can only # be decrypted with the matching public key. # # Source: http://support.microsoft.com/kb/246071 # # == Encrypting # # To encrypt a string using an asymmetric algorithm, the location of the # public key file must be specified. You can define the default for this # value like so: # # PluginAWeek::EncryptedStrings::AsymmetricEncryptor.default_public_key_file = "./public.key" # # If these configuration options are not passed in to #encrypt, then the # default values will be used. You can override the default values like so: # # password = "shhhh" # password.encrypt(:asymmetic, :public_key_file => "./encrypted_public.key") # => "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n" # # An exception will be raised if either the public key file could not be # found or the key could not decrypt the public key file. # # == Decrypting # # To decrypt a string using an asymmetric algorithm, the location of the # private key file must be specified. If this file is itself encrypted, you # must also specify the algorithm and key used to seed the symmetric # algorithm that will decrypt the plublic key file. You can define defaults # for these values like so: # # PluginAWeek::EncryptedStrings::AsymmetricEncryptor.default_private_key_file = "./private.key" # PluginAWeek::EncryptedStrings::SymmetricEncryptor.default_algorithm = "DES-EDE3-CBC" # PluginAWeek::EncryptedStrings::SymmetricEncryptor.default_key = "secret_key" # # If these configuration options are not passed in to #decrypt, then the # default values will be used. You can override the default values like so: # # password = "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n" # password.decrypt(:asymmetic, :public_key_file => "./encrypted_private.key", :key => "secret") # => "shhhh" # # An exception will be raised if either the private key file could not be # found or the key could not decrypt the private key file. class AsymmetricEncryptor < Encryptor # The default private key to use during encryption. Default is nil. @@default_private_key_file = nil cattr_accessor :default_private_key_file # The default public key to use during encryption. Default is nil. @@default_public_key_file = nil cattr_accessor :default_public_key_file # The default algorithm to use. Default is nil. @@default_algorithm = nil cattr_accessor :default_algorithm attr_reader :private_key_file attr_reader :public_key_file attr_accessor :algorithm attr_accessor :key # Configuration options: # * private_key_file - Encrypted private key file # * public_key_file - Public key file # * key - The key to use in the symmetric encryptor # * algorithm - Algorithm to use symmetrically encrypted strings def initialize(options = {}) options = options.symbolize_keys options.assert_valid_keys( :private_key_file, :public_key_file, :key, :algorithm ) options.reverse_merge!( :private_key_file => @@default_private_key_file, :public_key_file => @@default_public_key_file, :algorithm => @@default_algorithm ) @public_key = @private_key = nil @key = options[:key] @algorithm = options[:algorithm] self.private_key_file = options[:private_key_file] self.public_key_file = options[:public_key_file] super() end # Encrypts the given data def encrypt(data) raise NoPublicKeyError, "Public key file: #{@public_key_file}" unless public? encrypted_data = public_rsa.public_encrypt(data) Base64.encode64(encrypted_data) end # Decrypts the given data def decrypt(data) raise NoPrivateKeyError, "Private key file: #{@private_key_file}" unless private? decrypted_data = Base64.decode64(data) private_rsa.private_decrypt(decrypted_data) end # Sets the location of the private key and loads it def private_key_file=(file) @private_key_file = file and load_private_key end # Sets the location of the public key and loads it def public_key_file=(file) @public_key_file = file and load_public_key end # Is this string encrypted using a public key? def public? return true unless @public_key.nil? load_public_key !@public_key.nil? end # Is this string encrypted using a private key? def private? return true unless @private_key.nil? load_private_key !@private_key.nil? end private def load_private_key @private_rsa = nil if @private_key_file && File.file?(@private_key_file) @private_key = File.open(@private_key_file) {|f| f.read} end end def load_public_key @public_rsa = nil if @public_key_file && File.file?(@public_key_file) @public_key = File.open(@public_key_file) {|f| f.read} end end # Retrieves private RSA from the private key def private_rsa if @key private_key = @private_key.decrypt(:symmetric, :key => @key, :algorithm => @algorithm) OpenSSL::PKey::RSA.new(private_key) else @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key) end end # Retrieves the public RSA def public_rsa @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key) end end end end