lib/r509/private_key.rb in r509-0.9.2 vs lib/r509/private_key.rb in r509-0.10.0
- old
+ new
@@ -5,68 +5,37 @@
module R509
#private key management
class PrivateKey
include R509::IOHelpers
- # @option opts [Symbol] :type :rsa/:dsa/:ec
- # @option opts [String] :curve_name ("secp384r1") Only used if :type is :ec
- # @option opts [Integer] :bit_strength (2048) Only used if :type is :rsa or :dsa.
+ # a list of key types
+ KNOWN_TYPES = ["RSA","DSA","EC"]
+ # the default type
+ DEFAULT_TYPE = "RSA"
+ # default bit length for DSA/RSA
+ DEFAULT_STRENGTH = 2048
+ # default curve name for EC
+ DEFAULT_CURVE = "secp384r1"
+
+ # @option opts [Symbol] :type Defaults to R509::PrivateKey::DEFAULT_TYPE. Allows R509::PrivateKey::KNOWN_TYPES.
+ # @option opts [String] :curve_name ("secp384r1") Only used if :type is EC
+ # @option opts [Integer] :bit_length (2048) Only used if :type is RSA or DSA
+ # @option opts [Integer] :bit_strength (2048) Deprecated, identical to bit_length.
# @option opts [String] :password
# @option opts [String,OpenSSL::PKey::RSA,OpenSSL::PKey::DSA,OpenSSL::PKey::EC] :key
# @option opts [OpenSSL::Engine] :engine
# @option opts [string] :key_name (used with engine)
def initialize(opts={})
if not opts.kind_of?(Hash)
raise ArgumentError, 'Must provide a hash of options'
end
+ validate_engine(opts)
- if opts.has_key?(:engine) and opts.has_key?(:key)
- raise ArgumentError, 'You can\'t pass both :key and :engine'
- elsif opts.has_key?(:key_name) and not opts.has_key?(:engine)
- raise ArgumentError, 'When providing a :key_name you MUST provide an :engine'
- elsif opts.has_key?(:engine) and not opts.has_key?(:key_name)
- raise ArgumentError, 'When providing an :engine you MUST provide a :key_name'
- elsif opts.has_key?(:engine) and opts.has_key?(:key_name)
- if not opts[:engine].kind_of?(OpenSSL::Engine)
- raise ArgumentError, 'When providing an engine, it must be of type OpenSSL::Engine'
- end
- @engine = opts[:engine]
- @key_name = opts[:key_name]
- end
-
if opts.has_key?(:key)
- password = opts[:password] || nil
- #OpenSSL::PKey.read solves this begin/rescue garbage but is only
- #available to Ruby 1.9.3+ and may not solve the EC portion
- begin
- @key = OpenSSL::PKey::RSA.new(opts[:key],password)
- rescue OpenSSL::PKey::RSAError
- begin
- @key = OpenSSL::PKey::DSA.new(opts[:key],password)
- rescue
- begin
- @key = OpenSSL::PKey::EC.new(opts[:key],password)
- rescue
- raise R509::R509Error, "Failed to load private key. Invalid key or incorrect password."
- end
- end
- end
+ validate_key(opts)
else
- bit_strength = opts[:bit_strength] || 2048
- type = opts[:type] || :rsa
- case type
- when :rsa
- @key = OpenSSL::PKey::RSA.new(bit_strength)
- when :dsa
- @key = OpenSSL::PKey::DSA.new(bit_strength)
- when :ec
- curve_name = opts[:curve_name] || "secp384r1"
- @key = OpenSSL::PKey::EC.new(curve_name)
- @key.generate_key
- else
- raise ArgumentError, 'Must provide :rsa, :dsa , or :ec as type when key or engine is nil'
- end
+ generate_key(opts)
end
end
# Helper method to quickly load a private key from the filesystem
#
@@ -75,22 +44,23 @@
def self.load_from_file( filename, password = nil )
return R509::PrivateKey.new(:key => IOHelpers.read_data(filename), :password => password )
end
- # Returns the bit strength of the key
+ # Returns the bit length of the key
#
# @return [Integer]
- def bit_strength
+ def bit_length
if self.rsa?
return self.public_key.n.num_bits
elsif self.dsa?
return self.public_key.p.num_bits
elsif self.ec?
- raise R509::R509Error, 'Bit strength is not available for EC at this time.'
+ raise R509::R509Error, 'Bit length is not available for EC at this time.'
end
end
+ alias :bit_strength :bit_length
# Returns the short name of the elliptic curve used to generate the private key
# if the key is EC. If not, raises an error.
#
# @return [String] elliptic curve name
@@ -221,8 +191,62 @@
# Returns whether the key is EC
#
# @return [Boolean] true if the key is EC, false otherwise
def ec?
self.key.kind_of?(OpenSSL::PKey::EC)
+ end
+
+ private
+
+ def validate_engine(opts)
+ if opts.has_key?(:engine) and opts.has_key?(:key)
+ raise ArgumentError, 'You can\'t pass both :key and :engine'
+ elsif opts.has_key?(:key_name) and not opts.has_key?(:engine)
+ raise ArgumentError, 'When providing a :key_name you MUST provide an :engine'
+ elsif opts.has_key?(:engine) and not opts.has_key?(:key_name)
+ raise ArgumentError, 'When providing an :engine you MUST provide a :key_name'
+ elsif opts.has_key?(:engine) and opts.has_key?(:key_name)
+ if not opts[:engine].kind_of?(OpenSSL::Engine)
+ raise ArgumentError, 'When providing an engine, it must be of type OpenSSL::Engine'
+ end
+ @engine = opts[:engine]
+ @key_name = opts[:key_name]
+ end
+ end
+
+ def validate_key(opts)
+ password = opts[:password] || nil
+ #OpenSSL::PKey.read solves this begin/rescue garbage but is only
+ #available to Ruby 1.9.3+ and may not solve the EC portion
+ begin
+ @key = OpenSSL::PKey::RSA.new(opts[:key],password)
+ rescue OpenSSL::PKey::RSAError
+ begin
+ @key = OpenSSL::PKey::DSA.new(opts[:key],password)
+ rescue
+ begin
+ @key = OpenSSL::PKey::EC.new(opts[:key],password)
+ rescue
+ raise R509::R509Error, "Failed to load private key. Invalid key or incorrect password."
+ end
+ end
+ end
+ end
+
+ def generate_key(opts)
+ bit_length = opts[:bit_length] || opts[:bit_strength] || DEFAULT_STRENGTH
+ type = opts[:type] || DEFAULT_TYPE
+ case type.upcase
+ when "RSA"
+ @key = OpenSSL::PKey::RSA.new(bit_length)
+ when "DSA"
+ @key = OpenSSL::PKey::DSA.new(bit_length)
+ when "EC"
+ curve_name = opts[:curve_name] || DEFAULT_CURVE
+ @key = OpenSSL::PKey::EC.new(curve_name)
+ @key.generate_key
+ else
+ raise ArgumentError, "Must provide #{KNOWN_TYPES.join(", ")} as type when key or engine is nil"
+ end
end
end
end