class R509::PrivateKey
private key management
Constants
- DEFAULT_CURVE
default curve name for EC
- DEFAULT_STRENGTH
default bit length for DSA/RSA
- DEFAULT_TYPE
the default type
- KNOWN_TYPES
a list of key types
Public Class Methods
Helper method to quickly load a private key from the filesystem
@param [String] filename Path to file you want to load @return [R509::PrivateKey] PrivateKey
object
# File lib/r509/private_key.rb, line 44 def self.load_from_file(filename, password = nil) R509::PrivateKey.new(:key => IOHelpers.read_data(filename), :password => password) end
@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)
# File lib/r509/private_key.rb, line 27 def initialize(opts = {}) unless opts.is_a?(Hash) raise ArgumentError, 'Must provide a hash of options' end validate_engine(opts) if opts.key?(:key) validate_key(opts) else generate_key(opts) end end
Public Instance Methods
Returns the bit length of the key
@return [Integer]
# File lib/r509/private_key.rb, line 51 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 length is not available for EC at this time.' end end
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
# File lib/r509/private_key.rb, line 66 def curve_name if self.ec? self.key.group.curve_name else raise R509::R509Error, 'Curve name is only available with EC private keys' end end
Returns whether the key is DSA
@return [Boolean] true if the key is DSA, false otherwise
# File lib/r509/private_key.rb, line 183 def dsa? self.key.is_a?(OpenSSL::PKey::DSA) end
Returns whether the key is EC
@return [Boolean] true if the key is EC, false otherwise
# File lib/r509/private_key.rb, line 190 def ec? self.key.is_a?(OpenSSL::PKey::EC) end
@return [Boolean] whether the key is resident in hardware or not
# File lib/r509/private_key.rb, line 84 def in_hardware? if @engine true else false end end
@return [OpenSSL::PKey::RSA,OpenSSL::PKey::DSA,OpenSSL::Engine pkey] this method may return the PKey object itself or a handle to the private key in the HSM (which will not show the private key, just public)
# File lib/r509/private_key.rb, line 75 def key if in_hardware? @engine.load_private_key(@key_name) else @key end end
@return [OpenSSL::PKey::RSA,OpenSSL::PKey::DSA,OpenSSL::PKey::EC] public key
# File lib/r509/private_key.rb, line 93 def public_key if self.ec? # OpenSSL::PKey::EC.public_key returns an OpenSSL::PKey::EC::Point, which isn't consistent # with the way OpenSSL::PKey::RSA/DSA do it. We could return the original PKey::EC object # but if we do that then it has the private_key as well. Here's a ghetto workaround. # We have to supply the curve name to the temporary key object or else #public_key= fails curve_name = self.key.group.curve_name temp_key = OpenSSL::PKey::EC.new(curve_name) temp_key.public_key = self.key.public_key temp_key else self.key.public_key end end
Returns whether the key is RSA
@return [Boolean] true if the key is RSA, false otherwise
# File lib/r509/private_key.rb, line 176 def rsa? self.key.is_a?(OpenSSL::PKey::RSA) end
Converts the key into the DER format
@return [String] the key converted into DER format.
# File lib/r509/private_key.rb, line 138 def to_der if in_hardware? raise R509::R509Error, "This method cannot be called when using keys in hardware" end self.key.to_der end
Converts the key into encrypted PEM format
@param [String,OpenSSL::Cipher] cipher to use for encryption full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers (common ones are des3, aes256, aes128) @param [String] password password @return [String] the key converted into encrypted PEM format.
# File lib/r509/private_key.rb, line 127 def to_encrypted_pem(cipher, password) if in_hardware? raise R509::R509Error, "This method cannot be called when using keys in hardware" end cipher = OpenSSL::Cipher::Cipher.new(cipher) self.key.to_pem(cipher, password) end
Converts the key into the PEM format
@return [String] the key converted into PEM format.
# File lib/r509/private_key.rb, line 113 def to_pem if in_hardware? raise R509::R509Error, "This method cannot be called when using keys in hardware" end self.key.to_pem end
Writes the key into the DER format
@param [String, write] filename_or_io Either a string of the path for
the file that you'd like to write, or an IO-like object.
# File lib/r509/private_key.rb, line 169 def write_der(filename_or_io) write_data(filename_or_io, self.to_der) end
Writes the key into encrypted PEM format with specified cipher
@param [String, write] filename_or_io Either a string of the path for
the file that you'd like to write, or an IO-like object.
@param [String,OpenSSL::Cipher] cipher to use for encryption full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers (common ones are des3, aes256, aes128) @param [String] password password
# File lib/r509/private_key.rb, line 161 def write_encrypted_pem(filename_or_io, cipher, password) write_data(filename_or_io, to_encrypted_pem(cipher, password)) end
Writes the key into the PEM format
@param [String, write] filename_or_io Either a string of the path for
the file that you'd like to write, or an IO-like object.
# File lib/r509/private_key.rb, line 149 def write_pem(filename_or_io) write_data(filename_or_io, self.to_pem) end
Private Instance Methods
# File lib/r509/private_key.rb, line 231 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
# File lib/r509/private_key.rb, line 196 def validate_engine(opts) if opts.key?(:engine) && opts.key?(:key) raise ArgumentError, 'You can\'t pass both :key and :engine' elsif opts.key?(:key_name) && !opts.key?(:engine) raise ArgumentError, 'When providing a :key_name you MUST provide an :engine' elsif opts.key?(:engine) && !opts.key?(:key_name) raise ArgumentError, 'When providing an :engine you MUST provide a :key_name' elsif opts.key?(:engine) && opts.key?(:key_name) unless opts[:engine].is_a?(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
# File lib/r509/private_key.rb, line 212 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