Class: Trocla::Formats::X509

Inherits:
Base
  • Object
show all
Defined in:
lib/trocla/formats/x509.rb

Overview

Trocla::Formats::X509

Instance Attribute Summary

Attributes inherited from Base

#trocla

Instance Method Summary collapse

Methods inherited from Base

expensive, #expensive?, expensive?, #initialize

Constructor Details

This class inherits a constructor from Trocla::Formats::Base

Instance Method Details

#format(plain_password, options = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/trocla/formats/x509.rb', line 6

def format(plain_password,options={})
  if plain_password.match(/-----BEGIN RSA PRIVATE KEY-----.*-----END RSA PRIVATE KEY-----.*-----BEGIN CERTIFICATE-----.*-----END CERTIFICATE-----/m)
    # just an import, don't generate any new keys
    return plain_password
  end

  cn = nil
  if options['subject']
    subject = options['subject']
    if cna = OpenSSL::X509::Name.parse(subject).to_a.find { |e| e[0] == 'CN' }
      cn = cna[1]
    end
  elsif options['CN']
    subject = ''
    cn = options['CN']
    ['C', 'ST', 'L', 'O', 'OU', 'CN', 'emailAddress'].each do |field|
      subject << "/#{field}=#{options[field]}" if options[field]
    end
  else
    raise 'You need to pass "subject" or "CN" as an option to use this format'
  end
  hash = options['hash'] || 'sha2'
  sign_with = options['ca']
  become_ca = options['become_ca'] || false
  keysize = options['keysize'] || 4096
  days = options['days'].nil? ? 365 : options['days'].to_i
  name_constraints = Array(options['name_constraints'])
  key_usages = options['key_usages']
  key_usages = Array(key_usages) if key_usages

  altnames = if become_ca || (an = options['altnames']) && Array(an).empty?
               []
             else
               # ensure that we have the CN with us, but only if it
               # it's like a hostname.
               # This might have to be improved.
               if cn.include?(' ')
                 Array(an).collect { |v| "DNS:#{v}" }.join(', ')
               else
                 (["DNS:#{cn}"] + Array(an).collect { |v| "DNS:#{v}" }).uniq.join(', ')
               end
             end

  begin
    key = mkkey(keysize)
  rescue Exception => e
    puts e.backtrace
    raise "Private key for #{subject} creation failed: #{e.message}"
  end

  cert = nil
  if sign_with # certificate signed with CA
    begin
      ca_str = trocla.get_password(sign_with, 'x509')
      ca = OpenSSL::X509::Certificate.new(ca_str)
      cakey = OpenSSL::PKey::RSA.new(ca_str)
      caserial = getserial(sign_with)
    rescue Exception => e
      raise "Value of #{sign_with} can't be loaded as CA: #{e.message}"
    end

    begin
      subj = OpenSSL::X509::Name.parse(subject)
      request = mkreq(subj, key.public_key)
      request.sign(key, signature(hash))
    rescue Exception => e
      raise "Certificate request #{subject} creation failed: #{e.message}"
    end

    begin
      cert = mkcert(
        caserial, request.subject, ca, request.public_key, days,
        altnames, key_usages, name_constraints, become_ca
      )
      cert.sign(cakey, signature(hash))
      addserial(sign_with, caserial)
    rescue Exception => e
      raise "Certificate #{subject} signing failed: #{e.message}"
    end
  else # self-signed certificate
    begin
      subj = OpenSSL::X509::Name.parse(subject)
      cert = mkcert(
        getserial(subj), subj, nil, key.public_key, days,
        altnames, key_usages, name_constraints, become_ca
      )
      cert.sign(key, signature(hash))
    rescue Exception => e
      raise "Self-signed certificate #{subject} creation failed: #{e.message}"
    end
  end
  key.to_pem + cert.to_pem
end

#render(output, render_options = {}) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/trocla/formats/x509.rb', line 100

def render(output, render_options = {})
  if render_options['keyonly']
    OpenSSL::PKey::RSA.new(output).to_pem
  elsif render_options['certonly']
    OpenSSL::X509::Certificate.new(output).to_pem
  elsif render_options['publickeyonly']
    OpenSSL::PKey::RSA.new(output).public_key.to_pem
  else
    super(output, render_options)
  end
end