Class: SSHKey
- Inherits:
-
Object
- Object
- SSHKey
- Defined in:
- lib/sshkey.rb,
lib/sshkey/version.rb
Defined Under Namespace
Classes: PublicKeyError
Constant Summary collapse
- SSH_TYPES =
{ "ssh-rsa" => "rsa", "ssh-dss" => "dsa", "ssh-ed25519" => "ed25519", "ecdsa-sha2-nistp256" => "ecdsa", "ecdsa-sha2-nistp384" => "ecdsa", "ecdsa-sha2-nistp521" => "ecdsa", }
- SSHFP_TYPES =
{ "rsa" => 1, "dsa" => 2, "ecdsa" => 3, "ed25519" => 4, }
- SSH_CONVERSION =
{"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}
- SSH2_LINE_LENGTH =
+1 (for line wrap ‘/’ character) must be <= 72
70
- VERSION =
"2.0.0"
Instance Attribute Summary collapse
-
#comment ⇒ Object
Returns the value of attribute comment.
-
#directives ⇒ Object
Returns the value of attribute directives.
-
#key_object ⇒ Object
readonly
Returns the value of attribute key_object.
-
#passphrase ⇒ Object
Returns the value of attribute passphrase.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
Class Method Summary collapse
-
.fingerprint ⇒ Object
Fingerprints.
- .format_sshfp_record(hostname, type, key) ⇒ Object
-
.generate(options = {}) ⇒ Object
Generate a new keypair and return an SSHKey object.
-
.md5_fingerprint(key) ⇒ Object
Fingerprints.
-
.sha1_fingerprint(key) ⇒ Object
SHA1 fingerprint for the given SSH key.
-
.sha256_fingerprint(key) ⇒ Object
SHA256 fingerprint for the given SSH key.
-
.ssh_public_key_bits(ssh_public_key) ⇒ Object
Bits.
-
.ssh_public_key_to_ssh2_public_key(ssh_public_key, headers = nil) ⇒ Object
Convert an existing SSH public key to SSH2 (RFC4716) public key.
-
.sshfp(hostname, key) ⇒ Object
SSHFP records for the given SSH key.
-
.valid_ssh_public_key?(ssh_public_key) ⇒ Boolean
Validate an existing SSH public key.
Instance Method Summary collapse
-
#bits ⇒ Object
Determine the length (bits) of the key as an integer.
-
#encrypted_private_key ⇒ Object
Fetch the encrypted RSA/DSA private key using the passphrase provided.
-
#initialize(private_key, options = {}) ⇒ SSHKey
constructor
Create a new SSHKey object.
-
#md5_fingerprint ⇒ Object
(also: #fingerprint)
Fingerprints.
-
#private_key ⇒ Object
(also: #rsa_private_key, #dsa_private_key)
Fetch the RSA/DSA private key.
-
#public_key ⇒ Object
(also: #rsa_public_key, #dsa_public_key)
Fetch the RSA/DSA public key.
-
#randomart ⇒ Object
Randomart.
-
#sha1_fingerprint ⇒ Object
SHA1 fingerprint for the given SSH public key.
-
#sha256_fingerprint ⇒ Object
SHA256 fingerprint for the given SSH public key.
-
#ssh2_public_key(headers = nil) ⇒ Object
SSH2 public key (RFC4716).
-
#ssh_public_key ⇒ Object
SSH public key.
- #sshfp(hostname) ⇒ Object
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
-
:directives<~Array> - Options prefixed to the public key
-
242 243 244 245 246 247 248 249 250 251 252 253 |
# File 'lib/sshkey.rb', line 242 def initialize(private_key, = {}) @passphrase = [:passphrase] @comment = [:comment] || "" self.directives = [:directives] || [] 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
#comment ⇒ Object
Returns the value of attribute comment.
231 232 233 |
# File 'lib/sshkey.rb', line 231 def comment @comment end |
#directives ⇒ Object
Returns the value of attribute directives.
386 387 388 |
# File 'lib/sshkey.rb', line 386 def directives @directives end |
#key_object ⇒ Object (readonly)
Returns the value of attribute key_object.
230 231 232 |
# File 'lib/sshkey.rb', line 230 def key_object @key_object end |
#passphrase ⇒ Object
Returns the value of attribute passphrase.
231 232 233 |
# File 'lib/sshkey.rb', line 231 def passphrase @passphrase end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
230 231 232 |
# File 'lib/sshkey.rb', line 230 def type @type end |
Class Method Details
.fingerprint ⇒ Object
Fingerprints
Accepts either a public or private key
MD5 fingerprint for the given SSH key
103 104 105 106 107 108 109 |
# File 'lib/sshkey.rb', line 103 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 |
.format_sshfp_record(hostname, type, key) ⇒ Object
156 157 158 159 160 161 |
# File 'lib/sshkey.rb', line 156 def format_sshfp_record(hostname, type, key) [[Digest::SHA1, 1], [Digest::SHA256, 2]].map { |f, num| fpr = f.hexdigest(key) "#{hostname} IN SSHFP #{SSHFP_TYPES[type]} #{num} #{fpr}" }.join("\n") 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
-
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/sshkey.rb', line 39 def generate( = {}) type = [:type] || "rsa" # JRuby modulus size must range from 512 to 1024 default_bits = type == "rsa" ? 2048 : 1024 bits = [:bits] || default_bits cipher = OpenSSL::Cipher.new("AES-128-CBC") if [:passphrase] case type.downcase when "rsa" then new(OpenSSL::PKey::RSA.generate(bits).to_pem(cipher, [:passphrase]), ) when "dsa" then new(OpenSSL::PKey::DSA.generate(bits).to_pem(cipher, [:passphrase]), ) 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
96 97 98 99 100 101 102 |
# File 'lib/sshkey.rb', line 96 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
106 107 108 109 110 111 112 |
# File 'lib/sshkey.rb', line 106 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 |
.sha256_fingerprint(key) ⇒ Object
SHA256 fingerprint for the given SSH key
115 116 117 118 119 120 121 |
# File 'lib/sshkey.rb', line 115 def sha256_fingerprint(key) if key.match(/PRIVATE/) new(key).sha256_fingerprint else Base64.encode64(Digest::SHA256.digest(decoded_key(key))).gsub("\n", "") end end |
.ssh_public_key_bits(ssh_public_key) ⇒ Object
Bits
Returns ssh public key bits or false depending on the validity of the public key provided
Parameters
-
ssh_public_key<~String> - “ssh-rsa AAAAB3NzaC1yc2EA.…”
87 88 89 |
# File 'lib/sshkey.rb', line 87 def ssh_public_key_bits(ssh_public_key) unpacked_byte_array( *parse_ssh_public_key(ssh_public_key) ).last.num_bytes * 8 end |
.ssh_public_key_to_ssh2_public_key(ssh_public_key, headers = nil) ⇒ Object
Convert an existing SSH public key to SSH2 (RFC4716) public key
Parameters
-
ssh_public_key<~String> - “ssh-rsa AAAAB3NzaC1yc2EA.…”
-
headers<~Hash> - The Key will be used as the header-tag and the value as the header-value
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/sshkey.rb', line 139 def ssh_public_key_to_ssh2_public_key(ssh_public_key, headers = nil) raise PublicKeyError, "invalid ssh public key" unless SSHKey.valid_ssh_public_key?(ssh_public_key) _source_format, source_key = parse_ssh_public_key(ssh_public_key) # Add a 'Comment' Header Field unless others are explicitly passed in if source_comment = ssh_public_key.split(source_key)[1] headers = {'Comment' => source_comment.strip} if headers.nil? && !source_comment.empty? end header_fields = build_ssh2_headers(headers) ssh2_key = "---- BEGIN SSH2 PUBLIC KEY ----\n" ssh2_key << header_fields unless header_fields.nil? ssh2_key << source_key.scan(/.{1,#{SSH2_LINE_LENGTH}}/).join("\n") ssh2_key << "\n---- END SSH2 PUBLIC KEY ----" end |
.sshfp(hostname, key) ⇒ Object
SSHFP records for the given SSH key
124 125 126 127 128 129 130 131 |
# File 'lib/sshkey.rb', line 124 def sshfp(hostname, key) if key.match(/PRIVATE/) new(key).sshfp hostname else type, encoded_key = parse_ssh_public_key(key) format_sshfp_record(hostname, SSH_TYPES[type], Base64.decode64(encoded_key)) 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.…”
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/sshkey.rb', line 63 def valid_ssh_public_key?(ssh_public_key) ssh_type, encoded_key = parse_ssh_public_key(ssh_public_key) sections = unpacked_byte_array(ssh_type, encoded_key) case ssh_type when "ssh-rsa", "ssh-dss" sections.size == SSH_CONVERSION[SSH_TYPES[ssh_type]].size when "ssh-ed25519" sections.size == 1 # https://tools.ietf.org/id/draft-bjh21-ssh-ed25519-00.html#rfc.section.4 when "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521" sections.size == 2 # https://tools.ietf.org/html/rfc5656#section-3.1 else false end rescue false end |
Instance Method Details
#bits ⇒ Object
Determine the length (bits) of the key as an integer
318 319 320 |
# File 'lib/sshkey.rb', line 318 def bits self.class.ssh_public_key_bits(ssh_public_key) end |
#encrypted_private_key ⇒ Object
Fetch the encrypted RSA/DSA private key using the passphrase provided
If no passphrase is set, returns the unencrypted private key
267 268 269 270 |
# File 'lib/sshkey.rb', line 267 def encrypted_private_key return private_key unless passphrase key_object.to_pem(OpenSSL::Cipher.new("AES-128-CBC"), passphrase) end |
#md5_fingerprint ⇒ Object Also known as: fingerprint
Fingerprints
MD5 fingerprint for the given SSH public key
302 303 304 |
# File 'lib/sshkey.rb', line 302 def md5_fingerprint Digest::MD5.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2') end |
#private_key ⇒ Object 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
258 259 260 |
# File 'lib/sshkey.rb', line 258 def private_key key_object.to_pem end |
#public_key ⇒ Object 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
275 276 277 |
# File 'lib/sshkey.rb', line 275 def public_key key_object.public_key.to_pem end |
#randomart ⇒ Object
Randomart
Generate OpenSSH compatible ASCII art fingerprints See www.opensource.apple.com/source/OpenSSH/OpenSSH-175/openssh/key.c (key_fingerprint_randomart function)
Example: –[ RSA 2048]—- |o+ o.. | |..+.o | | ooo | |.++. o | |o
+ S | |.. + o . | | . + . | | . . | | Eo. | -----------------
339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
# File 'lib/sshkey.rb', line 339 def randomart fieldsize_x = 17 fieldsize_y = 9 x = fieldsize_x / 2 y = fieldsize_y / 2 raw_digest = Digest::MD5.digest(ssh_public_key_conversion) num_bytes = raw_digest.bytesize field = Array.new(fieldsize_x) { Array.new(fieldsize_y) {0} } raw_digest.bytes.each do |byte| 4.times do x += (byte & 0x1 != 0) ? 1 : -1 y += (byte & 0x2 != 0) ? 1 : -1 x = [[x, 0].max, fieldsize_x - 1].min y = [[y, 0].max, fieldsize_y - 1].min field[x][y] += 1 if (field[x][y] < num_bytes - 2) byte >>= 2 end end field[fieldsize_x / 2][fieldsize_y / 2] = num_bytes - 1 field[x][y] = num_bytes augmentation_string = " .o+=*BOX@%&#/^SE" output = "+--#{sprintf("[%4s %4u]", type.upcase, bits)}----+\n" fieldsize_y.times do |y| output << "|" fieldsize_x.times do |x| output << augmentation_string[[field[x][y], num_bytes].min] end output << "|" output << "\n" end output << "+#{"-" * fieldsize_x}+" output end |
#sha1_fingerprint ⇒ Object
SHA1 fingerprint for the given SSH public key
308 309 310 |
# File 'lib/sshkey.rb', line 308 def sha1_fingerprint Digest::SHA1.hexdigest(ssh_public_key_conversion).gsub(/(.{2})(?=.)/, '\1:\2') end |
#sha256_fingerprint ⇒ Object
SHA256 fingerprint for the given SSH public key
313 314 315 |
# File 'lib/sshkey.rb', line 313 def sha256_fingerprint Base64.encode64(Digest::SHA256.digest(ssh_public_key_conversion)).gsub("\n", "") end |
#ssh2_public_key(headers = nil) ⇒ Object
SSH2 public key (RFC4716)
Parameters
-
headers<~Hash> - Keys will be used as header-tags and values as header-values.
Examples
=> ‘2048-bit RSA created by user@example’ => ‘Private Use Value’
295 296 297 |
# File 'lib/sshkey.rb', line 295 def ssh2_public_key(headers = nil) self.class.ssh_public_key_to_ssh2_public_key(ssh_public_key, headers) end |
#ssh_public_key ⇒ Object
SSH public key
282 283 284 |
# File 'lib/sshkey.rb', line 282 def ssh_public_key [directives.join(",").strip, SSH_TYPES.invert[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip end |
#sshfp(hostname) ⇒ Object
379 380 381 |
# File 'lib/sshkey.rb', line 379 def sshfp(hostname) self.class.format_sshfp_record(hostname, @type, ssh_public_key_conversion) end |