4
%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
24
-record(dn, {commonName,
25
organizationalUnitName = "Erlang OTP",
26
organizationName = "Ericsson AB",
27
localityName = "Stockholm",
29
emailAddress = "peter@erix.ericsson.se"}).
31
all(DataDir, PrivDir) ->
32
OpenSSLCmd = "openssl",
33
create_rnd(DataDir, PrivDir), % For all requests
34
rootCA(PrivDir, OpenSSLCmd, "erlangCA"),
35
intermediateCA(PrivDir, OpenSSLCmd, "otpCA", "erlangCA"),
36
endusers(PrivDir, OpenSSLCmd, "otpCA", ["client", "server"]),
37
collect_certs(PrivDir, ["erlangCA", "otpCA"], ["client", "server"]),
38
%% Create keycert files
39
SDir = filename:join([PrivDir, "server"]),
40
SC = filename:join([SDir, "cert.pem"]),
41
SK = filename:join([SDir, "key.pem"]),
42
SKC = filename:join([SDir, "keycert.pem"]),
43
append_files([SK, SC], SKC),
44
CDir = filename:join([PrivDir, "client"]),
45
CC = filename:join([CDir, "cert.pem"]),
46
CK = filename:join([CDir, "key.pem"]),
47
CKC = filename:join([CDir, "keycert.pem"]),
48
append_files([CK, CC], CKC),
51
append_files(FileNames, ResultFileName) ->
52
{ok, ResultFile} = file:open(ResultFileName, [write]),
53
do_append_files(FileNames, ResultFile).
55
do_append_files([], RF) ->
57
do_append_files([F|Fs], RF) ->
58
{ok, Data} = file:read_file(F),
59
ok = file:write(RF, Data),
60
do_append_files(Fs, RF).
62
rootCA(Root, OpenSSLCmd, Name) ->
63
create_ca_dir(Root, Name, ca_cnf(Name)),
64
DN = #dn{commonName = Name},
65
create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
68
intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
70
create_ca_dir(Root, CA, ca_cnf(CA)),
71
CARoot = filename:join([Root, CA]),
72
DN = #dn{commonName = CA},
73
CnfFile = filename:join([CARoot, "req.cnf"]),
74
file:write_file(CnfFile, req_cnf(DN)),
75
KeyFile = filename:join([CARoot, "private", "key.pem"]),
76
ReqFile = filename:join([CARoot, "req.pem"]),
77
create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
78
CertFile = filename:join([CARoot, "cert.pem"]),
79
sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
81
endusers(Root, OpenSSLCmd, CA, Users) ->
82
lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
84
enduser(Root, OpenSSLCmd, CA, User) ->
85
UsrRoot = filename:join([Root, User]),
86
file:make_dir(UsrRoot),
87
CnfFile = filename:join([UsrRoot, "req.cnf"]),
88
DN = #dn{commonName = User},
89
file:write_file(CnfFile, req_cnf(DN)),
90
KeyFile = filename:join([UsrRoot, "key.pem"]),
91
ReqFile = filename:join([UsrRoot, "req.pem"]),
92
create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
93
CertFileAllUsage = filename:join([UsrRoot, "cert.pem"]),
94
sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFileAllUsage),
95
CertFileDigitalSigOnly = filename:join([UsrRoot, "digital_signature_only_cert.pem"]),
96
sign_req(Root, OpenSSLCmd, CA, "user_cert_digital_signature_only", ReqFile, CertFileDigitalSigOnly).
98
collect_certs(Root, CAs, Users) ->
101
File = filename:join([Root, CA, "cert.pem"]),
102
{ok, Bin} = file:read_file(File),
107
File = filename:join([Root, User, "cacerts.pem"]),
108
file:write_file(File, Bins)
111
create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
112
CARoot = filename:join([Root, CAName]),
113
CnfFile = filename:join([CARoot, "req.cnf"]),
114
file:write_file(CnfFile, Cnf),
115
KeyFile = filename:join([CARoot, "private", "key.pem"]),
116
CertFile = filename:join([CARoot, "cert.pem"]),
117
Cmd = [OpenSSLCmd, " req"
120
" -config ", CnfFile,
121
" -keyout ", KeyFile,
123
Env = [{"ROOTDIR", Root}],
126
create_ca_dir(Root, CAName, Cnf) ->
127
CARoot = filename:join([Root, CAName]),
128
file:make_dir(CARoot),
129
create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
130
create_rnd(Root, filename:join([CAName, "private"])),
131
create_files(CARoot, [{"serial", "01\n"},
135
create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
136
Cmd = [OpenSSLCmd, " req"
138
" -config ", CnfFile,
139
" -keyout ", KeyFile,
141
Env = [{"ROOTDIR", Root}],
144
sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
145
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
146
Cmd = [OpenSSLCmd, " ca"
149
" -config ", CACnfFile,
150
" -extensions ", CertType,
153
Env = [{"ROOTDIR", Root}],
160
create_dirs(Root, Dirs) ->
161
lists:foreach(fun(Dir) ->
162
file:make_dir(filename:join([Root, Dir])) end,
165
create_files(Root, NameContents) ->
167
fun({Name, Contents}) ->
168
file:write_file(filename:join([Root, Name]), Contents) end,
171
create_rnd(FromDir, ToDir) ->
172
From = filename:join([FromDir, "RAND"]),
173
To = filename:join([ToDir, "RAND"]),
177
File = filename:join([Dir, "RAND"]),
181
FCmd = lists:flatten(Cmd),
182
Port = open_port({spawn, FCmd}, [stream, eof, exit_status, stderr_to_stdout,
194
{Port, {exit_status, Status}} when Status /= 0 ->
195
%% io:fwrite("exit status: ~w~n", [Status]),
196
exit({eval_cmd, Status})
202
%% Contents of configuration files
206
["# Purpose: Configuration for requests (end users and CAs)."
208
"ROOTDIR = $ENV::ROOTDIR\n"
212
"input_password = secret\n"
213
"output_password = secret\n"
214
"default_bits = 1024\n"
215
"RANDFILE = $ROOTDIR/RAND\n"
217
"default_md = sha1\n"
218
"#string_mask = pkix\n"
219
"x509_extensions = ca_ext\n"
221
"distinguished_name= name\n"
225
"commonName = ", DN#dn.commonName, "\n"
226
"organizationalUnitName = ", DN#dn.organizationalUnitName, "\n"
227
"organizationName = ", DN#dn.organizationName, "\n"
228
"localityName = ", DN#dn.localityName, "\n"
229
"countryName = ", DN#dn.countryName, "\n"
230
"emailAddress = ", DN#dn.emailAddress, "\n"
234
"basicConstraints = critical, CA:true\n"
235
"keyUsage = cRLSign, keyCertSign\n"
236
"subjectKeyIdentifier = hash\n"
237
"subjectAltName = email:copy\n"].
241
["# Purpose: Configuration for CAs.\n"
243
"ROOTDIR = $ENV::ROOTDIR\n"
248
"dir = $ROOTDIR/", CA, "\n"
249
"certs = $dir/certs\n"
250
"crl_dir = $dir/crl\n"
251
"database = $dir/index.txt\n"
252
"new_certs_dir = $dir/newcerts\n"
253
"certificate = $dir/cert.pem\n"
254
"serial = $dir/serial\n"
255
"crl = $dir/crl.pem\n"
256
"private_key = $dir/private/key.pem\n"
257
"RANDFILE = $dir/private/RAND\n"
259
"x509_extensions = user_cert\n"
260
"unique_subject = no\n"
261
"default_days = 3600\n"
262
"default_md = sha1\n"
264
"policy = policy_match\n"
268
"commonName = supplied\n"
269
"organizationalUnitName = optional\n"
270
"organizationName = match\n"
271
"countryName = match\n"
272
"localityName = match\n"
273
"emailAddress = supplied\n"
277
"basicConstraints = CA:false\n"
278
"keyUsage = nonRepudiation, digitalSignature, keyEncipherment\n"
279
"subjectKeyIdentifier = hash\n"
280
"authorityKeyIdentifier = keyid,issuer:always\n"
281
"subjectAltName = email:copy\n"
282
"issuerAltName = issuer:copy\n"
285
"[user_cert_digital_signature_only]\n"
286
"basicConstraints = CA:false\n"
287
"keyUsage = digitalSignature\n"
288
"subjectKeyIdentifier = hash\n"
289
"authorityKeyIdentifier = keyid,issuer:always\n"
290
"subjectAltName = email:copy\n"
291
"issuerAltName = issuer:copy\n"
295
"basicConstraints = critical,CA:true\n"
296
"keyUsage = cRLSign, keyCertSign\n"
297
"subjectKeyIdentifier = hash\n"
298
"authorityKeyIdentifier = keyid:always,issuer:always\n"
299
"subjectAltName = email:copy\n"
300
"issuerAltName = issuer:copy\n"].