putty-key 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/putty-key.svg)](http://badge.fury.io/rb/putty-key) [![Build Status](https://travis-ci.org/philr/putty-key.svg?branch=master)](https://travis-ci.org/philr/putty-key) [![Coverage Status](https://coveralls.io/repos/philr/putty-key/badge.svg?branch=master)](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
|