Top Level Namespace

Instance Method Summary collapse

Instance Method Details

#ask_password(name) ⇒ Object



18
19
20
21
22
23
24
25
26
27
# File 'lib/functions.rb', line 18

def ask_password(name)
  password = ''
  loop do
    print "Enter password for #{name}.key: "
    password = $stdin.noecho(&:gets).chomp
    puts # trailing newline
    break unless password.empty?
  end
  password
end

#basic_cert(type, cn) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/functions.rb', line 74

def basic_cert(type, cn)
  cert = OpenSSL::X509::Certificate.new

  cert.version = 2
  cert.subject = OpenSSL::X509::Name.new([['CN', cn]] + REQ.to_a)
  cert.issuer = OpenSSL::X509::Name.new([['CN', CN_CA]] + REQ.to_a)
  cert.not_before = Time.now
  cert.not_after = time_after_days(EXPIRE[type])

  cert
end

#check_client(name) ⇒ Object



9
10
11
12
# File 'lib/functions.rb', line 9

def check_client(name)
  abort 'Error: client should have an alphanumeric name' unless name
  check_crt(name)
end

#check_crt(filename) ⇒ Object



3
4
5
6
7
# File 'lib/functions.rb', line 3

def check_crt(filename)
  %w[key crt].each {|ext|
    abort "#{filename}.#{ext} already exists, exiting" if File.exist? "#{filename}.#{ext}"
  }
end

#create_dir(name) ⇒ Object



153
154
155
156
157
158
# File 'lib/functions.rb', line 153

def create_dir(name)
  return if Dir.exist? name

  Dir.mkdir name
  puts "Created directory: #{name}"
end

#customize_cert(type, cert) ⇒ Object

rubocop:disable Metrics/MethodLength rubocop:disable Metrics/AbcSize



92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/functions.rb', line 92

def customize_cert(type, cert)
  # rubocop:enable Metrics/AbcSize
  # rubocop:enable Metrics/MethodLength

  ef = OpenSSL::X509::ExtensionFactory.new nil, cert
  ef.issuer_certificate = cert

  cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
  cert.add_extension ef.create_extension('authorityKeyIdentifier', 'keyid,issuer:always')
  cert.add_extension ef.create_extension('basicConstraints', type == 'ca' ? 'CA:true' : 'CA:false')

  case type
  when 'ca'
    cert.add_extension ef.create_extension('keyUsage', 'cRLSign,keyCertSign')
  when 'server'
    cert.add_extension ef.create_extension('keyUsage', 'keyEncipherment,digitalSignature')
    cert.add_extension ef.create_extension('extendedKeyUsage', 'serverAuth')
  when 'client'
    cert.add_extension ef.create_extension('keyUsage', 'digitalSignature')
    cert.add_extension ef.create_extension('extendedKeyUsage', 'clientAuth')
  end
  cert
end

#exe(cmd) ⇒ Object



14
15
16
# File 'lib/functions.rb', line 14

def exe(cmd)
  system(cmd) || abort("error executing: #{cmd}")
end

#gen_and_sign(type, certname, password) ⇒ Object



40
41
42
43
# File 'lib/functions.rb', line 40

def gen_and_sign(type, certname, password)
  gen_key(certname, password)
  sign_key(type, certname, password)
end

#gen_cert(type, cn, pubkey, serial) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/functions.rb', line 66

def gen_cert(type, cn, pubkey, serial)
  cert = basic_cert(type, cn)
  cert.public_key = pubkey
  cert.serial = serial

  customize_cert(type, cert)
end

#gen_crl(ca_pass) ⇒ Object



131
132
133
134
135
136
137
# File 'lib/functions.rb', line 131

def gen_crl(ca_pass)
  return if File.exist? CRL_FILE

  crl = OpenSSL::X509::CRL.new
  crl.issuer = OpenSSL::X509::Name.new([['CN', CN_CA]] + REQ.to_a)
  update_crl(crl, ca_pass)
end

#gen_key(certname, password) ⇒ Object



45
46
47
48
49
50
# File 'lib/functions.rb', line 45

def gen_key(certname, password)
  key = OpenSSL::PKey::RSA.new(KEY_SIZE)
  File.open("#{certname}.key", 'w') do |f|
    f.write password ? key.to_pem(OpenSSL::Cipher.new(ENCRYPT), password) : key
  end
end

#new_serialObject



147
148
149
150
151
# File 'lib/functions.rb', line 147

def new_serial
  File.read(SERIAL_FILE).to_i + 1
rescue Errno::ENOENT
  0
end

#revoke(certname) ⇒ Object

rubocop:disable Metrics/AbcSize



117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/functions.rb', line 117

def revoke(certname)
  # rubocop:enable Metrics/AbcSize
  crl = OpenSSL::X509::CRL.new(File.read(CRL_FILE))
  cert = OpenSSL::X509::Certificate.new(File.read("#{certname}.crt"))
  revoke = OpenSSL::X509::Revoked.new.tap {|rev|
    rev.serial = cert.serial
    rev.time = Time.now
  }
  crl.next_update = time_after_days(EXPIRE['crl'])
  crl.add_revoked(revoke)
  update_crl(crl, '')
  %w[crt key].each {|ext| File.delete "#{certname}.#{ext}" }
end

#sign_key(type, cn, password) ⇒ Object

type is one of: ‘ca’, ‘server’, ‘client’



53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/functions.rb', line 53

def sign_key(type, cn, password)
  certname = type == 'ca' ? 'ca' : cn
  key = OpenSSL::PKey::RSA.new File.read("#{certname}.key"), password
  serial = new_serial
  cert = gen_cert(type, cn, key.public_key, serial)

  ca_key = type == 'ca' ? key : unencrypt_ca_key
  cert.sign ca_key, OpenSSL::Digest.new(DIGEST)

  File.open(SERIAL_FILE, 'w') {|f| f.write serial }
  File.open("#{certname}.crt", 'w') {|f| f.write cert.to_pem }
end

#time_after_days(days) ⇒ Object



86
87
88
# File 'lib/functions.rb', line 86

def time_after_days(days)
  Time.now + days * 86_400 # days to seconds
end

#unencrypt_ca_key(pass = '') ⇒ Object



29
30
31
32
33
34
35
36
37
38
# File 'lib/functions.rb', line 29

def unencrypt_ca_key(pass = '')
  begin
    OpenSSL::PKey::RSA.new File.read('ca.key'), pass
  rescue OpenSSL::PKey::RSAError
    # this means pass is wrong, so ask for it
    OpenSSL::PKey::RSA.new File.read('ca.key'), ask_password('ca')
  end
rescue OpenSSL::PKey::RSAError
  retry
end

#update_crl(crl, ca_pass) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/functions.rb', line 139

def update_crl(crl, ca_pass)
  ca_key = unencrypt_ca_key(ca_pass)
  crl.last_update = Time.now
  crl.next_update = time_after_days(EXPIRE['crl'])
  crl.sign(ca_key, OpenSSL::Digest.new(DIGEST))
  File.open(CRL_FILE, 'w') {|f| f.write crl.to_pem }
end