sshkey 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +7 -7
- data/LICENSE +1 -1
- data/README.md +4 -4
- data/lib/sshkey.rb +23 -6
- data/lib/sshkey/version.rb +1 -1
- data/test/sshkey_test.rb +34 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e557b4605e1f00d77de9e7b939f79a5ba9601cdf
|
4
|
+
data.tar.gz: 6764a84f581a544e0adea8958731eb01bd35a550
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd96a9bacc99265e0f211b40a4c0d4a860ed4f6e5ffb7197277f5fdf11800776f6dbf687288b4c2b4bf3829e02273b2dcaa7b5c8fec662fda8b695b6f5861c5c
|
7
|
+
data.tar.gz: 8d68590c6f42a5d3868eaa0c39a8880c8c2666c5f63ac9d53df9cee0cba7f7827a3f3f3343166adecdca88e05de7599b0c7c163327ca12f72f3f04f2a0e0b965
|
data/.travis.yml
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
rvm:
|
4
|
-
- 1.8.7
|
5
|
-
- 1.9.2
|
6
4
|
- 1.9.3
|
7
|
-
- 2.0
|
8
|
-
- 2.1
|
9
|
-
- 2.2
|
5
|
+
- 2.0.0
|
6
|
+
- 2.1.9
|
7
|
+
- 2.2.6
|
8
|
+
- 2.3.3
|
9
|
+
- 2.4.0
|
10
10
|
- ruby-head
|
11
|
-
- ree
|
12
11
|
- jruby
|
13
12
|
- jruby-head
|
14
13
|
|
@@ -17,4 +16,5 @@ matrix:
|
|
17
16
|
- rvm: ruby-head
|
18
17
|
- rvm: jruby-head
|
19
18
|
|
20
|
-
sudo:
|
19
|
+
sudo: required
|
20
|
+
dist: trusty
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -6,7 +6,7 @@ Generate private and public SSH keys (RSA and DSA supported) using pure Ruby.
|
|
6
6
|
|
7
7
|
## Requirements
|
8
8
|
|
9
|
-
Tested / supported on CRuby 1.
|
9
|
+
Tested / supported on CRuby 1.9.3+ and JRuby.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -61,11 +61,11 @@ k.ssh_public_key
|
|
61
61
|
|
62
62
|
#### Encryption
|
63
63
|
|
64
|
-
If a
|
64
|
+
If a passphrase is set when a key is generated or by setting the `passphrase` accessor, you can
|
65
65
|
fetch the encrypted version of the private key.
|
66
66
|
|
67
67
|
```ruby
|
68
|
-
k.
|
68
|
+
k.passphrase = "foo"
|
69
69
|
# => "foo"
|
70
70
|
|
71
71
|
k.encrypted_private_key
|
@@ -211,4 +211,4 @@ SSHKey.ssh_public_key_to_ssh2_public_key "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ
|
|
211
211
|
|
212
212
|
## Copyright
|
213
213
|
|
214
|
-
Copyright (c) 2011-
|
214
|
+
Copyright (c) 2011-2016 James Miller
|
data/lib/sshkey.rb
CHANGED
@@ -7,7 +7,14 @@ require 'digest/sha1'
|
|
7
7
|
require 'sshkey/exception'
|
8
8
|
|
9
9
|
class SSHKey
|
10
|
-
SSH_TYPES
|
10
|
+
SSH_TYPES = {
|
11
|
+
"ssh-rsa" => "rsa",
|
12
|
+
"ssh-dss" => "dsa",
|
13
|
+
"ssh-ed25519" => "ed25519",
|
14
|
+
"ecdsa-sha2-nistp256" => "ecdsa",
|
15
|
+
"ecdsa-sha2-nistp384" => "ecdsa",
|
16
|
+
"ecdsa-sha2-nistp521" => "ecdsa",
|
17
|
+
}
|
11
18
|
SSH_CONVERSION = {"rsa" => ["e", "n"], "dsa" => ["p", "q", "g", "pub_key"]}
|
12
19
|
SSH2_LINE_LENGTH = 70 # +1 (for line wrap '/' character) must be <= 72
|
13
20
|
|
@@ -50,7 +57,17 @@ class SSHKey
|
|
50
57
|
#
|
51
58
|
def valid_ssh_public_key?(ssh_public_key)
|
52
59
|
ssh_type, encoded_key = parse_ssh_public_key(ssh_public_key)
|
53
|
-
|
60
|
+
sections = unpacked_byte_array(ssh_type, encoded_key)
|
61
|
+
case ssh_type
|
62
|
+
when "ssh-rsa", "ssh-dss"
|
63
|
+
sections.size == SSH_CONVERSION[SSH_TYPES[ssh_type]].size
|
64
|
+
when "ssh-ed25519"
|
65
|
+
sections.size == 1 && sections[0].num_bytes == 32 # https://tools.ietf.org/id/draft-bjh21-ssh-ed25519-00.html#rfc.section.4
|
66
|
+
when "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521"
|
67
|
+
sections.size == 2 # https://tools.ietf.org/html/rfc5656#section-3.1
|
68
|
+
else
|
69
|
+
false
|
70
|
+
end
|
54
71
|
rescue
|
55
72
|
false
|
56
73
|
end
|
@@ -124,7 +141,7 @@ class SSHKey
|
|
124
141
|
private
|
125
142
|
|
126
143
|
def unpacked_byte_array(ssh_type, encoded_key)
|
127
|
-
prefix = [
|
144
|
+
prefix = [ssh_type.length].pack("N") + ssh_type
|
128
145
|
decoded = Base64.decode64(encoded_key)
|
129
146
|
|
130
147
|
# Base64 decoding is too permissive, so we should validate if encoding is correct
|
@@ -158,7 +175,7 @@ class SSHKey
|
|
158
175
|
|
159
176
|
parsed = public_key.split(" ")
|
160
177
|
parsed.each_with_index do |el, index|
|
161
|
-
return parsed[index..(index+1)] if SSH_TYPES
|
178
|
+
return parsed[index..(index+1)] if SSH_TYPES[el]
|
162
179
|
end
|
163
180
|
raise PublicKeyError, "cannot determine key type"
|
164
181
|
end
|
@@ -231,7 +248,7 @@ class SSHKey
|
|
231
248
|
|
232
249
|
# SSH public key
|
233
250
|
def ssh_public_key
|
234
|
-
[directives.join(",").strip, SSH_TYPES[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
|
251
|
+
[directives.join(",").strip, SSH_TYPES.invert[type], Base64.encode64(ssh_public_key_conversion).gsub("\n", ""), comment].join(" ").strip
|
235
252
|
end
|
236
253
|
|
237
254
|
# SSH2 public key (RFC4716)
|
@@ -344,7 +361,7 @@ class SSHKey
|
|
344
361
|
# For instance, the "ssh-rsa" string is encoded as the following byte array
|
345
362
|
# [0, 0, 0, 7, 's', 's', 'h', '-', 'r', 's', 'a']
|
346
363
|
def ssh_public_key_conversion
|
347
|
-
typestr = SSH_TYPES[type]
|
364
|
+
typestr = SSH_TYPES.invert[type]
|
348
365
|
methods = SSH_CONVERSION[type]
|
349
366
|
pubkey = key_object.public_key
|
350
367
|
methods.inject([7].pack("N") + typestr) do |pubkeystr, m|
|
data/lib/sshkey/version.rb
CHANGED
data/test/sshkey_test.rb
CHANGED
@@ -79,17 +79,28 @@ EOF
|
|
79
79
|
SSH_PUBLIC_KEY2 = 'AAAAB3NzaC1yc2EAAAABIwAAAQEAxl6TpN7uFiY/JZ8qDnD7UrxDP+ABeh2PVg8Du1LEgXNk0+YWCeP5S6oHklqaWeDlbmAs1oHsBwCMAVpMa5tgONOLvz4JgwgkiqQEbKR8ofWJ+LADUElvqRVGmGiNEMLI6GJWeneL4sjmbb8d6U+M53c6iWG0si9XE5m7teBQSsCl0Tk3qMIkQGw5zpJeCXjZ8KpJhIJRYgexFkGgPlYRV+UYIhxpUW90t0Ra5i6JOFYwq98k5S/6SJIZQ/A9F4JNzwLw3eVxZj0yVHWxkGz1+TyELNY1kOyMxnZaqSfGzSQJTrnIXpdweVHuYh1LtOgedRQhCyiELeSMGwio1vRPKw=='
|
80
80
|
SSH_PUBLIC_KEY3 = 'AAAAB3NzaC1kc3MAAACBALyVy5dwVwgL3CxXzsvo8DBh58qArQLBNIPW/f9pptmy7jD5QXzOw+12w0/z4lZ86ncoVutRMf44OABcX9ovhRl+luxB7jjpkVXy/p2ZaqPbeyTQUtdTmXa2y4n053Jd61VeMG+iLP7+viT+Ib96y9aVUYQfCrl5heBDUZ9cAFjdAAAAFQDFXnO7JJpFKwkeoor4GWGHtz0D2QAAAIEAqel0RUBO0MY5b3DZ69J/mRzUifN1O6twk4er2ph0JpryuUwZohLpcVZwqoGWmPQy/ZHmV1b3RtT9GWUa+HUqKdMhFVOx/iq1khVfLi83whjMMvXj3ecqd0yzGxGHnSsjVKefa2ywCLHrh4nlUVIaXI5gQpgMyVbMcromDe1WZzoAAACBAIwTRPAEcroqOzaebiVspFcmsXxDQ4wXQZQdho1ExW6FKS8s7/6pItmZYXTvJDwLXgq2/iK1fRRcKk2PJEaSuJR7WeNGsJKfWmQ2UbOhqA3wWLDazIZtcMKjFzD0hM4E8qgjHjMvKDE6WgT6SFP+tqx3nnh7pJWwsbGjSMQexpyR'
|
81
81
|
|
82
|
+
SSH_PUBLIC_KEY_ED25519 = 'AAAAC3NzaC1lZDI1NTE5AAAAIBrNsRCISAtKXV5OVxqV6unVcdis5Uh3oiC6B7CMB7HQ'
|
83
|
+
SSH_PUBLIC_KEY_ECDSA_256 = 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHJFDZ5qymZfIzoJcxYeu3C9HjJ08QAbqR28C2zSMLwcb3ZzWdRApnj6wEgRvizsBmr9zyPKb2u5Rp0vjJtQcZo='
|
84
|
+
SSH_PUBLIC_KEY_ECDSA_384 = 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBP+GtUCOR8aW7xTtpkbJS0qqNZ98PgbUNtTFhE+Oe+khgoFMX+o0JG5bckVuvtkRl8dr+63kUK0QPTtzP9O5yixB9CYnB8CgCgYo1FCXZuJIImf12wW5nWKglrCH4kV1Qg=='
|
85
|
+
SSH_PUBLIC_KEY_ECDSA_521 = 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBACsunidnIZ77AjCHSDp/xknLGDW3M0Ia7nxLdImmp0XGbxtbwYm2ga5XUzV9dMO9wF9ICC3OuH6g9DtGOBNPru1PwFDjaPISGgm0vniEzWazLsvjJVLThOA3VyYLxmtjm0WfS+/DfxgWVS6oeCTnDjjoVVpwU/fDbUbYPPRZI84/hOGNA=='
|
86
|
+
|
82
87
|
KEY1_MD5_FINGERPRINT = "2a:89:84:c9:29:05:d1:f8:49:79:1c:ba:73:99:eb:af"
|
83
88
|
KEY2_MD5_FINGERPRINT = "3c:af:74:87:cc:cc:a1:12:05:1a:09:b7:7b:ce:ed:ce"
|
84
89
|
KEY3_MD5_FINGERPRINT = "14:f6:6a:12:96:be:44:32:e6:3c:77:43:94:52:f5:7a"
|
90
|
+
ED25519_MD5_FINGERPRINT = "6f:1a:8a:c1:4f:13:5c:36:6e:3f:be:eb:49:3b:8e:3e"
|
91
|
+
ECDSA_256_MD5_FINGERPRINT = "d9:3a:7f:de:b2:65:04:ac:62:05:1a:1e:97:e9:2b:9d"
|
85
92
|
|
86
93
|
KEY1_SHA1_FINGERPRINT = "e4:f9:79:f2:fe:d6:be:2d:ef:2e:c2:fa:aa:f8:b0:17:34:fe:0d:c0"
|
87
94
|
KEY2_SHA1_FINGERPRINT = "9a:52:78:2b:6b:cb:39:b7:85:ed:90:8a:28:62:aa:b3:98:88:e6:07"
|
88
95
|
KEY3_SHA1_FINGERPRINT = "15:68:c6:72:ac:18:d1:fc:ab:a2:b7:b5:8c:d1:fe:8f:b9:ae:a9:47"
|
96
|
+
ED25519_SHA1_FINGERPRINT = "57:41:7c:d0:e2:53:28:87:7e:87:53:d4:69:ef:ef:63:ec:c0:0e:5e"
|
97
|
+
ECDSA_256_SHA1_FINGERPRINT = "94:e8:92:2b:1b:ec:49:de:ff:85:ea:6e:10:d6:8d:87:7a:67:40:ee"
|
89
98
|
|
90
99
|
KEY1_SHA256_FINGERPRINT = "js3llFehloxCfsVuDw5xu3NtS9AOAxcXY8WL6vkDIts="
|
91
100
|
KEY2_SHA256_FINGERPRINT = "23f/6U/LdxIFx1CQFKHylw76n+LIHYoY4nRxKcFoos4="
|
92
101
|
KEY3_SHA256_FINGERPRINT = "mPqEPQlOPGORrTJrU17sPax1jOqeutZja6MOsFIca+8="
|
102
|
+
ED25519_SHA256_FINGERPRINT = "gyzHUKl1eO8Bk1Cvn4joRgxRlXo1+1HJ3Vho/hAtKEg="
|
103
|
+
ECDSA_256_SHA256_FINGERPRINT = "ncy2crhoL44R58GCZPQ5chPRrjlQKKgu07FDNelDmdk="
|
93
104
|
|
94
105
|
KEY1_RANDOMART = <<-EOF.rstrip
|
95
106
|
+--[ RSA 2048]----+
|
@@ -311,17 +322,34 @@ EOF
|
|
311
322
|
assert !SSHKey.valid_ssh_public_key?(invalid5)
|
312
323
|
end
|
313
324
|
|
325
|
+
def test_ssh_public_key_validation_elliptic
|
326
|
+
assert SSHKey.valid_ssh_public_key?("ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519} me@example.com")
|
327
|
+
assert SSHKey.valid_ssh_public_key?("ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_256}")
|
328
|
+
assert SSHKey.valid_ssh_public_key?("ecdsa-sha2-nistp384 #{SSH_PUBLIC_KEY_ECDSA_384} me@example.com")
|
329
|
+
assert SSHKey.valid_ssh_public_key?(%Q{from="trusted.eng.cam.ac.uk",no-port-forwarding,no-pty ecdsa-sha2-nistp521 #{SSH_PUBLIC_KEY_ECDSA_521} me@example.com})
|
330
|
+
|
331
|
+
assert !SSHKey.valid_ssh_public_key?("ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519}= me@example.com") # bad base64
|
332
|
+
assert !SSHKey.valid_ssh_public_key?("ssh-ed25519 #{SSH_PUBLIC_KEY_ECDSA_384} me@example.com") # mismatched key format
|
333
|
+
assert !SSHKey.valid_ssh_public_key?("ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_384} me@example.com") # mismatched key format
|
334
|
+
assert !SSHKey.valid_ssh_public_key?("ssh-ed25519 asdf me@example.com") # gibberish key data
|
335
|
+
assert !SSHKey.valid_ssh_public_key?("ecdsa-sha2-nistp256 asdf me@example.com") # gibberish key data
|
336
|
+
end
|
337
|
+
|
314
338
|
def test_ssh_public_key_validation_with_newlines
|
315
339
|
expected1 = "ssh-rsa #{SSH_PUBLIC_KEY1}\n"
|
340
|
+
expected2 = "ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519} me@example.com\n"
|
316
341
|
invalid1 = "ssh-rsa #{SSH_PUBLIC_KEY1}\nme@example.com"
|
317
342
|
invalid2 = "ssh-rsa #{SSH_PUBLIC_KEY1}\n me@example.com"
|
318
343
|
invalid3 = "ssh-rsa #{SSH_PUBLIC_KEY1} \nme@example.com"
|
344
|
+
invalid4 = "ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_256}\nme@example.com"
|
319
345
|
|
320
346
|
assert SSHKey.valid_ssh_public_key?(expected1)
|
347
|
+
assert SSHKey.valid_ssh_public_key?(expected2)
|
321
348
|
|
322
349
|
assert !SSHKey.valid_ssh_public_key?(invalid1)
|
323
350
|
assert !SSHKey.valid_ssh_public_key?(invalid2)
|
324
351
|
assert !SSHKey.valid_ssh_public_key?(invalid3)
|
352
|
+
assert !SSHKey.valid_ssh_public_key?(invalid4)
|
325
353
|
end
|
326
354
|
|
327
355
|
def test_ssh_public_key_bits
|
@@ -389,6 +417,8 @@ end
|
|
389
417
|
assert_equal KEY2_MD5_FINGERPRINT, SSHKey.md5_fingerprint("ssh-rsa #{SSH_PUBLIC_KEY2} me@me.com")
|
390
418
|
assert_equal KEY3_MD5_FINGERPRINT, SSHKey.md5_fingerprint(SSH_PRIVATE_KEY3)
|
391
419
|
assert_equal KEY3_MD5_FINGERPRINT, SSHKey.md5_fingerprint("ssh-dss #{SSH_PUBLIC_KEY3}")
|
420
|
+
assert_equal ED25519_MD5_FINGERPRINT, SSHKey.md5_fingerprint("ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519}")
|
421
|
+
assert_equal ECDSA_256_MD5_FINGERPRINT, SSHKey.md5_fingerprint("ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_256} me@me.com")
|
392
422
|
|
393
423
|
assert_equal KEY1_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint(SSH_PRIVATE_KEY1)
|
394
424
|
assert_equal KEY1_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint("ssh-rsa #{SSH_PUBLIC_KEY1}")
|
@@ -396,6 +426,8 @@ end
|
|
396
426
|
assert_equal KEY2_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint("ssh-rsa #{SSH_PUBLIC_KEY2} me@me.com")
|
397
427
|
assert_equal KEY3_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint(SSH_PRIVATE_KEY3)
|
398
428
|
assert_equal KEY3_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint("ssh-dss #{SSH_PUBLIC_KEY3}")
|
429
|
+
assert_equal ED25519_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint("ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519}")
|
430
|
+
assert_equal ECDSA_256_SHA1_FINGERPRINT, SSHKey.sha1_fingerprint("ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_256} me@me.com")
|
399
431
|
|
400
432
|
assert_equal KEY1_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint(SSH_PRIVATE_KEY1)
|
401
433
|
assert_equal KEY1_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint("ssh-rsa #{SSH_PUBLIC_KEY1}")
|
@@ -403,6 +435,8 @@ end
|
|
403
435
|
assert_equal KEY2_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint("ssh-rsa #{SSH_PUBLIC_KEY2} me@me.com")
|
404
436
|
assert_equal KEY3_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint(SSH_PRIVATE_KEY3)
|
405
437
|
assert_equal KEY3_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint("ssh-dss #{SSH_PUBLIC_KEY3}")
|
438
|
+
assert_equal ED25519_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint("ssh-ed25519 #{SSH_PUBLIC_KEY_ED25519}")
|
439
|
+
assert_equal ECDSA_256_SHA256_FINGERPRINT, SSHKey.sha256_fingerprint("ecdsa-sha2-nistp256 #{SSH_PUBLIC_KEY_ECDSA_256} me@me.com")
|
406
440
|
end
|
407
441
|
|
408
442
|
def test_bits
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sshkey
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Miller
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
78
|
rubyforge_project: sshkey
|
79
|
-
rubygems_version: 2.
|
79
|
+
rubygems_version: 2.6.8
|
80
80
|
signing_key:
|
81
81
|
specification_version: 4
|
82
82
|
summary: SSH private/public key generator in Ruby
|