omf_common 6.0.0 → 6.0.2.pre.1
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.
- data/Gemfile +4 -0
- data/bin/file_broadcaster.rb +56 -0
- data/bin/file_receiver.rb +62 -0
- data/bin/omf_keygen +21 -0
- data/bin/{monitor_topic.rb → omf_monitor_topic} +21 -8
- data/bin/omf_send_create +118 -0
- data/bin/{send_request.rb → omf_send_request} +12 -7
- data/example/engine_alt.rb +23 -24
- data/example/ls_app.yaml +21 -0
- data/lib/omf_common.rb +73 -12
- data/lib/omf_common/auth.rb +15 -0
- data/lib/omf_common/auth/certificate.rb +174 -0
- data/lib/omf_common/auth/certificate_store.rb +72 -0
- data/lib/omf_common/auth/ssh_pub_key_convert.rb +80 -0
- data/lib/omf_common/comm.rb +66 -9
- data/lib/omf_common/comm/amqp/amqp_communicator.rb +40 -13
- data/lib/omf_common/comm/amqp/amqp_file_transfer.rb +259 -0
- data/lib/omf_common/comm/amqp/amqp_topic.rb +14 -21
- data/lib/omf_common/comm/local/local_communicator.rb +31 -2
- data/lib/omf_common/comm/local/local_topic.rb +19 -3
- data/lib/omf_common/comm/topic.rb +48 -34
- data/lib/omf_common/comm/xmpp/communicator.rb +19 -10
- data/lib/omf_common/comm/xmpp/topic.rb +22 -81
- data/lib/omf_common/default_logging.rb +11 -0
- data/lib/omf_common/eventloop.rb +14 -0
- data/lib/omf_common/eventloop/em.rb +39 -6
- data/lib/omf_common/eventloop/local_evl.rb +15 -0
- data/lib/omf_common/exec_app.rb +29 -15
- data/lib/omf_common/message.rb +53 -5
- data/lib/omf_common/message/json/json_message.rb +149 -39
- data/lib/omf_common/message/xml/message.rb +112 -39
- data/lib/omf_common/protocol/6.0.rnc +5 -1
- data/lib/omf_common/protocol/6.0.rng +12 -0
- data/lib/omf_common/version.rb +1 -1
- data/omf_common.gemspec +7 -2
- data/test/fixture/omf_test.cert.pem +15 -0
- data/test/fixture/omf_test.pem +15 -0
- data/test/fixture/omf_test.pub +1 -0
- data/test/fixture/omf_test.pub.pem +6 -0
- data/test/omf_common/auth/certificate_spec.rb +113 -0
- data/test/omf_common/auth/ssh_pub_key_convert_spec.rb +13 -0
- data/test/omf_common/comm/topic_spec.rb +175 -0
- data/test/omf_common/comm/xmpp/communicator_spec.rb +15 -16
- data/test/omf_common/comm/xmpp/topic_spec.rb +63 -10
- data/test/omf_common/comm_spec.rb +66 -9
- data/test/omf_common/message/xml/message_spec.rb +43 -13
- data/test/omf_common/message_spec.rb +14 -0
- data/test/test_helper.rb +25 -0
- metadata +78 -15
- data/bin/send_create.rb +0 -94
data/example/ls_app.yaml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# Describes how to run simple 'ls' on a node
|
3
|
+
#
|
4
|
+
create:
|
5
|
+
type: application
|
6
|
+
properties:
|
7
|
+
binary_path: /bin/ls
|
8
|
+
state: running
|
9
|
+
membership: apps
|
10
|
+
#environment: # (Hash) the environment variables to set prior to starting this app.
|
11
|
+
parameters:
|
12
|
+
p1:
|
13
|
+
cmd: -l
|
14
|
+
value: true
|
15
|
+
type: Boolean
|
16
|
+
order: 1
|
17
|
+
p2:
|
18
|
+
cmd: /
|
19
|
+
value: true
|
20
|
+
type: Boolean
|
21
|
+
order: 2
|
data/lib/omf_common.rb
CHANGED
@@ -6,7 +6,7 @@ require 'omf_common/measure'
|
|
6
6
|
require 'omf_common/message'
|
7
7
|
require 'omf_common/comm'
|
8
8
|
require 'omf_common/command'
|
9
|
-
require 'omf_common/
|
9
|
+
require 'omf_common/auth'
|
10
10
|
require 'omf_common/core_ext/string'
|
11
11
|
require 'omf_common/eventloop'
|
12
12
|
|
@@ -19,8 +19,9 @@ module OmfCommon
|
|
19
19
|
type: 'em'
|
20
20
|
},
|
21
21
|
logging: {
|
22
|
-
level:
|
23
|
-
|
22
|
+
level: {
|
23
|
+
default: 'debug'
|
24
|
+
},
|
24
25
|
appenders: {
|
25
26
|
stdout: {
|
26
27
|
date_pattern: '%H:%M:%S',
|
@@ -35,8 +36,35 @@ module OmfCommon
|
|
35
36
|
type: :em
|
36
37
|
},
|
37
38
|
logging: {
|
38
|
-
level:
|
39
|
+
level: {
|
40
|
+
default: 'info'
|
41
|
+
},
|
42
|
+
appenders: {
|
43
|
+
file: {
|
44
|
+
log_dir: '/var/log',
|
45
|
+
#log_file: 'foo.log',
|
46
|
+
date_pattern: '%F %T %z',
|
47
|
+
pattern: '[%d] %-5l %c: %m\n'
|
48
|
+
}
|
49
|
+
}
|
39
50
|
|
51
|
+
}
|
52
|
+
},
|
53
|
+
daemon: {
|
54
|
+
daemonize: {
|
55
|
+
dir_mode: :script,
|
56
|
+
dir: '/tmp',
|
57
|
+
backtrace: true,
|
58
|
+
log_dir: '/var/log',
|
59
|
+
log_output: true
|
60
|
+
},
|
61
|
+
eventloop: {
|
62
|
+
type: :em
|
63
|
+
},
|
64
|
+
logging: {
|
65
|
+
level: {
|
66
|
+
default: 'info'
|
67
|
+
},
|
40
68
|
appenders: {
|
41
69
|
file: {
|
42
70
|
log_dir: '/var/log',
|
@@ -54,8 +82,9 @@ module OmfCommon
|
|
54
82
|
},
|
55
83
|
eventloop: { type: :local},
|
56
84
|
logging: {
|
57
|
-
level:
|
58
|
-
|
85
|
+
level: {
|
86
|
+
default: 'debug'
|
87
|
+
},
|
59
88
|
appenders: {
|
60
89
|
stdout: {
|
61
90
|
date_pattern: '%H:%M:%S',
|
@@ -65,7 +94,7 @@ module OmfCommon
|
|
65
94
|
}
|
66
95
|
}
|
67
96
|
},
|
68
|
-
|
97
|
+
test_daemon: {
|
69
98
|
daemonize: {
|
70
99
|
dir_mode: :script,
|
71
100
|
dir: '/tmp',
|
@@ -74,10 +103,12 @@ module OmfCommon
|
|
74
103
|
log_output: true
|
75
104
|
},
|
76
105
|
eventloop: {
|
77
|
-
type: :
|
106
|
+
type: :em
|
78
107
|
},
|
79
108
|
logging: {
|
80
|
-
level:
|
109
|
+
level: {
|
110
|
+
default: 'debug'
|
111
|
+
},
|
81
112
|
appenders: {
|
82
113
|
file: {
|
83
114
|
log_dir: '/tmp',
|
@@ -120,9 +151,14 @@ module OmfCommon
|
|
120
151
|
unless copts = opts[:communication]
|
121
152
|
raise "Missing :communication description"
|
122
153
|
end
|
123
|
-
|
154
|
+
|
155
|
+
if aopts = opts[:auth]
|
156
|
+
require 'omf_common/auth/credential_store'
|
157
|
+
OmfCommon::Auth::CredentialStore.init(aopts)
|
158
|
+
end
|
124
159
|
|
125
160
|
# Initialise event loop
|
161
|
+
eopts = opts[:eventloop]
|
126
162
|
Eventloop.init(eopts)
|
127
163
|
# start eventloop immediately if we received a run block
|
128
164
|
eventloop.run do
|
@@ -151,6 +187,10 @@ module OmfCommon
|
|
151
187
|
# :same - Look in the same directory as '$0'
|
152
188
|
# :remove_root ROOT_NAME: Remove the root node. Throw exception if not ROOT_NAME
|
153
189
|
# :wait_for_readable SECS: Wait until the yaml file becomes readable. Check every SECS
|
190
|
+
# :erb_process flag: Run the content of the loaded file through ERB first before YAML parsing
|
191
|
+
# :erb_safe_level level: If safe_level is set to a non-nil value, ERB code will be run in a
|
192
|
+
# separate thread with $SAFE set to the provided level.
|
193
|
+
# :erb_binding binding: Optional binding given to ERB#result
|
154
194
|
#
|
155
195
|
def self.load_yaml(file_name, opts = {})
|
156
196
|
if path_opt = opts[:path]
|
@@ -167,7 +207,14 @@ module OmfCommon
|
|
167
207
|
sleep readable_check # wait until file shows up
|
168
208
|
end
|
169
209
|
end
|
170
|
-
|
210
|
+
|
211
|
+
str = File.read(file_name)
|
212
|
+
if opts[:erb_process]
|
213
|
+
require 'erb'
|
214
|
+
str = ERB.new(str, opts[:erb_safe_level]).result(opts[:erb_binding] || binding)
|
215
|
+
end
|
216
|
+
yh = YAML.load(str)
|
217
|
+
|
171
218
|
if opts[:symbolize_keys]
|
172
219
|
yh = _rec_sym_keys(yh)
|
173
220
|
end
|
@@ -209,11 +256,25 @@ module OmfCommon
|
|
209
256
|
end
|
210
257
|
end
|
211
258
|
if level = opts[:level]
|
212
|
-
|
259
|
+
if level.is_a? Hash
|
260
|
+
# package level settings
|
261
|
+
level.each do |name, lvl|
|
262
|
+
if name.to_s == 'default'
|
263
|
+
logger.level = lvl.to_sym
|
264
|
+
else
|
265
|
+
Logging.logger[name.to_s].level = lvl.to_sym
|
266
|
+
end
|
267
|
+
end
|
268
|
+
else
|
269
|
+
logger.level = level.to_sym
|
270
|
+
end
|
213
271
|
end
|
214
272
|
end
|
215
273
|
|
216
274
|
def self._rec_merge(this_hash, other_hash)
|
275
|
+
# if the dominant side is not a hash we stop recursing and pick the primitive value
|
276
|
+
return other_hash unless other_hash.is_a? Hash
|
277
|
+
|
217
278
|
r = {}
|
218
279
|
this_hash.merge(other_hash) do |key, oldval, newval|
|
219
280
|
r[key] = oldval.is_a?(Hash) ? _rec_merge(oldval, newval) : newval
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'omf_common/auth'
|
3
|
+
require 'omf_common/auth/ssh_pub_key_convert'
|
4
|
+
|
5
|
+
module OmfCommon::Auth
|
6
|
+
|
7
|
+
class Certificate
|
8
|
+
DEF_DOMAIN_NAME = 'acme'
|
9
|
+
DEF_DURATION = 3600
|
10
|
+
|
11
|
+
BEGIN_CERT = "-----BEGIN CERTIFICATE-----\n"
|
12
|
+
END_CERT = "\n-----END CERTIFICATE-----\n"
|
13
|
+
@@serial = 0
|
14
|
+
|
15
|
+
# @param [String] name unique name of the entity (resource name)
|
16
|
+
# @param [String] type type of the entity (resource type)
|
17
|
+
# @param [String] domain of the resource
|
18
|
+
#
|
19
|
+
def self.create(address, name, type, domain = DEF_DOMAIN_NAME, issuer = nil, not_before = Time.now, duration = 3600, key = nil)
|
20
|
+
subject = _create_name(name, type, domain)
|
21
|
+
if key.nil?
|
22
|
+
key, digest = _create_key()
|
23
|
+
else
|
24
|
+
digest = _create_digest
|
25
|
+
end
|
26
|
+
|
27
|
+
c = _create_x509_cert(address, subject, key, digest, issuer, not_before, duration)
|
28
|
+
c[:address] = address if address
|
29
|
+
self.new c
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param [String] pem is the content of existing x509 cert
|
33
|
+
# @param [OpenSSL::PKey::RSA|String] key is the private key which can be attached to the instance for signing.
|
34
|
+
def self.create_from_x509(pem, key = nil)
|
35
|
+
unless pem.start_with? BEGIN_CERT
|
36
|
+
pem = "#{BEGIN_CERT}#{pem}#{END_CERT}"
|
37
|
+
end
|
38
|
+
cert = OpenSSL::X509::Certificate.new(pem)
|
39
|
+
|
40
|
+
key = OpenSSL::PKey::RSA.new(key) if key && key.is_a?(String)
|
41
|
+
|
42
|
+
if key && !cert.check_private_key(key)
|
43
|
+
raise ArgumentError, "Private key provided could not match the public key of given certificate"
|
44
|
+
end
|
45
|
+
self.new({ cert: cert, key: key })
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns an array with a new RSA key and a SHA1 digest
|
49
|
+
#
|
50
|
+
def self._create_key(size = 2048)
|
51
|
+
[OpenSSL::PKey::RSA.new(size), OpenSSL::Digest::SHA1.new]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self._create_digest
|
55
|
+
OpenSSL::Digest::SHA1.new
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [String] name unique name of the entity (resource name)
|
59
|
+
# @param [String] type type of the entity (resource type)
|
60
|
+
#
|
61
|
+
def self._create_name(name, type, domain = DEF_DOMAIN_NAME)
|
62
|
+
OpenSSL::X509::Name.new [['CN', "frcp//#{domain}//frcp.#{type}.#{name}"]], {}
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create a X509 certificate
|
66
|
+
#
|
67
|
+
# @param [] address
|
68
|
+
# @return {cert, key}
|
69
|
+
#
|
70
|
+
def self._create_x509_cert(address, subject, key, digest = nil,
|
71
|
+
issuer = nil, not_before = Time.now, duration = DEF_DURATION, extensions = [])
|
72
|
+
extensions << ["subjectAltName", "URI:#{address}", false] if address
|
73
|
+
|
74
|
+
cert = OpenSSL::X509::Certificate.new
|
75
|
+
cert.version = 2
|
76
|
+
# TODO change serial to non-sequential secure random numbers for production use
|
77
|
+
cert.serial = (@@serial += 1)
|
78
|
+
cert.subject = subject
|
79
|
+
cert.public_key = key.public_key
|
80
|
+
cert.not_before = not_before
|
81
|
+
cert.not_after = not_before + duration
|
82
|
+
unless extensions.empty?
|
83
|
+
issuer_cert = issuer ? issuer.to_x509 : cert
|
84
|
+
ef = OpenSSL::X509::ExtensionFactory.new
|
85
|
+
ef.subject_certificate = cert
|
86
|
+
ef.issuer_certificate = issuer_cert
|
87
|
+
extensions.each{|oid, value, critical|
|
88
|
+
cert.add_extension(ef.create_extension(oid, value, critical))
|
89
|
+
}
|
90
|
+
end
|
91
|
+
if issuer
|
92
|
+
cert.issuer = issuer.subject
|
93
|
+
cert.sign(issuer.key, issuer.digest)
|
94
|
+
else
|
95
|
+
# self signed
|
96
|
+
cert.issuer = subject
|
97
|
+
cert.sign(key, digest)
|
98
|
+
end
|
99
|
+
{ cert: cert, key: key }
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :address, :subject, :key, :digest
|
103
|
+
|
104
|
+
def initialize(opts)
|
105
|
+
if @cert = opts[:cert]
|
106
|
+
@subject = @cert.subject
|
107
|
+
end
|
108
|
+
unless @address = opts[:address]
|
109
|
+
# try to see it it is in cert
|
110
|
+
if @cert
|
111
|
+
@cert.extensions.each do |ext|
|
112
|
+
if ext.oid == 'subjectAltName'
|
113
|
+
@address = ext.value[4 .. -1] # strip off 'URI:'
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
if @key = opts[:key]
|
119
|
+
@digest = opts[:digest] || OpenSSL::Digest::SHA1.new
|
120
|
+
end
|
121
|
+
unless @subject ||= opts[:subject]
|
122
|
+
name = opts[:name]
|
123
|
+
type = opts[:type]
|
124
|
+
domain = opts[:domain]
|
125
|
+
@subject = _create_name(name, type, domain)
|
126
|
+
end
|
127
|
+
@cert ||= _create_x509_cert(@address, @subject, @key, @digest)[:cert]
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_for(address, name, type, domain = DEF_DOMAIN_NAME, duration = 3600, key = nil)
|
131
|
+
raise ArgumentError, "Address required" unless address
|
132
|
+
cert = self.class.create(address, name, type, domain, self, Time.now, duration, key)
|
133
|
+
CertificateStore.instance.register(cert, address)
|
134
|
+
cert
|
135
|
+
end
|
136
|
+
|
137
|
+
# Return the X509 certificate. If it hasn't been passed in, return a self-signed one
|
138
|
+
def to_x509()
|
139
|
+
@cert
|
140
|
+
end
|
141
|
+
|
142
|
+
def can_sign?
|
143
|
+
!@key.nil? && @key.private?
|
144
|
+
end
|
145
|
+
|
146
|
+
def to_pem
|
147
|
+
to_x509.to_pem
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_pem_compact
|
151
|
+
to_pem.lines.to_a[1 ... -1].join.strip
|
152
|
+
end
|
153
|
+
|
154
|
+
def verify_cert
|
155
|
+
if @cert.issuer == self.subject # self signed cert
|
156
|
+
@cert.verify(@cert.public_key)
|
157
|
+
else
|
158
|
+
@cert.verify(CertificateStore.instance.cert_for(@cert.issuer).to_x509.public_key)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Will return one of the following
|
163
|
+
#
|
164
|
+
# :HS256, :HS384, :HS512, :RS256, :RS384, :RS512, :ES256, :ES384, :ES512
|
165
|
+
#
|
166
|
+
# def key_algorithm
|
167
|
+
#
|
168
|
+
# end
|
169
|
+
|
170
|
+
def to_s
|
171
|
+
"#<#{self.class} addr=#{@address} subj=#{@subject} can-sign=#{@key != nil}>"
|
172
|
+
end
|
173
|
+
end # class
|
174
|
+
end # module
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
require 'omf_common/auth'
|
4
|
+
|
5
|
+
#require 'singleton'
|
6
|
+
|
7
|
+
# module OmfCommon
|
8
|
+
# class Key
|
9
|
+
# include Singleton
|
10
|
+
#
|
11
|
+
# attr_accessor :private_key
|
12
|
+
#
|
13
|
+
# def import(filename)
|
14
|
+
# self.private_key = OpenSSL::PKey.read(File.read(filename))
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
|
19
|
+
module OmfCommon::Auth
|
20
|
+
|
21
|
+
class MissingPrivateKeyException < AuthException; end
|
22
|
+
|
23
|
+
class CertificateStore
|
24
|
+
|
25
|
+
|
26
|
+
@@instance = nil
|
27
|
+
|
28
|
+
def self.init(opts = {})
|
29
|
+
if @@instance
|
30
|
+
raise "CertificateStore already iniitalised"
|
31
|
+
end
|
32
|
+
@@instance = self.new(opts)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.instance
|
36
|
+
throw "CertificateStore not initialized" unless @@instance
|
37
|
+
@@instance
|
38
|
+
end
|
39
|
+
|
40
|
+
def register(certificate, address = nil)
|
41
|
+
if address ||= certificate.address
|
42
|
+
@certs[address] = certificate if address
|
43
|
+
else
|
44
|
+
warn "Register certificate without address - #{certificate}"
|
45
|
+
end
|
46
|
+
@certs[certificate.subject] = certificate
|
47
|
+
end
|
48
|
+
|
49
|
+
def register_x509(cert_pem, address = nil)
|
50
|
+
if (cert = Certificate.create_from_x509(cert_pem))
|
51
|
+
debug "REGISTERED #{cert}"
|
52
|
+
register(cert, address)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def cert_for(url)
|
57
|
+
@certs[url]
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
private
|
62
|
+
def initialize(opts)
|
63
|
+
@certs = {}
|
64
|
+
if store = opts[:store]
|
65
|
+
else
|
66
|
+
@store = {private: {}, public: {}}
|
67
|
+
end
|
68
|
+
@serial = 0
|
69
|
+
end
|
70
|
+
end # class
|
71
|
+
|
72
|
+
end # module
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'openssl'
|
3
|
+
require 'omf_common/auth'
|
4
|
+
|
5
|
+
module OmfCommon::Auth
|
6
|
+
# This file provides a converter that accepts an SSH public key string
|
7
|
+
# and converts it to an OpenSSL::PKey::RSA object for use in verifying
|
8
|
+
# received messages. (DSA support pending).
|
9
|
+
#
|
10
|
+
class SSHPubKeyConvert
|
11
|
+
# Unpack a 4-byte unsigned integer from the +bytes+ array.
|
12
|
+
#
|
13
|
+
# Returns a pair (+u32+, +bytes+), where +u32+ is the extracted
|
14
|
+
# unsigned integer, and +bytes+ is the remainder of the original
|
15
|
+
# +bytes+ array that follows +u32+.
|
16
|
+
#
|
17
|
+
def self.unpack_u32(bytes)
|
18
|
+
return bytes.unpack("N")[0], bytes[4..-1]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Unpack a string from the +bytes+ array. Exactly +len+ bytes will
|
22
|
+
# be extracted.
|
23
|
+
#
|
24
|
+
# Returns a pair (+string+, +bytes+), where +string+ is the
|
25
|
+
# extracted string (of length +len+), and +bytes+ is the remainder
|
26
|
+
# of the original +bytes+ array that follows +string+.
|
27
|
+
#
|
28
|
+
def self.unpack_string(bytes, len)
|
29
|
+
return bytes.unpack("A#{len}")[0], bytes[len..-1]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Convert a string in SSH public key format to a key object
|
33
|
+
# suitable for use with OpenSSL. If the key is an RSA key then an
|
34
|
+
# OpenSSL::PKey::RSA object is returned. If the key is a DSA key
|
35
|
+
# then an OpenSSL::PKey::DSA object is returned. In either case,
|
36
|
+
# the object returned is suitable for encrypting data or verifying
|
37
|
+
# signatures, but cannot be used for decrypting or signing.
|
38
|
+
#
|
39
|
+
# The +keystring+ should be a single line, as per an SSH public key
|
40
|
+
# file as generated by +ssh-keygen+, or a line from an SSH
|
41
|
+
# +authorized_keys+ file.
|
42
|
+
#
|
43
|
+
def self.convert(keystring)
|
44
|
+
(type, b64, id) = keystring.split(' ')
|
45
|
+
decoded_key = Base64.decode64(b64)
|
46
|
+
(n, bytes) = unpack_u32(decoded_key)
|
47
|
+
(keytype, bytes) = unpack_string(bytes, n)
|
48
|
+
|
49
|
+
if keytype == "ssh-rsa"
|
50
|
+
(n, bytes) = unpack_u32(bytes)
|
51
|
+
(estr, bytes) = unpack_string(bytes, n)
|
52
|
+
(n, bytes) = unpack_u32(bytes)
|
53
|
+
(nstr, bytes) = unpack_string(bytes, n)
|
54
|
+
|
55
|
+
key = OpenSSL::PKey::RSA.new
|
56
|
+
key.n = OpenSSL::BN.new(nstr, 2)
|
57
|
+
key.e = OpenSSL::BN.new(estr, 2)
|
58
|
+
key
|
59
|
+
elsif keytype == 'ssh-dss'
|
60
|
+
(n, bytes) = unpack_u32(bytes)
|
61
|
+
(pstr, bytes) = unpack_string(bytes, n)
|
62
|
+
(n, bytes) = unpack_u32(bytes)
|
63
|
+
(qstr, bytes) = unpack_string(bytes, n)
|
64
|
+
(n, bytes) = unpack_u32(bytes)
|
65
|
+
(gstr, bytes) = unpack_string(bytes, n)
|
66
|
+
(n, bytes) = unpack_u32(bytes)
|
67
|
+
(pkstr, bytes) = unpack_string(bytes, n)
|
68
|
+
|
69
|
+
key = OpenSSL::PKey::DSA.new
|
70
|
+
key.p = OpenSSL::BN.new(pstr, 2)
|
71
|
+
key.q = OpenSSL::BN.new(qstr, 2)
|
72
|
+
key.g = OpenSSL::BN.new(gstr, 2)
|
73
|
+
key.pub_key = OpenSSL::BN.new(pkstr, 2)
|
74
|
+
key
|
75
|
+
else
|
76
|
+
raise ArgumentError, "Unknown key type '#{keytype}'"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|