Class: RHC::Vendor::SSHKey

Inherits:
Object show all
Defined in:
lib/rhc/vendor/sshkey.rb

Constant Summary collapse

SSH_TYPES =
{"rsa" => "ssh-rsa", "dsa" => "ssh-dss"}
SSH_CONVERSION =
{"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(private_key, options = {}) ⇒ SSHKey

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



153
154
155
156
157
158
159
160
161
162
163
# 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

Instance Attribute Details

#commentObject (readonly)

Returns the value of attribute comment.



36
37
38
# File 'lib/rhc/vendor/sshkey.rb', line 36

def comment
  @comment
end

#key_objectObject (readonly)

Returns the value of attribute key_object.



36
37
38
# File 'lib/rhc/vendor/sshkey.rb', line 36

def key_object
  @key_object
end

#passphraseObject

Returns the value of attribute passphrase.



37
38
39
# File 'lib/rhc/vendor/sshkey.rb', line 37

def passphrase
  @passphrase
end

#typeObject (readonly)

Returns the value of attribute type.



36
37
38
# File 'lib/rhc/vendor/sshkey.rb', line 36

def type
  @type
end

Class Method Details

.fingerprintObject

Fingerprints

Accepts either a public or private key

MD5 fingerprint for the given SSH key



114
115
116
117
118
119
120
# File 'lib/rhc/vendor/sshkey.rb', line 114

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

.generate(options = {}) ⇒ Object

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



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 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) ⇒ Object

Fingerprints

Accepts either a public or private key

MD5 fingerprint for the given SSH key



107
108
109
110
111
112
113
# 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

.sha1_fingerprint(key) ⇒ Object

SHA1 fingerprint for the given SSH key



117
118
119
120
121
122
123
# 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

.valid_ssh_public_key?(ssh_public_key) ⇒ Boolean

Validate an existing SSH public key

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

Parameters

  • ssh_public_key<~String> - “ssh-rsa AAAAB3NzaC1yc2EA.…”

Returns:

  • (Boolean)


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# 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

Instance Method Details

#encrypted_private_keyObject

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

If no passphrase is set, returns the unencrypted private key



177
178
179
180
# 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

#md5_fingerprintObject Also known as: fingerprint

Fingerprints

MD5 fingerprint for the given SSH public key



199
200
201
# File 'lib/rhc/vendor/sshkey.rb', line 199

def md5_fingerprint
  Digest::MD5.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
end

#private_keyObject Also known as: rsa_private_key, dsa_private_key

Fetch the RSA/DSA private key

rsa_private_key and dsa_private_key are aliased for backward compatibility



168
169
170
# File 'lib/rhc/vendor/sshkey.rb', line 168

def private_key
  key_object.to_pem
end

#public_keyObject Also known as: rsa_public_key, dsa_public_key

Fetch the RSA/DSA public key

rsa_public_key and dsa_public_key are aliased for backward compatibility



185
186
187
# File 'lib/rhc/vendor/sshkey.rb', line 185

def public_key
  key_object.public_key.to_pem
end

#sha1_fingerprintObject

SHA1 fingerprint for the given SSH public key



205
206
207
# File 'lib/rhc/vendor/sshkey.rb', line 205

def sha1_fingerprint
  Digest::SHA1.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2')
end

#ssh_public_keyObject

SSH public key



192
193
194
# 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