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
|
#!/usr/bin/env ruby
require 'openssl'
require 'ca_config'
require 'fileutils'
require 'getopts'
include OpenSSL
def usage
myname = File::basename($0)
$stderr.puts "Usage: #{myname} csr_file [--type (client|server|ca|ocsp)]"
exit
end
getopts nil, 'type:client'
cert_type = $OPT_type
csr_file = ARGV.shift or usage
ARGV.empty? or usage
csr = X509::Request.new(File.open(csr_file).read)
unless csr.verify(csr.public_key)
raise "CSR sign verification failed."
end
# Only checks signature here. You must verify CSR according to your CP/CPS.
$stdout.sync = true
# CA setup
ca_file = CAConfig::CERT_FILE
puts "Reading CA cert (from #{ca_file})"
ca = X509::Certificate.new(File.read(ca_file))
ca_keypair_file = CAConfig::KEYPAIR_FILE
puts "Reading CA keypair (from #{ca_keypair_file})"
ca_keypair = PKey::RSA.new(File.read(ca_keypair_file), &CAConfig::PASSWD_CB)
serial = File.open(CAConfig::SERIAL_FILE, "r").read.chomp.hex
File.open(CAConfig::SERIAL_FILE, "w") do |f|
f << sprintf("%04X", serial + 1)
end
# Generate new cert
cert = X509::Certificate.new
from = Time.now # + 30 * 60 # Wait 30 minutes.
cert.subject = csr.subject
cert.issuer = ca.subject
cert.not_before = from
cert.not_after = from + CAConfig::CERT_DAYS * 24 * 60 * 60
cert.public_key = csr.public_key
cert.serial = serial
cert.version = 2 # X509v3
basic_constraint = nil
key_usage = []
ext_key_usage = []
case cert_type
when "ca"
basic_constraint = "CA:TRUE,pathlen:0"
key_usage << "cRLSign" << "keyCertSign"
when "server"
basic_constraint = "CA:FALSE"
key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
key_usage << "dataEncipherment"
ext_key_usage << "serverAuth"
when "ocsp"
basic_constraint = "CA:FALSE"
key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
key_usage << "dataEncipherment"
ext_key_usage << "serverAuth" << "OCSPSigning"
when "client"
basic_constraint = "CA:FALSE"
key_usage << "nonRepudiation" << "digitalSignature" << "keyEncipherment"
ext_key_usage << "clientAuth" << "codeSigning" << "emailProtection"
else
raise "unknonw cert type \"#{cert_type}\" is specified."
end
ef = X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = ca
ex = []
ex << ef.create_extension("basicConstraints", basic_constraint, true)
ex << ef.create_extension("nsComment","Ruby/OpenSSL Generated Certificate")
ex << ef.create_extension("subjectKeyIdentifier", "hash")
ex << ef.create_extension("nsCertType", "client,email")
ex << ef.create_extension("keyUsage", key_usage.join(",")) unless key_usage.empty?
ex << ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
ex << ef.create_extension("extendedKeyUsage", ext_key_usage.join(",")) unless ext_key_usage.empty?
ex << ef.create_extension("crlDistributionPoints", CAConfig::CDP_LOCATION) if CAConfig::CDP_LOCATION
ex << ef.create_extension("authorityInfoAccess", "OCSP;" << CAConfig::OCSP_LOCATION) if CAConfig::OCSP_LOCATION
cert.extensions = ex
cert.sign(ca_keypair, Digest::SHA1.new)
# For backup
cert_file = CAConfig::NEW_CERTS_DIR + "/#{cert.serial}_cert.pem"
File.open(cert_file, "w", 0644) do |f|
f << cert.to_pem
end
puts "Writing cert.pem..."
FileUtils.copy(cert_file, "cert.pem")
puts "DONE. (Generated certificate for '#{cert.subject}')"
|