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

load_from_file(filename, password = nil) click to toggle source

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
new(opts = {}) click to toggle source

@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

bit_length() click to toggle source

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
Also aliased as: bit_strength
bit_strength()
Alias for: bit_length
curve_name() click to toggle source

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
dsa?() click to toggle source

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
ec?() click to toggle source

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
in_hardware?() click to toggle source

@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
key() click to toggle source

@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
public_key() click to toggle source

@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
Also aliased as: to_s
rsa?() click to toggle source

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
to_der() click to toggle source

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
to_encrypted_pem(cipher, password) click to toggle source

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
to_pem() click to toggle source

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
to_s()
Alias for: public_key
write_der(filename_or_io) click to toggle source

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
write_encrypted_pem(filename_or_io, cipher, password) click to toggle source

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
write_pem(filename_or_io) click to toggle source

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

generate_key(opts) click to toggle source
# 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
validate_engine(opts) click to toggle source
# 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
validate_key(opts) click to toggle source
# 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