sshkey 1.8.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|