Class: R509::PrivateKey

Inherits:
Object
  • Object
show all
Includes:
IOHelpers
Defined in:
lib/r509/private_key.rb

Overview

private key management

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from IOHelpers

#read_data, read_data, #write_data, write_data

Constructor Details

- (PrivateKey) initialize(opts = {})

A new instance of PrivateKey

Parameters:

  • opts (Hash) (defaults to: {})

    a customizable set of options

Options Hash (opts):

  • :type (Symbol)

    :rsa/:dsa/:ec

  • :curve_name (String) — default: "secp384r1"

    Only used if :type is :ec

  • :bit_strength (Integer) — default: 2048

    Only used if :type is :rsa or :dsa.

  • :password (String)
  • :key (String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA, OpenSSL::PKey::EC)
  • :engine (OpenSSL::Engine)
  • :key_name (string) — default: used with engine


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/r509/private_key.rb', line 17

def initialize(opts={})
  if not opts.kind_of?(Hash)
    raise ArgumentError, 'Must provide a hash of options'
  end

  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
  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
  end
end

Class Method Details

+ (R509::PrivateKey) load_from_file(filename, password = nil)

Helper method to quickly load a private key from the filesystem

Parameters:

  • filename (String)

    Path to file you want to load

Returns:



75
76
77
# File 'lib/r509/private_key.rb', line 75

def self.load_from_file( filename, password = nil )
  return R509::PrivateKey.new(:key => IOHelpers.read_data(filename), :password => password )
end

Instance Method Details

- (Integer) bit_strength

Returns the bit strength of the key

Returns:

  • (Integer)


83
84
85
86
87
88
89
90
91
# File 'lib/r509/private_key.rb', line 83

def bit_strength
  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.'
  end
end

- (String) curve_name

Returns the short name of the elliptic curve used to generate the private key if the key is EC. If not, raises an error.

Returns:

  • (String)

    elliptic curve name



97
98
99
100
101
102
103
# File 'lib/r509/private_key.rb', line 97

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

- (Boolean) dsa?

Returns whether the key is DSA

Returns:

  • (Boolean)

    true if the key is DSA, false otherwise



217
218
219
# File 'lib/r509/private_key.rb', line 217

def dsa?
  self.key.kind_of?(OpenSSL::PKey::DSA)
end

- (Boolean) ec?

Returns whether the key is EC

Returns:

  • (Boolean)

    true if the key is EC, false otherwise



224
225
226
# File 'lib/r509/private_key.rb', line 224

def ec?
  self.key.kind_of?(OpenSSL::PKey::EC)
end

- (Boolean) in_hardware?

Whether the key is resident in hardware or not

Returns:

  • (Boolean)

    whether the key is resident in hardware or not



115
116
117
118
119
120
121
# File 'lib/r509/private_key.rb', line 115

def in_hardware?
  if not @engine.nil?
    true
  else
    false
  end
end

- (OpenSSL::PKey::RSA, ...) key

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)

Returns:

  • (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)



106
107
108
109
110
111
112
# File 'lib/r509/private_key.rb', line 106

def key
  if in_hardware?
    @engine.load_private_key(@key_name)
  else
    @key
  end
end

- (OpenSSL::PKey::RSA, ...) public_key Also known as: to_s

Public key

Returns:

  • (OpenSSL::PKey::RSA, OpenSSL::PKey::DSA, OpenSSL::PKey::EC)

    public key



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/r509/private_key.rb', line 124

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

- (Boolean) rsa?

Returns whether the key is RSA

Returns:

  • (Boolean)

    true if the key is RSA, false otherwise



210
211
212
# File 'lib/r509/private_key.rb', line 210

def rsa?
  self.key.kind_of?(OpenSSL::PKey::RSA)
end

- (String) to_der

Converts the key into the DER format

Returns:

  • (String)

    the key converted into DER format.



170
171
172
173
174
175
# File 'lib/r509/private_key.rb', line 170

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

- (String) to_encrypted_pem(cipher, password)

Converts the key into encrypted PEM format

full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers (common ones are des3, aes256, aes128)

Parameters:

  • cipher (String, OpenSSL::Cipher)

    to use for encryption

  • password (String)

    password

Returns:

  • (String)

    the key converted into encrypted PEM format.



158
159
160
161
162
163
164
# File 'lib/r509/private_key.rb', line 158

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

- (String) to_pem

Converts the key into the PEM format

Returns:

  • (String)

    the key converted into PEM format.



144
145
146
147
148
149
# File 'lib/r509/private_key.rb', line 144

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

- (Object) write_der(filename_or_io)

Writes the key into the DER format

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.



202
203
204
# File 'lib/r509/private_key.rb', line 202

def write_der(filename_or_io)
  write_data(filename_or_io, self.to_der)
end

- (Object) write_encrypted_pem(filename_or_io, cipher, password)

Writes the key into encrypted PEM format with specified cipher

full list of available ciphers can be obtained with OpenSSL::Cipher.ciphers (common ones are des3, aes256, aes128)

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.

  • cipher (String, OpenSSL::Cipher)

    to use for encryption

  • password (String)

    password



194
195
196
# File 'lib/r509/private_key.rb', line 194

def write_encrypted_pem(filename_or_io,cipher,password)
  write_data(filename_or_io, to_encrypted_pem(cipher,password))
end

- (Object) write_pem(filename_or_io)

Writes the key into the PEM format

Parameters:

  • filename_or_io (String, #write)

    Either a string of the path for the file that you'd like to write, or an IO-like object.



181
182
183
# File 'lib/r509/private_key.rb', line 181

def write_pem(filename_or_io)
  write_data(filename_or_io, self.to_pem)
end