class RHC::Vendor::SSHKey

Constants

SSH_CONVERSION
SSH_TYPES

Attributes

comment[R]
key_object[R]
passphrase[RW]
type[R]

Public Class Methods

new(private_key, options = {}) click to toggle source

Create a new SSHKey object

Parameters

  • #private_key - Existing RSA or DSA private key

  • options<~Hash>

    • :comment<~String> - Comment to use for the public key, defaults to “”

    • :passphrase<~String> - If the key is encrypted, supply the passphrase

# File lib/rhc/vendor/sshkey.rb, line 153
def initialize(private_key, options = {})
  @passphrase = options[:passphrase]
  @comment    = options[:comment] || ""
  begin
    @key_object = OpenSSL::PKey::RSA.new(private_key, passphrase)
    @type = "rsa"
  rescue
    @key_object = OpenSSL::PKey::DSA.new(private_key, passphrase)
    @type = "dsa"
  end
end

Public Instance Methods

dsa_private_key()
Alias for: private_key
dsa_public_key()
Alias for: public_key
encrypted_private_key() click to toggle source

Fetch the encrypted RSA/DSA private key using the passphrase provided

If no passphrase is set, returns the unencrypted private key

# File lib/rhc/vendor/sshkey.rb, line 177
def encrypted_private_key
  return private_key unless passphrase
  key_object.to_pem(OpenSSL::Cipher::Cipher.new("AES-128-CBC"), passphrase)
end
fingerprint(key)
Alias for: md5_fingerprint
generate(options = {}) click to toggle source

Generate a new keypair and return an SSHKey object

The default behavior when providing no options will generate a 2048-bit RSA keypair.

Parameters

  • options<~Hash>:

    • :type<~String> - “rsa” or “dsa”, “rsa” by default

    • :bits<~Integer> - Bit length

    • :comment<~String> - Comment to use for the public key, defaults to “”

    • :passphrase<~String> - Encrypt the key with this passphrase

# File lib/rhc/vendor/sshkey.rb, line 52
def generate(options = {})
  type   = options[:type] || "rsa"

  # JRuby modulus size must range from 512 to 1024
  default_bits = type == "rsa" ? 2048 : 1024

  bits   = options[:bits] || default_bits
  cipher = OpenSSL::Cipher::Cipher.new("AES-128-CBC") if options[:passphrase]

  case type.downcase
  when "rsa" then new(OpenSSL::PKey::RSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
  when "dsa" then new(OpenSSL::PKey::DSA.generate(bits).to_pem(cipher, options[:passphrase]), options)
  else
    raise "Unknown key type: #{type}"
  end
end
md5_fingerprint(key) click to toggle source

Fingerprints

Accepts either a public or private key

MD5 fingerprint for the given SSH key

# File lib/rhc/vendor/sshkey.rb, line 107
def md5_fingerprint(key)
  if key.match(/PRIVATE/)
    new(key).md5_fingerprint
  else
    Digest::MD5.hexdigest(decoded_key(key)).gsub(fingerprint_regex, '\1:\2')
  end
end
Also aliased as: fingerprint
private_key() click to toggle source

Fetch the RSA/DSA private key

#rsa_private_key and #dsa_private_key are aliased for backward compatibility

# File lib/rhc/vendor/sshkey.rb, line 168
def private_key
  key_object.to_pem
end
Also aliased as: rsa_private_key, dsa_private_key
public_key() click to toggle source

Fetch the RSA/DSA public key

#rsa_public_key and #dsa_public_key are aliased for backward compatibility

# File lib/rhc/vendor/sshkey.rb, line 185
def public_key
  key_object.public_key.to_pem
end
Also aliased as: rsa_public_key, dsa_public_key
rsa_private_key()
Alias for: private_key
rsa_public_key()
Alias for: public_key
sha1_fingerprint(key) click to toggle source

SHA1 fingerprint for the given SSH key

# File lib/rhc/vendor/sshkey.rb, line 117
def sha1_fingerprint(key)
  if key.match(/PRIVATE/)
    new(key).sha1_fingerprint
  else
    Digest::SHA1.hexdigest(decoded_key(key)).gsub(fingerprint_regex, '\1:\2')
  end
end
ssh_public_key() click to toggle source

SSH public key

# File lib/rhc/vendor/sshkey.rb, line 192
def ssh_public_key
  [SSH_TYPES[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
end
valid_ssh_public_key?(ssh_public_key) click to toggle source

Validate an existing SSH public key

Returns true or false depending on the validity of the public key provided

Parameters

# File lib/rhc/vendor/sshkey.rb, line 76
def valid_ssh_public_key?(ssh_public_key)
  ssh_type, encoded_key = ssh_public_key.split(" ")
  type = SSH_TYPES.invert[ssh_type]
  prefix = [0,0,0,7].pack("C*")
  decoded = Base64.decode64(encoded_key)

  # Base64 decoding is too permissive, so we should validate if encoding is correct
  return false unless Base64.encode64(decoded).gsub("\n", "") == encoded_key
  return false unless decoded.sub!(/^#{prefix}#{ssh_type}/, "")

  unpacked = decoded.unpack("C*")
  data = []
  index = 0
  until unpacked[index].nil?
    datum_size = from_byte_array unpacked[index..index+4-1], 4
    index = index + 4
    datum = from_byte_array unpacked[index..index+datum_size-1], datum_size
    data << datum
    index = index + datum_size
  end

  SSH_CONVERSION[type].size == data.size
rescue
  false
end

Private Instance Methods

decoded_key(key) click to toggle source
# File lib/rhc/vendor/sshkey.rb, line 136
def decoded_key(key)
  Base64.decode64(key.chomp.gsub(/ssh-[dr]s[as] /, ''))
end
encode_unsigned_int_32(value) click to toggle source
# File lib/rhc/vendor/sshkey.rb, line 233
def encode_unsigned_int_32(value)
  out = []
  out[0] = value >> 24 & 0xff
  out[1] = value >> 16 & 0xff
  out[2] = value >> 8 & 0xff
  out[3] = value & 0xff
  return out
end
fingerprint_regex() click to toggle source
# File lib/rhc/vendor/sshkey.rb, line 140
def fingerprint_regex
  /(.{2})(?=.)/
end
from_byte_array(byte_array, expected_size = nil) click to toggle source
# File lib/rhc/vendor/sshkey.rb, line 127
def from_byte_array(byte_array, expected_size = nil)
  num = 0
  raise "Byte array too short" if !expected_size.nil? && expected_size != byte_array.size
  byte_array.reverse.each_with_index do |item, index|
    num += item * 256**(index)
  end
  num
end
ssh_public_key_conversion() click to toggle source

For instance, the “ssh-rsa” string is encoded as the following byte array

0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a'
# File lib/rhc/vendor/sshkey.rb, line 220
def ssh_public_key_conversion
  out = [0,0,0,7].pack("C*")
  out += SSH_TYPES[type]

  SSH_CONVERSION[type].each do |method|
    byte_array = to_byte_array(key_object.public_key.send(method).to_i)
    out += encode_unsigned_int_32(byte_array.length).pack("c*")
    out += byte_array.pack("C*")
  end

  return out
end
to_byte_array(num) click to toggle source
# File lib/rhc/vendor/sshkey.rb, line 242
def to_byte_array(num)
  result = []
  begin
    result << (num & 0xff)
    num >>= 8
  end until (num == 0 || num == -1) && (result.last[7] == num[7])
  result.reverse
end