Class: OrangeData::Credentials

Inherits:
Object
  • Object
show all
Defined in:
lib/orange_data/credentials.rb

Overview

wrapper for keys/certs used for connection auth

Defined Under Namespace

Modules: KeyEncoding

Constant Summary collapse

DEFAULT_KEY_LENGTH =
2048

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(signature_key_name: nil, signature_key: nil, certificate: nil, certificate_key: nil, title: nil) ⇒ Credentials

Returns a new instance of Credentials.

Raises:

  • (ArgumentError)


81
82
83
84
85
86
87
88
89
# File 'lib/orange_data/credentials.rb', line 81

def initialize(signature_key_name:nil, signature_key:nil, certificate:nil, certificate_key:nil, title:nil)
  raise ArgumentError, "Signature key should be a private key" if signature_key && !signature_key.private?
  raise ArgumentError, "Certificate key should be a private key" if certificate_key && !certificate_key.private?
  @signature_key_name = signature_key_name
  @signature_key = signature_key
  @certificate = certificate
  @certificate_key = certificate_key
  @title = title
end

Instance Attribute Details

#certificateObject

Returns the value of attribute certificate.



79
80
81
# File 'lib/orange_data/credentials.rb', line 79

def certificate
  @certificate
end

#certificate_keyObject

Returns the value of attribute certificate_key.



79
80
81
# File 'lib/orange_data/credentials.rb', line 79

def certificate_key
  @certificate_key
end

#signature_keyObject

Returns the value of attribute signature_key.



79
80
81
# File 'lib/orange_data/credentials.rb', line 79

def signature_key
  @signature_key
end

#signature_key_nameObject

Returns the value of attribute signature_key_name.



79
80
81
# File 'lib/orange_data/credentials.rb', line 79

def signature_key_name
  @signature_key_name
end

#titleObject

Returns the value of attribute title.



79
80
81
# File 'lib/orange_data/credentials.rb', line 79

def title
  @title
end

Class Method Details

.default_testObject

ключи для тествого окружения



215
216
217
# File 'lib/orange_data/credentials.rb', line 215

def self.default_test
  from_hash(YAML.load_file(File.expand_path('credentials_test.yml', __dir__)))
end

.from_hash(creds) ⇒ Object



109
110
111
112
113
114
115
116
117
# File 'lib/orange_data/credentials.rb', line 109

def self.from_hash(creds)
  new(
    title: creds[:title],
    signature_key_name: creds[:signature_key_name],
    signature_key: OpenSSL::PKey::RSA.load_from(creds[:signature_key], creds[:signature_key_pass]),
    certificate: creds[:certificate] && OpenSSL::X509::Certificate.new(creds[:certificate]),
    certificate_key: OpenSSL::PKey::RSA.load_from(creds[:certificate_key], creds[:certificate_key_pass])
  )
end

.from_json(json) ⇒ Object



144
145
146
147
# File 'lib/orange_data/credentials.rb', line 144

def self.from_json(json)
  require 'json'
  from_hash(JSON.parse(json, symbolize_names: true))
end

.generate_signature_key(key_length = DEFAULT_KEY_LENGTH) ⇒ Object

Raises:

  • (ArgumentError)


177
178
179
180
# File 'lib/orange_data/credentials.rb', line 177

def self.generate_signature_key(key_length=DEFAULT_KEY_LENGTH)
  raise ArgumentError, "key length should be >= 489, recomended #{DEFAULT_KEY_LENGTH}" unless key_length >= 489
  OpenSSL::PKey::RSA.new(key_length)
end

.read_certs_from_pack(path, signature_key_name: nil, cert_key_pass: nil, title: nil, signature_key: nil) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/orange_data/credentials.rb', line 182

def self.read_certs_from_pack(path, signature_key_name:nil, cert_key_pass:nil, title:nil, signature_key:nil)
  path = File.expand_path(path)
  client_cert = Dir.glob(path + '/*.{crt}').select{|f| File.file?(f.sub(/.crt\z/, '.key')) }
  raise 'Expect to find exactly one <num>.crt with corresponding <num>.key file' unless client_cert.size == 1
  client_cert = client_cert.first

  unless signature_key
    # private_key_test.xml || rsa_\d+_private_key.xml
    xmls = Dir.glob(path + '/*.{xml}').select{|f| f =~ /private/ }
    signature_key = if xmls.size == 1
      File.read(xmls.first)
    else
      generate_signature_key(DEFAULT_KEY_LENGTH)
      # .tap{|k| logger.info("Generated public signature key: #{k.public_key.to_xml}") }
    end
  end

  from_hash(
    title: title || "Generated from #{File.basename(path)}",
    signature_key_name: signature_key_name || File.basename(client_cert).gsub(/\..*/, ''),
    certificate: File.read(client_cert),
    certificate_key: File.read(client_cert.sub(/.crt\z/, '.key')),
    certificate_key_pass: cert_key_pass,
    signature_key: signature_key
  )
end

Instance Method Details

#==(other) ⇒ Object



99
100
101
102
103
104
105
106
107
# File 'lib/orange_data/credentials.rb', line 99

def ==(other)
  return false unless %i[signature_key_name title].all?{|m| send(m) == other.send(m) }
  # certificates/keys cannot be compared directly, so dump
  %i[signature_key certificate certificate_key].all?{|m|
    c1 = send(m)
    c2 = other.send(m)
    c1 == c2 || (c1 && c2 && c1.to_der == c2.to_der)
  }
end

#generate_signature_key!(key_length = DEFAULT_KEY_LENGTH) ⇒ Object

deprecated



173
174
175
# File 'lib/orange_data/credentials.rb', line 173

def generate_signature_key!(key_length=DEFAULT_KEY_LENGTH)
  self.signature_key = self.class.generate_signature_key(key_length)
end

#inspectObject



157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/orange_data/credentials.rb', line 157

def inspect
  info_fields = {
    title: (title || 'untitled').inspect,
    key_name: signature_key_name.inspect,
  }

  if certificate && (subject_name = certificate.subject.to_a.select{|ent| ent.first == 'O' }.first)
    info_fields[:certificate] = %("#{(subject_name[1] || 'unknown').gsub('"', '\"')}")
  end

  "#<#{self.class.name}:#{object_id} #{info_fields.map{|(k, v)| "#{k}=#{v}" }.join(' ')}>"
end

#signature_public_xmlObject

публичная часть ключа подписи в формате пригодном для отдачи в ЛК



210
211
212
# File 'lib/orange_data/credentials.rb', line 210

def signature_public_xml
  signature_key.public_key.to_xml
end

#to_hash(key_pass: nil, save_pass: false) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/orange_data/credentials.rb', line 119

def to_hash(key_pass:nil, save_pass:false)
  if key_pass.nil?
    key_pass = SecureRandom.hex
    save_pass = true
  elsif key_pass == false
    key_pass = nil
  end

  {
    title: title,
    signature_key_name: signature_key_name,
    signature_key: signature_key &&
      signature_key.to_pem(key_pass && OpenSSL::Cipher.new("aes-128-cbc"), key_pass),
    certificate: certificate && certificate.to_pem,
    certificate_key: certificate_key &&
      certificate_key.to_pem(key_pass && OpenSSL::Cipher.new("aes-128-cbc"), key_pass),
  }.tap do |h|
    h.delete(:title) if !title || title == ''
    if save_pass
      h[:certificate_key_pass] = key_pass if certificate && key_pass
      h[:signature_key_pass] = key_pass if signature_key && key_pass
    end
  end
end

#to_json(key_pass: nil, save_pass: false) ⇒ Object



149
150
151
# File 'lib/orange_data/credentials.rb', line 149

def to_json(key_pass:nil, save_pass:false)
  to_hash(key_pass:key_pass, save_pass:save_pass).to_json
end

#to_yaml(key_pass: nil, save_pass: false) ⇒ Object



153
154
155
# File 'lib/orange_data/credentials.rb', line 153

def to_yaml(key_pass:nil, save_pass:false)
  to_hash(key_pass:key_pass, save_pass:save_pass).to_yaml
end

#valid?Boolean

Returns:

  • (Boolean)


91
92
93
94
95
96
97
# File 'lib/orange_data/credentials.rb', line 91

def valid?
  signature_key_name &&
    signature_key && signature_key.private? &&
    (signature_key.n.num_bits >= 489) && # minimum working key length for sha256 signature
    certificate && certificate_key &&
    certificate_key.private? && certificate.check_private_key(certificate_key)
end