Permalink
Browse files

Support for using methods and procs for keys

  • Loading branch information...
1 parent d8c1f1b commit ba219500a991701848546757c636ebda25e14270 @spikex committed Apr 18, 2012
Showing with 181 additions and 4 deletions.
  1. +25 −3 README.textile
  2. +1 −1 lib/strongbox.rb
  3. +9 −0 lib/strongbox/lock.rb
  4. +77 −0 test/method_key_test.rb
  5. +57 −0 test/proc_key_test.rb
  6. +12 −0 test/test_helper.rb
View
@@ -69,11 +69,11 @@ Which will encrypt the attribute "secret". The attribute will be encrypted using
Options to encrypt_with_public_key are:
-:public_key - Path to the public key file. Overrides :keypair.
+:public_key - Public key. Overrides :key_pair. See Key Formats below.
-:private_key - Path to the private key file. Overrides :keypair.
+:private_key - Private key. Overrides :key_pair.
-:keypair - Path to a file containing both the public and private keys.
+:key_pair - Key pair, containing both the public and private keys.
:symmetric :always/:never - Encrypt the date using symmetric encryption. The public key is used to encrypt an automatically generated key and IV. This allows for large amounts of data to be encrypted. The size of data that can be encrypted directly with the public is limit to key size (in bytes) - 11. So a 2048 key can encrypt *245 bytes*. Defaults to *:always*.
@@ -111,8 +111,19 @@ bc. class User < ActiveRecord::Base
:key_pair => File.join(RAILS_ROOT,'config','another_key.pem')
end
+h2 Key Formats
+
+_:public_key_, _:private_key_, and _:key_pair_ can be in one of the following formats:
+
+* A string containing path to a file. This is the default interpretation of a string.
+* A string contanting a key in PEM format, needs to match this the regex /^-+BEGIN .* KEY-+$/
+* A symbol naming a method to call. Can return any of the other valid key formats.
+* A instance of OpenSSL::PKey::RSA. Must be unlocked to be used as the private key.
+
h2. Key Generation
+h3. In the shell
+
Generate a key pair:
bc. openssl genrsa -des3 -out config/private.pem 2048
@@ -135,6 +146,17 @@ bc. cat config/private.pem config/public.pem >> config/keypair.pem
Or, for added security, store the private key file else where, leaving only the public key.
+h3. In code
+
+bc. require 'openssl'
+rsa_key = OpenSSL::PKey::RSA.new(2048)
+cipher = OpenSSL::Cipher::Cipher.new('des3')
+private_key = rsa_key.to_pem(cipher,'password')
+public_key = rsa_key.public_key.to_pem
+key_pair = private_key + public_key
+
+_private_key_, _public_key_, and _key_pair_ are strings, store as you see fit.
+
h2. Table Creation
In it's default configuration Strongbox requires three columns, one the encrypted data, one for the encrypted symmetric key, and one for the encrypted symmetric IV. If symmetric encryption is disabled then only the columns for the data being encrypted is needed.
View
@@ -5,7 +5,7 @@
module Strongbox
- VERSION = "0.5.0"
+ VERSION = "0.6.0"
RSA_PKCS1_PADDING = OpenSSL::PKey::RSA::PKCS1_PADDING
RSA_SSLV23_PADDING = OpenSSL::PKey::RSA::SSLV23_PADDING
@@ -133,7 +133,16 @@ def ensure_required_columns
private
def get_rsa_key(key,password = '')
+ if key.is_a?(Proc)
+ key = key.call
+ end
+
+ if key.is_a?(Symbol)
+ key = @instance.send(key)
+ end
+
return key if key.is_a?(OpenSSL::PKey::RSA)
+
if key !~ /^-+BEGIN .* KEY-+$/
key = File.read(key)
end
@@ -0,0 +1,77 @@
+require 'test/test_helper'
+
+class MethodKeyTest < Test::Unit::TestCase
+ context 'With an attribute containing a string for the key pair' do
+ setup do
+ @password = 'boost facile'
+ rebuild_model :key_pair => :key_pair_attribute
+ Dummy.class_eval do
+ attr_accessor :key_pair_attribute
+ end
+
+ @dummy = Dummy.new
+ @dummy.key_pair_attribute = File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With a methods returning the key pair' do
+ setup do
+ @password = 'boost facile'
+ rebuild_model :key_pair => :key_pair_method
+ Dummy.class_eval do
+ def key_pair_method
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+ end
+ end
+
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With attributes containing strings for the keys' do
+ setup do
+ @password = 'boost facile'
+ rsa_key = OpenSSL::PKey::RSA.new(2048)
+ cipher = OpenSSL::Cipher::Cipher.new('des3')
+ rebuild_model :public_key => :public_key_attribute,
+ :private_key => :private_key_attribute
+ Dummy.class_eval do
+ attr_accessor :public_key_attribute, :private_key_attribute
+ end
+ @dummy = Dummy.new
+ @dummy.public_key_attribute = rsa_key.public_key.to_pem
+ @dummy.private_key_attribute = rsa_key.to_pem(cipher,@password)
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With methods returning the keys' do
+ setup do
+ @password = 'boost facile'
+ rebuild_model :public_key => :public_key_method,
+ :private_key => :private_key_method
+ Dummy.class_eval do
+ def public_key_method
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+ end
+
+ def private_key_method
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+ end
+ end
+
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+end
View
@@ -0,0 +1,57 @@
+require 'test/test_helper'
+
+class ProcKeyTest < Test::Unit::TestCase
+ context 'With a Proc returning a string for a key pair' do
+ setup do
+ @password = 'boost facile'
+ rebuild_model :key_pair => Proc.new {
+ File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+ }
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With a Proc returning a key object' do
+ setup do
+ @password = 'boost facile'
+ @private_key = OpenSSL::PKey::RSA.new(2048)
+ rebuild_model :key_pair => Proc.new { @private_key }
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With Procs returning public and private key strings' do
+ setup do
+ @password = 'boost facile'
+ @key_pair = File.read(File.join(FIXTURES_DIR,'keypair.pem'))
+
+ rebuild_model :public_key => Proc.new { @key_pair },
+ :private_key => Proc.new { @key_pair }
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+
+ context 'With Procs returning public and private key objects' do
+ setup do
+ @password = 'boost facile'
+ @private_key = OpenSSL::PKey::RSA.new(2048)
+ @public_key = @private_key.public_key
+
+ rebuild_model :public_key => Proc.new { @public_key },
+ :private_key => Proc.new { @private_key }
+ @dummy = Dummy.new
+ @dummy.secret = 'Shhhh'
+ end
+
+ should_encypted_and_decrypt
+ end
+end
View
@@ -72,3 +72,15 @@ def generate_key_pair(password = nil,size = 2048)
key_pair << rsa_key.public_key.to_pem
return key_pair
end
+
+class Test::Unit::TestCase
+ def self.should_encypted_and_decrypt
+ should 'return "*encrypted*" when locked' do
+ assert_equal '*encrypted*', @dummy.secret.decrypt
+ end
+
+ should 'return secret when unlocked' do
+ assert_equal 'Shhhh', @dummy.secret.decrypt(@password)
+ end
+ end
+end

0 comments on commit ba21950

Please sign in to comment.