Class: Mqlight::SecureSocket

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/mqlight/util.rb

Overview

This class handles and processes the SSL connection options for this client.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#logger, logger

Constructor Details

#initialize(options) ⇒ SecureSocket

Returns a new instance of SecureSocket.



279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/mqlight/util.rb', line 279

def initialize(options)
  @id = options.fetch(:id, nil)
  logger.entry(@id) { self.class.to_s + '#' + __method__.to_s }
  parms = Hash[method(__method__).parameters.map do |parm|
    [parm[1], eval(parm[1].to_s)]
  end]
  Logging.logger.parms(@id, parms) do
    self.class.to_s + '#' + __method__.to_s
  end

  # Look for any non keystore argument and remember first
  first_with_keystore_argument = nil
  without_keystore_options = \
    [:ssl_client_certificate, :ssl_trust_certificate,
     :ssl_client_key, :ssl_client_key_passphrase]
  with_keystore_options = [:ssl_keystore, :ssl_keystore_passphrase]
  with_keystore_options.each do |argument_name|
    unless options[argument_name].nil?
      first_with_keystore_argument = argument_name
      break
    end
  end

  # Look for any keystore argument and remember first
  first_without_keystore_argument = nil
  without_keystore_options.each do |argument_name|
    unless options[argument_name].nil?
      first_without_keystore_argument = argument_name
      break
    end
  end

  fail ArgumentError, 'Invalid combination of arguments '\
       "#{first_without_keystore_argument} and " \
       "#{first_with_keystore_argument}" \
    if first_without_keystore_argument && first_with_keystore_argument

  # Load and validate arguments
  if first_without_keystore_argument
    # Load options
    @ssl_client_certificate = options[:ssl_client_certificate]
    @ssl_trust_certificate = options[:ssl_trust_certificate]
    @ssl_client_key = options[:ssl_client_key]
    @ssl_client_key_passphrase = options[:ssl_client_key_passphrase]
    # Validate types
    fail ArgumentError, 'ssl_client_certificate must be of type String' \
      unless @ssl_client_certificate.nil? ||
             @ssl_client_certificate.is_a?(String)
    fail ArgumentError, 'ssl_trust_certificate must be of type String' \
      unless @ssl_trust_certificate.nil? ||
             @ssl_trust_certificate.is_a?(String)
    fail ArgumentError, 'ssl_client_key must be of type String' \
      unless @ssl_client_key.nil? ||
             @ssl_client_key.is_a?(String)
    fail ArgumentError, 'ssl_client_key_passphrase must be of type String' \
      unless @ssl_client_key_passphrase.nil? ||
             @ssl_client_key_passphrase.is_a?(String)
    # Combination check.
    fail ArgumentError,
         'Invalid combination of arguments. The client key passphrase is ' \
         'present but no associated client key has been specified' \
      if !@ssl_client_key_passphrase.nil? && @ssl_client_key.nil?
    # client key set : If one is present then all must be present
    # sslClientCertificate, sslClientKey and sslClientKeyPassphrase
    client_key_set_one_present = !@ssl_client_certificate.nil? || !@ssl_client_key.nil? || !@ssl_client_key_passphrase.nil?
    client_key_set_one_missing = @ssl_client_certificate.nil? || @ssl_client_key.nil? || @ssl_client_key_passphrase.nil?
    fail ArgumentError,
         'sslClientCertificate, sslClientKey and sslClientKeyPassphrase ' \
         'options must all be specified' \
      if client_key_set_one_present && client_key_set_one_missing
    # Check file references
    validate_file_path @ssl_trust_certificate, 'ssl_trust_certificate' \
      unless @ssl_trust_certificate.nil?
    validate_file_path @ssl_client_certificate, 'ssl_client_certificate'\
      unless @ssl_client_certificate.nil?
    validate_file_path @ssl_client_key, 'ssl_client_key'\
      unless @ssl_client_key.nil?
    @keystore_present = false
  elsif first_with_keystore_argument
    # Load options
    ssl_keystore = options[:ssl_keystore]
    ssl_keystore_passphrase = options[:ssl_keystore_passphrase]
    # Validate types
    fail ArgumentError, 'ssl_keystore must be of type String' \
      unless ssl_keystore.is_a? String
    fail ArgumentError, 'ssl_keystore_passphrase must be of type String' \
      unless ssl_keystore_passphrase.is_a? String
    # Combination check
    fail ArgumentError,
         'Invalid combination of arguments. The keystore passphrase is ' \
         'present but no associated keystore has been specified' \
      if ssl_keystore_passphrase.nil? && !ssl_keystore.nil?
    # Check file references
    validate_file_path ssl_keystore, 'ssl_keystore' \
      unless ssl_keystore.nil?

    # Load the keystore
    data = File.binread(ssl_keystore)
    @keystore_pkcs12 = OpenSSL::PKCS12.new(data, ssl_keystore_passphrase)
    @keystore_present = true
  end

  # If server host verification option required?
  @ssl_verify_name = options.fetch(:ssl_verify_name, false)
  fail ArgumentError, 'ssl_verify_name must be of type Binary' \
    unless @ssl_verify_name.is_a? TrueClass or
           @ssl_verify_name.is_a? FalseClass

  logger.exit(@id) { self.class.to_s + '#' + __method__.to_s }
rescue StandardError => e
  logger.throw(@id, e) { self.class.to_s + '#' + __method__.to_s }
  raise e
end

Instance Attribute Details

#ssl_trust_certificateObject (readonly)

Returns the value of attribute ssl_trust_certificate.



273
274
275
# File 'lib/mqlight/util.rb', line 273

def ssl_trust_certificate
  @ssl_trust_certificate
end

#verified_host_nameObject (readonly)

Returns the value of attribute verified_host_name.



274
275
276
# File 'lib/mqlight/util.rb', line 274

def verified_host_name
  @verified_host_name
end

Instance Method Details

#context(server_host_name) ⇒ SSLContext

Create the SSL context based on the given attributes

Parameters:

  • server_host_name (String)

    name of the server host to be used for validating certificates.

Returns:

  • (SSLContext)

    the generated context



486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
# File 'lib/mqlight/util.rb', line 486

def context(server_host_name)
  logger.entry(@id) { self.class.to_s + '#' + __method__.to_s }
  parms = Hash[method(__method__).parameters.map do |parm|
    [parm[1], eval(parm[1].to_s)]
  end]
  logger.parms(@id, parms) { self.class.to_s + '#' + __method__.to_s }

  context = OpenSSL::SSL::SSLContext.new

  # Create the X509 Store for the CAs
  context.cert_store = x509_certificate_authorities

  # Private key
  context.key = rsa_private_key

  # Client Certificate
  context.cert = x509_certificate
  context.verify_mode = OpenSSL::SSL::VERIFY_PEER

  # If verify name required then assign callback facility to
  # receive acknowledgement
  @verified_host_name = false
  if @ssl_verify_name
    context.verify_callback = proc do |_preverify, inner_context|
      @verified_host_name |= OpenSSL::SSL.verify_certificate_identity(
        inner_context.current_cert, server_host_name)
      true
    end
  end

  logger.exit(@id, context) { self.class.to_s + '#' + __method__.to_s }
  context
end

#rsa_private_keyPKey

Returns containing the given private key or nil if none is present.

Returns:

  • (PKey)

    containing the given private key or nil if none is present



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
# File 'lib/mqlight/util.rb', line 396

def rsa_private_key
  logger.entry(@id) { self.class.to_s + '#' + __method__.to_s }
  rc = nil
  if @keystore_present
    rc = @keystore_pkcs12.key
  elsif !@ssl_client_key.nil?
    begin
      rc = OpenSSL::PKey::RSA.new \
        File.read(@ssl_client_key), \
        @ssl_client_key_passphrase
    rescue OpenSSL::PKey::RSAError => re
      logger.throw(@id, re) { self.class.to_s + '#' + __method__.to_s }
      fail ArgumentError, \
           'File given for the ssl_client_key is not a valid RSA ' \
           'Certificate'
    end
  end
  logger.exit(@id) { self.class.to_s + '#' + __method__.to_s }
  rc
end

#validate_file_path(file_path, file_description) ⇒ Object

Validates that the given file is present and regular

Parameters:

  • file_path (String)

    file path to file to be validate

  • file_description (String)

    descriptive file of file.



523
524
525
526
527
528
529
530
# File 'lib/mqlight/util.rb', line 523

def validate_file_path(file_path, file_description)
  fail ArgumentError,
       "The file specified for #{file_description} does not exist" \
         unless File.exist?(file_path)
  fail ArgumentError,
       "The file specified for #{file_description} is not a regular file" \
         unless File.file?(file_path)
end

#verify_server_host_name_failed?Boolean

failed.

Returns:

  • (Boolean)

    true indicate verification was required and it



536
537
538
# File 'lib/mqlight/util.rb', line 536

def verify_server_host_name_failed?
  @ssl_verify_name && !@verified_host_name
end

#x509_certificateOpenSSL::X509::Certificate

Returns from the given attributes or null if not are present.

Returns:

  • (OpenSSL::X509::Certificate)

    from the given attributes or null if not are present.



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/mqlight/util.rb', line 462

def x509_certificate
  logger.entry(@id) { self.class.to_s + '#' + __method__.to_s }
  if @keystore_present
    rc = @keystore_pkcs12.certificate
  elsif ! @ssl_client_certificate.nil?
    begin
      rc = OpenSSL::X509::Certificate.new File.read(@ssl_client_certificate)
    rescue OpenSSL::X509::CertificateError => se
      logger.throw(@id, se) { self.class.to_s + '#' + __method__.to_s }
      fail ArgumentError,
           'File given for the \'ssl_client_certificate\' argument ' \
           'is not a valid X509 certificate'
    end
  end
  logger.exit(@id) { self.class.to_s + '#' + __method__.to_s }
  rc
end

#x509_certificate_authoritiesX509Store

Returns contains one or more CA for the given keystore or arguments. nil is returned is none are present.

Returns:

  • (X509Store)

    contains one or more CA for the given keystore or arguments. nil is returned is none are present



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
# File 'lib/mqlight/util.rb', line 421

def x509_certificate_authorities
  logger.entry(@id) { self.class.to_s + '#' + __method__.to_s }
  store = nil
  if @keystore_present && !@keystore_pkcs12.ca_certs.nil?
    store = OpenSSL::X509::Store.new
    @keystore_pkcs12.ca_certs.each do |cert|
      begin
        store.add_cert cert
      rescue OpenSSL::X509::StoreError => e
        fail ArgumentError, \
             'File given for the \'ssl_trust_certificate\' argument ' \
             "has the following error \'#{e}\'" \
             unless e.message.include? 'cert already in hash table'
               
        logger.data(@id, e.message + ' - Certificate has been skipped') do
          self.class.to_s + '#' + __method__.to_s
        end
      end
    end
  elsif ! @ssl_trust_certificate.nil?
    begin
      store = OpenSSL::X509::Store.new
      store.add_file @ssl_trust_certificate
    rescue OpenSSL::X509::StoreError => se
      logger.throw(@id, se) { self.class.to_s + '#' + __method__.to_s }
      fail ArgumentError, \
           'File given for the \'ssl_trust_certificate\' argument is not ' \
           'a valid trust certificate'
    end
  else
    store = OpenSSL::X509::Store.new
    store.set_default_paths
  end
  logger.exit(@id) { self.class.to_s + '#' + __method__.to_s }
  store
end