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
|
class Trocla::Formats::X509
require 'openssl'
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
if options['subject']
subject = options['subject']
elsif options['CN']
subject = ''
['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
sign_with = options['ca'] || nil
keysize = options['keysize'] || 2048
serial = options['serial'] || 1
days = options['days'] || 365
altnames = options['altnames'] || nil
altnames.collect { |v| "DNS:#{v}" }.join(', ') if altnames
# nice help: https://gist.github.com/mitfik/1922961
def mkkey(len)
OpenSSL::PKey::RSA.generate(len)
end
def mkreq(subject,public_key)
request = OpenSSL::X509::Request.new
request.version = 0
request.subject = subject
request.public_key = public_key
request
end
def mkcert(serial,subject,issuer,public_key,days,altnames)
cert = OpenSSL::X509::Certificate.new
issuer = cert if issuer == nil
cert.subject = subject
cert.issuer = issuer.subject
cert.not_before = Time.now
cert.not_after = Time.now + days * 24 * 60 * 60
cert.public_key = public_key
cert.serial = serial
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = issuer
cert.extensions = [ ef.create_extension("subjectKeyIdentifier", "hash") ]
cert.add_extension ef.create_extension("basicConstraints","CA:TRUE", true) if subject == issuer
cert.add_extension ef.create_extension("basicConstraints","CA:FALSE", true) if subject != issuer
cert.add_extension ef.create_extension("keyUsage", "nonRepudiation, digitalSignature, keyEncipherment", true)
cert.add_extension ef.create_extension("subjectAltName", altnames, true) if altnames
cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")
cert
end
def getca(ca)
subreq = Trocla.new
subreq.get_password(ca,'x509')
end
def getserial(ca,serial)
subreq = Trocla.new
newser = subreq.get_password("#{ca}_serial",'plain')
if newser
newser + 1
else
serial
end
end
def setserial(ca,serial)
subreq = Trocla.new
subreq.set_password("#{ca}_serial",'plain',serial)
end
begin
key = mkkey(keysize)
rescue Exception => e
raise "Private key for #{subject} creation failed: #{e.message}"
end
if sign_with # certificate signed with CA
begin
ca = OpenSSL::X509::Certificate.new(getca(sign_with))
cakey = OpenSSL::PKey::RSA.new(getca(sign_with))
caserial = getserial(sign_with, serial)
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, OpenSSL::Digest::SHA1.new)
rescue Exception => e
raise "Certificate request #{subject} creation failed: #{e.message}"
end
begin
csr_cert = mkcert(caserial, request.subject, ca, request.public_key, days, altnames)
csr_cert.sign(cakey, OpenSSL::Digest::SHA1.new)
setserial(sign_with, caserial)
rescue Exception => e
raise "Certificate #{subject} signing failed: #{e.message}"
end
key.send("to_pem") + csr_cert.send("to_pem")
else # self-signed certificate
begin
subj = OpenSSL::X509::Name.parse(subject)
cert = mkcert(serial, subj, nil, key.public_key, days, altnames)
cert.sign(key, OpenSSL::Digest::SHA1.new)
rescue Exception => e
raise "Self-signed certificate #{subject} creation failed: #{e.message}"
end
key.send("to_pem") + cert.send("to_pem")
end
end
end
|