48c1ead204e787223e58a52b084b33bb2c4fe8ec
[strongswan.git] / src / pki / commands / issue.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16 #include <time.h>
17
18 #include "pki.h"
19
20 #include <debug.h>
21 #include <utils/linked_list.h>
22 #include <credentials/certificates/certificate.h>
23 #include <credentials/certificates/x509.h>
24 #include <credentials/certificates/pkcs10.h>
25
26 /**
27 * Issue a certificate using a CA certificate and key
28 */
29 static int issue()
30 {
31 hash_algorithm_t digest = HASH_SHA1;
32 certificate_t *cert_req = NULL, *cert = NULL, *ca =NULL;
33 private_key_t *private = NULL;
34 public_key_t *public = NULL;
35 bool pkcs10 = FALSE;
36 char *file = NULL, *dn = NULL, *hex = NULL, *cacert = NULL, *cakey = NULL;
37 char *error = NULL;
38 identification_t *id = NULL;
39 linked_list_t *san, *cdps, *ocsp;
40 int lifetime = 1080;
41 chunk_t serial = chunk_empty;
42 chunk_t encoding = chunk_empty;
43 time_t not_before, not_after;
44 x509_flag_t flags = 0;
45 x509_t *x509;
46 char *arg;
47
48 san = linked_list_create();
49 cdps = linked_list_create();
50 ocsp = linked_list_create();
51
52 while (TRUE)
53 {
54 switch (command_getopt(&arg))
55 {
56 case 'h':
57 goto usage;
58 case 't':
59 if (streq(arg, "pkcs10"))
60 {
61 pkcs10 = TRUE;
62 }
63 else if (!streq(arg, "pub"))
64 {
65 error = "invalid input type";
66 goto usage;
67 }
68 continue;
69 case 'g':
70 digest = get_digest(arg);
71 if (digest == HASH_UNKNOWN)
72 {
73 error = "invalid --digest type";
74 goto usage;
75 }
76 continue;
77 case 'i':
78 file = arg;
79 continue;
80 case 'c':
81 cacert = arg;
82 continue;
83 case 'k':
84 cakey = arg;
85 continue;
86 case 'd':
87 dn = arg;
88 continue;
89 case 'a':
90 san->insert_last(san, identification_create_from_string(arg));
91 continue;
92 case 'l':
93 lifetime = atoi(arg);
94 if (!lifetime)
95 {
96 error = "invalid --lifetime value";
97 goto usage;
98 }
99 continue;
100 case 's':
101 hex = arg;
102 continue;
103 case 'b':
104 flags |= X509_CA;
105 continue;
106 case 'f':
107 if (streq(arg, "serverAuth"))
108 {
109 flags |= X509_SERVER_AUTH;
110 }
111 else if (streq(arg, "ocspSigning"))
112 {
113 flags |= X509_OCSP_SIGNER;
114 }
115 continue;
116 case 'u':
117 cdps->insert_last(cdps, arg);
118 continue;
119 case 'o':
120 ocsp->insert_last(ocsp, arg);
121 continue;
122 case EOF:
123 break;
124 default:
125 error = "invalid --issue option";
126 goto usage;
127 }
128 break;
129 }
130
131 if (!pkcs10 && !dn)
132 {
133 error = "--dn is required";
134 goto usage;
135 }
136 if (!cacert)
137 {
138 error = "--cacert is required";
139 goto usage;
140 }
141 if (!cakey)
142 {
143 error = "--cakey is required";
144 goto usage;
145 }
146 if (dn)
147 {
148 id = identification_create_from_string(dn);
149 if (id->get_type(id) != ID_DER_ASN1_DN)
150 {
151 error = "supplied --dn is not a distinguished name";
152 goto end;
153 }
154 }
155
156 DBG2("Reading ca certificate:");
157 ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
158 BUILD_FROM_FILE, cacert, BUILD_END);
159 if (!ca)
160 {
161 error = "parsing CA certificate failed";
162 goto end;
163 }
164 x509 = (x509_t*)ca;
165 if (!(x509->get_flags(x509) & X509_CA))
166 {
167 error = "CA certificate misses CA basicConstraint";
168 goto end;
169 }
170 public = ca->get_public_key(ca);
171 if (!public)
172 {
173 error = "extracting CA certificate public key failed";
174 goto end;
175 }
176
177 DBG2("Reading ca private key:");
178 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
179 public->get_type(public),
180 BUILD_FROM_FILE, cakey, BUILD_END);
181 if (!private)
182 {
183 error = "parsing CA private key failed";
184 goto end;
185 }
186 if (!private->belongs_to(private, public))
187 {
188 error = "CA private key does not match CA certificate";
189 goto end;
190 }
191 public->destroy(public);
192
193 if (hex)
194 {
195 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
196 }
197 else
198 {
199 rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
200
201 if (!rng)
202 {
203 error = "no random number generator found";
204 goto end;
205 }
206 rng->allocate_bytes(rng, 8, &serial);
207 rng->destroy(rng);
208 }
209
210 if (pkcs10)
211 {
212 enumerator_t *enumerator;
213 identification_t *subjectAltName;
214 pkcs10_t *req;
215
216 DBG2("Reading certificate request");
217 if (file)
218 {
219 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
220 CERT_PKCS10_REQUEST,
221 BUILD_FROM_FILE, file, BUILD_END);
222 }
223 else
224 {
225 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
226 CERT_PKCS10_REQUEST,
227 BUILD_FROM_FD, 0, BUILD_END);
228 }
229 if (!cert_req)
230 {
231 error = "parsing certificate request failed";
232 goto end;
233 }
234
235 /* If not set yet use subject from PKCS#10 certificate request as DN */
236 if (!id)
237 {
238 id = cert_req->get_subject(cert_req);
239 id = id->clone(id);
240 }
241
242 /* Add subjectAltNames from PKCS#10 certificate request */
243 req = (pkcs10_t*)cert_req;
244 enumerator = req->create_subjectAltName_enumerator(req);
245 while (enumerator->enumerate(enumerator, &subjectAltName))
246 {
247 san->insert_last(san, subjectAltName->clone(subjectAltName));
248 }
249 enumerator->destroy(enumerator);
250
251 /* Use public key from PKCS#10 certificate request */
252 public = cert_req->get_public_key(cert_req);
253 }
254 else
255 {
256 DBG2("Reading public key:");
257 if (file)
258 {
259 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
260 BUILD_FROM_FILE, file, BUILD_END);
261 }
262 else
263 {
264 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
265 BUILD_FROM_FD, 0, BUILD_END);
266 }
267 }
268 if (!public)
269 {
270 error = "parsing public key failed";
271 goto end;
272 }
273
274 not_before = time(NULL);
275 not_after = not_before + lifetime * 24 * 60 * 60;
276
277 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
278 BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
279 BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
280 BUILD_NOT_BEFORE_TIME, not_before, BUILD_DIGEST_ALG, digest,
281 BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
282 BUILD_SUBJECT_ALTNAMES, san, BUILD_X509_FLAG, flags,
283 BUILD_CRL_DISTRIBUTION_POINTS, cdps,
284 BUILD_OCSP_ACCESS_LOCATIONS, ocsp, BUILD_END);
285 if (!cert)
286 {
287 error = "generating certificate failed";
288 goto end;
289 }
290 encoding = cert->get_encoding(cert);
291 if (!encoding.ptr)
292 {
293 error = "encoding certificate failed";
294 goto end;
295 }
296 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
297 {
298 error = "writing certificate key failed";
299 goto end;
300 }
301
302 end:
303 DESTROY_IF(id);
304 DESTROY_IF(cert_req);
305 DESTROY_IF(cert);
306 DESTROY_IF(ca);
307 DESTROY_IF(public);
308 DESTROY_IF(private);
309 san->destroy_offset(san, offsetof(identification_t, destroy));
310 cdps->destroy(cdps);
311 ocsp->destroy(ocsp);
312 free(encoding.ptr);
313 free(serial.ptr);
314
315 if (error)
316 {
317 fprintf(stderr, "%s\n", error);
318 return 1;
319 }
320 return 0;
321
322 usage:
323 san->destroy_offset(san, offsetof(identification_t, destroy));
324 cdps->destroy(cdps);
325 ocsp->destroy(ocsp);
326 return command_usage(error);
327 }
328
329 /**
330 * Register the command.
331 */
332 static void __attribute__ ((constructor))reg()
333 {
334 command_register((command_t) {
335 issue, 'i', "issue",
336 "issue a certificate using a CA certificate and key",
337 {"[--in file] [--type pub|pkcs10]",
338 " --cacert file --cakey file --dn subject-dn [--san subjectAltName]+",
339 "[--lifetime days] [--serial hex] [--ca] [--crl uri]+ [--ocsp uri]+",
340 "[--flag serverAuth|ocspSigning]+",
341 "[--digest md5|sha1|sha224|sha256|sha384|sha512]"},
342 {
343 {"help", 'h', 0, "show usage information"},
344 {"in", 'i', 1, "public key/request file to issue, default: stdin"},
345 {"type", 't', 1, "type of input, default: pub"},
346 {"cacert", 'c', 1, "CA certificate file"},
347 {"cakey", 'k', 1, "CA private key file"},
348 {"dn", 'd', 1, "distinguished name to include as subject"},
349 {"san", 'a', 1, "subjectAltName to include in certificate"},
350 {"lifetime",'l', 1, "days the certificate is valid, default: 1080"},
351 {"serial", 's', 1, "serial number in hex, default: random"},
352 {"ca", 'b', 0, "include CA basicConstraint, default: no"},
353 {"flag", 'f', 1, "include extendedKeyUsage flag"},
354 {"crl", 'u', 1, "CRL distribution point URI to include"},
355 {"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
356 {"digest", 'g', 1, "digest for signature creation, default: sha1"},
357 }
358 });
359 }
360