summaryrefslogtreecommitdiff
path: root/nullca
blob: f503ac04e716fb8da08cfaff3f098bafee23660d (plain)
1
2
3
4
5
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env ruby
# encoding: utf-8
# vim: ts=4:sw=4:et:
require "openssl"
require "optparse"
require "pp"
require_relative "lib/backend"
require_relative "lib/cert_util"
require_relative "lib/issuer"
require_relative "lib/openssl_ext"
#require_relative "lib/x509_ext" # not yet used, only needed for KRB5: names

def import_pubkey(path)
    buf = File.read(path)
    if buf =~ /^-----BEGIN (?:NEW )?CERTIFICATE REQUEST-----\r?$/
        req = OpenSSL::X509::Request.new(buf)
        return req.public_key
    elsif buf =~ /^-----BEGIN CERTIFICATE-----\r?$/
        cert = OpenSSL::X509::Certificate.new(buf)
        return cert.public_key
    elsif buf =~ /^-----BEGIN PUBLIC KEY-----\r?$/
        key = OpenSSL::PKey.read(buf)
        return key.public_key
    elsif buf =~ /^-----BEGIN (?:RSA )?PRIVATE KEY-----\r?$/
        key = OpenSSL::PKey.read(buf)
        if key.is_a? OpenSSL::PKey::EC
            # The .public_key is a PKey::EC::Point which won't work
            return key
        else
            pp key
            return key.public_key
        end
    else
        raise "unrecognized request or public key file: #{path}"
    end
end

def import_request_subject(path)
    buf = File.read(path)
    if buf =~ /^-----BEGIN (?:NEW )?CERTIFICATE REQUEST-----\r?$/
        req = OpenSSL::X509::Request.new(buf)
        return req.subject.to_rfc2253
    elsif buf =~ /^-----BEGIN CERTIFICATE-----\r?$/
        cert = OpenSSL::X509::Certificate.new(buf)
        return cert.subject.to_rfc2253
    else
        raise "unrecognized request file: #{path}"
    end
end

arg_dbdir = nil
arg_request = nil
arg_template = nil
arg_subject = nil
arg_aliases = []
arg_unit = nil
arg_days = nil
arg_output = nil

OptionParser.new do |parser|
    parser.on("-d", "--ca-dir PATH", String, "Path to CA database") do |arg|
        arg_dbdir = arg
    end
    parser.on("-r", "--request FILE", String, "Input request or public key") do |arg|
        arg_request = arg
    end
    parser.on("-t", "--type NAME", String, "Certificate template") do |arg|
        arg_template = arg
    end
    parser.on("-s", "--subject NAME", String, "Certificate subject") do |arg|
        arg_subject = arg
    end
    parser.on("-a", "--alias HOSTNAME", String, "Additional subject name") do |arg|
        arg_aliases << arg
    end
    parser.on("-u", "--ou NAME", "Organizational unit") do |arg|
        arg_unit = arg
    end
    parser.on("-y", "--days DAYS", "Certificate lifetime") do |arg|
        arg_days = parse_lifetime(arg)
    end
    parser.on("-o", "--output FILE", String, "Output certificate filename") do |arg|
        arg_output = arg
    end
    parser.on("--[no-]debug", "Enable pkcs11-provider debugging") do |arg|
        ENV["PKCS11_PROVIDER_DEBUG"] = "file:/dev/stderr,level:2"
    end
    # Prevent short options like "-d" from matching "--debug" if they aren't
    # explicitly declared.
    #parser.require_exact = true
end.parse!

if !arg_dbdir
    arg_dbdir = File.readlink(".default") rescue "root"
end

if !arg_request
    raise "request or public key not specified"
end

if !arg_template
    raise "template not specified"
end

if !arg_subject
    if arg_template == "subca"
        arg_subject = import_request_subject(arg_request)
    else
        raise "Subject name not specified"
    end
end

pubkey = import_pubkey(arg_request)
puts "Loaded request:"
puts "    Public key: #{pubkey}"

ca = Backend.new(arg_dbdir)
puts "Loaded CA chain:"
ca.chain.each do |cert|
    puts "    #{cert.subject.to_rfc2253}"
end

iss = Issuer.new(ca)
params = [pubkey, arg_subject, arg_aliases, arg_unit, arg_days]
case arg_template
    when "host"
        cert = iss.issue_host(*params)
    when "user"
        cert = iss.issue_client(*params)
    when "addc"
        cert = iss.issue_ad_dc(*params)
    when "device"
        cert = iss.issue_device(*params)
    when "subca"
        cert = iss.issue_subca(*params)
    when "code"
        cert = iss.issue_code(*params)
    else
        raise "Unknown certificate type: #{arg_template.inspect}"
end

if cert.nil?
    raise "certificate issuance was aborted"
end

#puts "got cert:"
#puts cert.to_text.gsub(/^\s+([0-9a-f:])+\n/, "")

if !arg_output
    arg_output = "#{cert_to_filename(cert)}.crt"
end

File.open(arg_output, "w") do |fh|
    fh.write(cert.to_pem)
    fh.write(ca.chain.map(&:to_pem).join)
end

puts "Saved certificate to: #{arg_output}"