putty-key 1.0.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 +7 -0
- checksums.yaml.gz.sig +3 -0
- data.tar.gz.sig +1 -0
- data/.yardopts +7 -0
- data/CHANGES.md +4 -0
- data/Gemfile +14 -0
- data/LICENSE +19 -0
- data/README.md +137 -0
- data/Rakefile +110 -0
- data/lib/putty/key.rb +25 -0
- data/lib/putty/key/error.rb +26 -0
- data/lib/putty/key/openssl.rb +182 -0
- data/lib/putty/key/ppk.rb +374 -0
- data/lib/putty/key/util.rb +128 -0
- data/lib/putty/key/version.rb +6 -0
- data/putty-key.gemspec +29 -0
- data/test/fixtures/dss-1024-encrypted.ppk +17 -0
- data/test/fixtures/dss-1024.pem +12 -0
- data/test/fixtures/dss-1024.ppk +17 -0
- data/test/fixtures/ecdsa-secp256k1.pem +5 -0
- data/test/fixtures/ecdsa-sha2-nistp256-encrypted.ppk +10 -0
- data/test/fixtures/ecdsa-sha2-nistp256.pem +5 -0
- data/test/fixtures/ecdsa-sha2-nistp256.ppk +10 -0
- data/test/fixtures/ecdsa-sha2-nistp384-encrypted.ppk +11 -0
- data/test/fixtures/ecdsa-sha2-nistp384.pem +6 -0
- data/test/fixtures/ecdsa-sha2-nistp384.ppk +11 -0
- data/test/fixtures/ecdsa-sha2-nistp521-encrypted.ppk +12 -0
- data/test/fixtures/ecdsa-sha2-nistp521.pem +7 -0
- data/test/fixtures/ecdsa-sha2-nistp521.ppk +12 -0
- data/test/fixtures/rsa-2048-encrypted.ppk +26 -0
- data/test/fixtures/rsa-2048.pem +27 -0
- data/test/fixtures/rsa-2048.ppk +26 -0
- data/test/fixtures/test-blank-comment.ppk +11 -0
- data/test/fixtures/test-encrypted.ppk +11 -0
- data/test/fixtures/test-invalid-blob-lines.ppk +11 -0
- data/test/fixtures/test-invalid-encryption-type.ppk +11 -0
- data/test/fixtures/test-invalid-format-1.ppk +11 -0
- data/test/fixtures/test-invalid-format-3.ppk +11 -0
- data/test/fixtures/test-invalid-private-mac.ppk +11 -0
- data/test/fixtures/test-truncated.ppk +10 -0
- data/test/fixtures/test-unix-line-endings.ppk +11 -0
- data/test/fixtures/test.ppk +11 -0
- data/test/openssl_test.rb +252 -0
- data/test/ppk_test.rb +247 -0
- data/test/test_helper.rb +81 -0
- data/test/util_test.rb +180 -0
- data/test/version_test.rb +7 -0
- metadata +124 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2f25fb48a4e37551a2660d1d4fd3147d3e3170ac
|
4
|
+
data.tar.gz: 8e4bd0d38dbf9a5a56e1e10fdf7db11cd2f7a055
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 54e123b54417b3b2dcb280c6fccb054ae7f4a1d2bb4af8924c33f12886cdea5dcf3d998dbe48c435840578ebed83f471742b45fea808334ebd0010047b1f45bd
|
7
|
+
data.tar.gz: cd8f021629d480f19e4c3b7f628b1b602c318e69ce1316b6619dd6f94b42fc3dd969d2a53b8cbc4e6df1cc655a54dc83512cc74499804fa20fc3cbc26eb0e33b
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
MI�,�+�^������i�+-�X�6�����&9���W�1=�@�W�V�a��q3#R���+?3�ɐ�A�o�y�Zۄ�毡��(��lvq'fզ��C�y�ĉL��*&{�����Ic���O��M���{{ǥ��>z\}�,��?R '��"�y[��`kf
|
data/.yardopts
ADDED
data/CHANGES.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem 'rake', '~> 10.5'
|
7
|
+
gem 'git', '~> 1.2', require: false
|
8
|
+
end
|
9
|
+
|
10
|
+
group :test do
|
11
|
+
gem 'minitest', '~> 5.8'
|
12
|
+
gem 'simplecov', '~> 0.11', require: false
|
13
|
+
gem 'coveralls', '~> 0.8', require: false
|
14
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2016 Philip Ross
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
8
|
+
so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# PuTTY::Key #
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/putty-key) [](https://travis-ci.org/philr/putty-key) [](https://coveralls.io/r/philr/putty-key?branch=master)
|
4
|
+
|
5
|
+
PuTTY::Key is a pure-Ruby implementation of the PuTTY private key (ppk) format,
|
6
|
+
handling reading and writing .ppk files. It includes a refinement to Ruby's
|
7
|
+
OpenSSL library to add support for converting DSA, EC and RSA private keys to
|
8
|
+
and from PuTTY private key files. This allows OpenSSH ecdsa, ssh-dss and ssh-rsa
|
9
|
+
private keys to be converted to and from PuTTY's private key format.
|
10
|
+
|
11
|
+
|
12
|
+
## Installation ##
|
13
|
+
|
14
|
+
To install the PuTTY::Key gem, run the following command:
|
15
|
+
|
16
|
+
```bash
|
17
|
+
gem install putty-key
|
18
|
+
```
|
19
|
+
|
20
|
+
To add PuTTY::Key as a Bundler dependency, add the following line to your
|
21
|
+
`Gemfile`:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'putty-key'
|
25
|
+
```
|
26
|
+
|
27
|
+
## Compatibility ##
|
28
|
+
|
29
|
+
PuTTY::Key is compatible with Ruby MRI 2.1.0+ and Rubinius 2.5.4+ (provided the
|
30
|
+
OpenSSL standard library is available).
|
31
|
+
|
32
|
+
JRuby will be supported (DSA/DSS and RSA keys only) once jruby-openssl pull
|
33
|
+
requests [#82](https://github.com/jruby/jruby-openssl/pull/82) and
|
34
|
+
[#83](https://github.com/jruby/jruby-openssl/pull/83) have been released.
|
35
|
+
|
36
|
+
|
37
|
+
## Usage ##
|
38
|
+
|
39
|
+
To use PuTTY::Key, it must first be loaded with:
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
require 'putty/key'
|
43
|
+
```
|
44
|
+
|
45
|
+
The included [refinement](http://ruby-doc.org/core-2.3.0/doc/syntax/refinements_rdoc.html)
|
46
|
+
to Ruby's OpenSSL library can then either be activated in the lexical scope
|
47
|
+
(file, class or module) where it will be used with:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
using PuTTY::Key
|
51
|
+
```
|
52
|
+
|
53
|
+
or installed globally by calling:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
PuTTY::Key.global_install
|
57
|
+
```
|
58
|
+
|
59
|
+
Note that Rubinius (as of version 3.22) does not support refinements, so the
|
60
|
+
global installation approach is required.
|
61
|
+
|
62
|
+
JRuby (as of version 9.0.5.0) includes support for refinements, but there are
|
63
|
+
still outstanding issues. The global installation approach is preferable on
|
64
|
+
JRuby.
|
65
|
+
|
66
|
+
The following sections give examples of how PuTTY::Key can be used.
|
67
|
+
|
68
|
+
|
69
|
+
### Converting a .pem formatted key file to an unencrypted .ppk file ###
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
require 'openssl'
|
73
|
+
require 'putty/key'
|
74
|
+
using PuTTY::Key # or PuTTY::Key.global_install
|
75
|
+
|
76
|
+
pem = File.read('key.pem', mode: 'rb')
|
77
|
+
pkey = OpenSSL::PKey.read(pem)
|
78
|
+
ppk = pkey.to_ppk
|
79
|
+
ppk.comment = 'Optional comment'
|
80
|
+
ppk.save('key.ppk')
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
### Generating a new RSA key and saving it as an encrypted .ppk file ###
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require 'openssl'
|
88
|
+
require 'putty/key'
|
89
|
+
using PuTTY::Key # or PuTTY::Key.global_install
|
90
|
+
|
91
|
+
rsa = OpenSSL::PKey::RSA.generate(2048)
|
92
|
+
ppk = rsa.to_ppk
|
93
|
+
ppk.comment = 'RSA 2048'
|
94
|
+
ppk.save('rsa.ppk', 'Passphrase for encryption')
|
95
|
+
```
|
96
|
+
|
97
|
+
|
98
|
+
### Converting an unencrypted .ppk file to .pem format ###
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
require 'openssl'
|
102
|
+
require 'putty/key'
|
103
|
+
using PuTTY::Key # or PuTTY::Key.global_install
|
104
|
+
|
105
|
+
ppk = PuTTY::Key::PPK.new('key.ppk')
|
106
|
+
pkey = OpenSSL::PKey.from_ppk(ppk)
|
107
|
+
pem = pkey.to_pem
|
108
|
+
File.write('key.pem', pem, mode: 'wb')
|
109
|
+
```
|
110
|
+
|
111
|
+
|
112
|
+
### Decrypting a .ppk file and re-saving it without encryption ###
|
113
|
+
|
114
|
+
```ruby
|
115
|
+
require 'putty/key'
|
116
|
+
|
117
|
+
ppk = PuTTY::Key::PPK.new('rsa.ppk', 'Passphrase for encryption')
|
118
|
+
ppk.save('rsa-plain.ppk')
|
119
|
+
```
|
120
|
+
|
121
|
+
|
122
|
+
## API Documentation ##
|
123
|
+
|
124
|
+
API documentation for PuTTY::Key is available on
|
125
|
+
[RubyDoc.info](http://www.rubydoc.info/gems/putty-key).
|
126
|
+
|
127
|
+
|
128
|
+
## License ##
|
129
|
+
|
130
|
+
PuTTY::Key is distributed under the terms of the MIT license. A copy of this
|
131
|
+
license can be found in the included LICENSE file.
|
132
|
+
|
133
|
+
|
134
|
+
## GitHub Project ##
|
135
|
+
|
136
|
+
Source code, release information and the issue tracker can be found on the
|
137
|
+
[PuTTY::Key GitHub project page](https://github.com/philr/putty-key).
|
data/Rakefile
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
BASE_DIR = File.expand_path(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
task :default => :test
|
9
|
+
|
10
|
+
spec = eval(File.read('putty-key.gemspec'))
|
11
|
+
|
12
|
+
# Attempt to find the private key and return a spec with added options for
|
13
|
+
# signing the gem if found.
|
14
|
+
def add_signing_key(spec)
|
15
|
+
private_key_path = File.expand_path(File.join(BASE_DIR, '..', 'key', 'gem-private_key.pem'))
|
16
|
+
|
17
|
+
if File.exist?(private_key_path)
|
18
|
+
spec = spec.clone
|
19
|
+
spec.signing_key = private_key_path
|
20
|
+
spec.cert_chain = [File.join(BASE_DIR, 'gem-public_cert.pem')]
|
21
|
+
else
|
22
|
+
puts 'WARNING: Private key not found. Not signing gem file.'
|
23
|
+
end
|
24
|
+
|
25
|
+
spec
|
26
|
+
end
|
27
|
+
|
28
|
+
package_task = Gem::PackageTask.new(add_signing_key(spec)) do
|
29
|
+
end
|
30
|
+
|
31
|
+
# Ensure files are world-readable before packaging.
|
32
|
+
Rake::Task[package_task.package_dir_path].enhance do
|
33
|
+
recurse_chmod(package_task.package_dir_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def recurse_chmod(dir)
|
37
|
+
File.chmod(0755, dir)
|
38
|
+
|
39
|
+
Dir.entries(dir).each do |entry|
|
40
|
+
if entry != '.' && entry != '..'
|
41
|
+
path = File.join(dir, entry)
|
42
|
+
if File.directory?(path)
|
43
|
+
recurse_chmod(path)
|
44
|
+
else
|
45
|
+
File.chmod(0644, path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
desc 'Create a tag for the current version'
|
52
|
+
task :tag do
|
53
|
+
require 'git'
|
54
|
+
g = Git.init(BASE_DIR)
|
55
|
+
g.add_tag("v#{spec.version}", annotate: true, message: "Tagging v#{spec.version}")
|
56
|
+
end
|
57
|
+
|
58
|
+
def define_test_task(type, test_coverage)
|
59
|
+
type = type.to_s
|
60
|
+
|
61
|
+
env_task = "test:env:#{type}"
|
62
|
+
Rake::Task::define_task(env_task) do
|
63
|
+
ENV['TEST_COVERAGE'] = test_coverage ? '1' : '0'
|
64
|
+
ENV['TEST_TYPE'] = type
|
65
|
+
end
|
66
|
+
|
67
|
+
test_task = "test:#{type}"
|
68
|
+
Rake::TestTask.new(test_task) do |t|
|
69
|
+
t.libs = [File.join(BASE_DIR, 'test')]
|
70
|
+
t.pattern = File.join(BASE_DIR, 'test', '**', '*_test.rb')
|
71
|
+
t.warning = true
|
72
|
+
end
|
73
|
+
|
74
|
+
Rake::Task[test_task].enhance([env_task])
|
75
|
+
end
|
76
|
+
|
77
|
+
# JRuby 9.0.5.0 doesn't handle refinements correctly.
|
78
|
+
if RUBY_ENGINE == 'jruby'
|
79
|
+
# Don't run coverage tests on JRuby due to inaccurate results.
|
80
|
+
TEST_COVERAGE = false
|
81
|
+
|
82
|
+
task 'test:refinement' do
|
83
|
+
puts 'Skipping refinement tests on JRuby'
|
84
|
+
end
|
85
|
+
elsif !respond_to?(:using, true)
|
86
|
+
# Don't run coverage tests on platforms that don't support refinements, since
|
87
|
+
# it won't be possible to get complete coverage.
|
88
|
+
TEST_COVERAGE = false
|
89
|
+
|
90
|
+
task 'test:refinement' do
|
91
|
+
puts "Skipping refinement tests because #{RUBY_DESCRIPTION} lacks support for refinements"
|
92
|
+
end
|
93
|
+
else
|
94
|
+
TEST_COVERAGE = true
|
95
|
+
define_test_task(:refinement, TEST_COVERAGE)
|
96
|
+
end
|
97
|
+
|
98
|
+
define_test_task(:global, TEST_COVERAGE)
|
99
|
+
|
100
|
+
require 'coveralls/rake/task'
|
101
|
+
Coveralls::RakeTask.new
|
102
|
+
|
103
|
+
desc 'Remove coverage results'
|
104
|
+
task :clean_coverage do
|
105
|
+
FileUtils.rm_f(File.join(BASE_DIR, 'coverage', '.resultset.json'))
|
106
|
+
end
|
107
|
+
|
108
|
+
desc 'Run tests using the refinement, then with the global install'
|
109
|
+
task :test => [:clean_coverage, 'test:refinement', 'test:global'] + (TEST_COVERAGE ? ['coveralls:push'] : []) do
|
110
|
+
end
|
data/lib/putty/key.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module PuTTY
|
2
|
+
# PuTTY::Key is a pure-Ruby implementation of the PuTTY private key (ppk)
|
3
|
+
# format, handling reading and writing .ppk files. It includes a refinement to
|
4
|
+
# Ruby's OpenSSL library to add support for converting DSA, EC and RSA private
|
5
|
+
# keys to and from PuTTY private key files. This allows OpenSSH ecdsa, ssh-dss
|
6
|
+
# and ssh-rsa private keys to be converted to and from PuTTY's private key
|
7
|
+
# format.
|
8
|
+
module Key
|
9
|
+
|
10
|
+
# Makes the refinements available in PuTTY::Key available globally. After
|
11
|
+
# calling {global_install}, it is no longer necessary to include
|
12
|
+
# `using PuTTY::Key` when using the `to_ppk` and `from_ppk` methods added to
|
13
|
+
# `OpenSSL::PKey`.
|
14
|
+
def self.global_install
|
15
|
+
::PuTTY::Key::OpenSSL.global_install
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'putty/key/version'
|
21
|
+
require 'putty/key/error'
|
22
|
+
require 'putty/key/util'
|
23
|
+
require 'putty/key/ppk'
|
24
|
+
require 'putty/key/openssl'
|
25
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module PuTTY
|
2
|
+
module Key
|
3
|
+
# Base class for all the error classes included in PuTTY::Key.
|
4
|
+
class Error < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
# Indicates that an error was encountered in a .ppk file that is being
|
8
|
+
# read or converted to another format.
|
9
|
+
class FormatError < Error
|
10
|
+
end
|
11
|
+
|
12
|
+
# Indicates that an operation cannot be performed with the current state
|
13
|
+
# of the receiver.
|
14
|
+
class InvalidStateError < Error
|
15
|
+
end
|
16
|
+
|
17
|
+
# Indicates that the specified elliptic curve is not supported.
|
18
|
+
class UnsupportedCurveError < Error
|
19
|
+
end
|
20
|
+
|
21
|
+
# Indicates that a nil value has been encountered.
|
22
|
+
class NilValueError < Error
|
23
|
+
end
|
24
|
+
private_constant :NilValueError
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module PuTTY
|
4
|
+
module Key
|
5
|
+
module OpenSSL
|
6
|
+
# {OpenSSL::PKey} classes to be refined.
|
7
|
+
PKEY_CLASSES = Hash[%i(DSA EC RSA).map {|c| [c, ::OpenSSL::PKey.const_get(c)] rescue nil }.compact]
|
8
|
+
private_constant :PKEY_CLASSES
|
9
|
+
|
10
|
+
# Mapping from SSH curve names to their equivalent OpenSSL names.
|
11
|
+
OPENSSL_CURVES = {
|
12
|
+
'nistp256' => 'prime256v1',
|
13
|
+
'nistp384' => 'secp384r1',
|
14
|
+
'nistp521' => 'secp521r1'
|
15
|
+
}
|
16
|
+
private_constant :OPENSSL_CURVES
|
17
|
+
|
18
|
+
# Mapping from OpenSSL curve names to their equivalent SSH names.
|
19
|
+
SSH_CURVES = OPENSSL_CURVES.invert
|
20
|
+
private_constant :SSH_CURVES
|
21
|
+
|
22
|
+
# The {ClassMethods} module is used to extend `OpenSSL::PKey` when
|
23
|
+
# using the PuTTY::Key refinement or calling {PuTTY::Key.global_install}.
|
24
|
+
# This adds a `from_ppk` class method to `OpenSSL::PKey`.
|
25
|
+
#
|
26
|
+
module ClassMethods
|
27
|
+
# Creates a new `OpenSSL::PKey` from a PuTTY private key (instance of
|
28
|
+
# {PPK}).
|
29
|
+
#
|
30
|
+
# This method is called using `OpenSSL::PKey.from_ppk(ppk)`.
|
31
|
+
#
|
32
|
+
# PuTTY keys using the algorithms `ssh-dss`, `ssh-rsa`,
|
33
|
+
# `ecdsa-sha2-nistp256`, `ecdsa-sha2-nistp384` and `ecdsa-sha2-nistp521`
|
34
|
+
# are supported.
|
35
|
+
#
|
36
|
+
# @return [Object] An instance of either `OpenSSL::PKey::DSA`,
|
37
|
+
# `OpenSSL::PKey::RSA` or `OpenSSL::PKey::EC` depending on the
|
38
|
+
# algorithm of `ppk`.
|
39
|
+
#
|
40
|
+
# @raise [ArgumentError] If `ppk` is `nil`.
|
41
|
+
# @raise [ArgumentError] If the algorithm of `ppk` is not supported.
|
42
|
+
def from_ppk(ppk)
|
43
|
+
raise ArgumentError, 'ppk must not be nil' unless ppk
|
44
|
+
|
45
|
+
case ppk.algorithm
|
46
|
+
when 'ssh-dss'
|
47
|
+
::OpenSSL::PKey::DSA.new.tap do |pkey|
|
48
|
+
_, pkey.p, pkey.q, pkey.g, pkey.pub_key = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint, :mpint, :mpint)
|
49
|
+
pkey.priv_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
|
50
|
+
end
|
51
|
+
when 'ssh-rsa'
|
52
|
+
::OpenSSL::PKey::RSA.new.tap do |pkey|
|
53
|
+
_, pkey.e, pkey.n = Util.ssh_unpack(ppk.public_blob, :string, :mpint, :mpint)
|
54
|
+
pkey.d, pkey.p, pkey.q, pkey.iqmp = Util.ssh_unpack(ppk.private_blob, :mpint, :mpint, :mpint, :mpint)
|
55
|
+
pkey.dmp1 = pkey.d % (pkey.p - 1)
|
56
|
+
pkey.dmq1 = pkey.d % (pkey.q - 1)
|
57
|
+
end
|
58
|
+
when /\Aecdsa-sha2-(nistp(?:256|384|521))\z/
|
59
|
+
curve = OPENSSL_CURVES[$1]
|
60
|
+
|
61
|
+
# jruby-openssl doesn't include an EC class (version 0.9.16)
|
62
|
+
ec_class = (::OpenSSL::PKey::EC rescue raise ArgumentError, "Unsupported algorithm: #{ppk.algorithm}")
|
63
|
+
|
64
|
+
ec_class.new(curve).tap do |pkey|
|
65
|
+
_, _, point = Util.ssh_unpack(ppk.public_blob, :string, :string, :mpint)
|
66
|
+
pkey.public_key = ::OpenSSL::PKey::EC::Point.new(pkey.group, point)
|
67
|
+
pkey.private_key = Util.ssh_unpack(ppk.private_blob, :mpint).first
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise ArgumentError, "Unsupported algorithm: #{ppk.algorithm}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The {DSA} module is included into `OpenSSL::PKey::DSA` when using the
|
76
|
+
# PuTTY::Key refinement or calling {PuTTY::Key.global_install}. This adds
|
77
|
+
# a `to_ppk` instance method to `OpenSSL::PKey::DSA`.
|
78
|
+
module DSA
|
79
|
+
# Returns a new {PPK} instance that is equivalent to this key.
|
80
|
+
#
|
81
|
+
# `to_ppk` can be called on instances of `OpenSSL::PKey::DSA`.
|
82
|
+
#
|
83
|
+
# @return [PPK] A new instance of {PPK} that is equivalent to this key.
|
84
|
+
#
|
85
|
+
# @raise [InvalidStateError] If the key has not been initialized.
|
86
|
+
def to_ppk
|
87
|
+
PPK.new.tap do |ppk|
|
88
|
+
ppk.algorithm = 'ssh-dss'
|
89
|
+
begin
|
90
|
+
ppk.public_blob = Util.ssh_pack('ssh-dss', p, q, g, pub_key)
|
91
|
+
ppk.private_blob = Util.ssh_pack(priv_key)
|
92
|
+
rescue NilValueError
|
93
|
+
raise InvalidStateError, 'The key has not been fully initialized (the p, q, g, pub_key and priv_key parameters must all be assigned)'
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# The {EC} module is included into `OpenSSL::PKey::EC` when using the
|
100
|
+
# PuTTY::Key refinement or calling {PuTTY::Key.global_install}. This adds
|
101
|
+
# a `to_ppk` instance method to `OpenSSL::PKey::EC`.
|
102
|
+
module EC
|
103
|
+
# Returns a new {PPK} instance that is equivalent to this key.
|
104
|
+
#
|
105
|
+
# `to_ppk` can be called on instances of `OpenSSL::PKey::EC`.
|
106
|
+
#
|
107
|
+
# @return [PPK] A new instance of {PPK} that is equivalent to this key.
|
108
|
+
#
|
109
|
+
# @raise [InvalidStateError] If the key has not been initialized.
|
110
|
+
# @raise [UnsupportedCurveError] If the key uses a curve that is not
|
111
|
+
# supported by PuTTY.
|
112
|
+
def to_ppk
|
113
|
+
curve = group && group.curve_name
|
114
|
+
raise InvalidStateError, 'The key has not been fully initialized (a curve name must be assigned)' unless curve
|
115
|
+
ssh_curve = SSH_CURVES[curve]
|
116
|
+
raise UnsupportedCurveError, "The curve '#{curve}' is not supported" unless ssh_curve
|
117
|
+
|
118
|
+
PPK.new.tap do |ppk|
|
119
|
+
ppk.algorithm = "ecdsa-sha2-#{ssh_curve}"
|
120
|
+
begin
|
121
|
+
ppk.public_blob = Util.ssh_pack(ppk.algorithm, ssh_curve, public_key && public_key.to_bn)
|
122
|
+
ppk.private_blob = Util.ssh_pack(private_key)
|
123
|
+
rescue NilValueError
|
124
|
+
raise InvalidStateError, 'The key has not been fully initialized (public_key and private_key must both be assigned)'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# The {RSA} module is included into `OpenSSL::PKey::RSA` when using the
|
131
|
+
# PuTTY::Key refinement or calling {PuTTY::Key.global_install}. This adds
|
132
|
+
# a `to_ppk` instance method to `OpenSSL::PKey::RSA`.
|
133
|
+
module RSA
|
134
|
+
# Returns a new {PPK} instance that is equivalent to this key.
|
135
|
+
#
|
136
|
+
# `to_ppk` can be called on instances of `OpenSSL::PKey::DSA`.
|
137
|
+
#
|
138
|
+
# @return [PPK] A new instance of {PPK} that is equivalent to this key.
|
139
|
+
#
|
140
|
+
# @raise [InvalidStateError] If the key has not been initialized.
|
141
|
+
def to_ppk
|
142
|
+
PPK.new.tap do |ppk|
|
143
|
+
ppk.algorithm = 'ssh-rsa'
|
144
|
+
begin
|
145
|
+
ppk.public_blob = Util.ssh_pack('ssh-rsa', e, n)
|
146
|
+
ppk.private_blob = Util.ssh_pack(d, p, q, iqmp)
|
147
|
+
rescue NilValueError
|
148
|
+
raise InvalidStateError, 'The key has not been fully initialized (the e, n, d, p, q and iqmp parameters must all be assigned)'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Makes the refinements to `OpenSSL` available in PuTTY::Key available
|
155
|
+
# globally. After calling {global_install}, it is no longer necessary to
|
156
|
+
# include `using PuTTY::Key` when using the `to_ppk` and `from_ppk`
|
157
|
+
# methods added to `OpenSSL::PKey`.
|
158
|
+
def self.global_install
|
159
|
+
PKEY_CLASSES.each do |name, openssl_class|
|
160
|
+
mod = const_get(name)
|
161
|
+
openssl_class.class_eval do
|
162
|
+
include mod
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
::OpenSSL::PKey.module_eval do
|
167
|
+
extend ClassMethods
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
OpenSSL.const_get(:PKEY_CLASSES).each do |name, openssl_class|
|
173
|
+
refine openssl_class do
|
174
|
+
include OpenSSL.const_get(name)
|
175
|
+
end if respond_to?(:refine, true)
|
176
|
+
end
|
177
|
+
|
178
|
+
refine ::OpenSSL::PKey.singleton_class do
|
179
|
+
include OpenSSL::ClassMethods
|
180
|
+
end if respond_to?(:refine, true)
|
181
|
+
end
|
182
|
+
end
|