pki tool shows and builds crlSign keyUsage
[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 cred_encoding_type_t form = CERT_ASN1_DER;
32 hash_algorithm_t digest = HASH_SHA1;
33 certificate_t *cert_req = NULL, *cert = NULL, *ca =NULL;
34 private_key_t *private = NULL;
35 public_key_t *public = NULL;
36 bool pkcs10 = FALSE;
37 char *file = NULL, *dn = NULL, *hex = NULL, *cacert = NULL, *cakey = NULL;
38 char *error = NULL, *keyid = NULL;
39 identification_t *id = NULL, *crl_issuer = NULL;;
40 linked_list_t *san, *cdps, *ocsp;
41 int lifetime = 1095;
42 int pathlen = X509_NO_PATH_LEN_CONSTRAINT;
43 chunk_t serial = chunk_empty;
44 chunk_t encoding = chunk_empty;
45 time_t not_before, not_after;
46 x509_flag_t flags = 0;
47 x509_t *x509;
48 char *arg;
49
50 san = linked_list_create();
51 cdps = linked_list_create();
52 ocsp = linked_list_create();
53
54 while (TRUE)
55 {
56 switch (command_getopt(&arg))
57 {
58 case 'h':
59 goto usage;
60 case 't':
61 if (streq(arg, "pkcs10"))
62 {
63 pkcs10 = TRUE;
64 }
65 else if (!streq(arg, "pub"))
66 {
67 error = "invalid input type";
68 goto usage;
69 }
70 continue;
71 case 'g':
72 digest = get_digest(arg);
73 if (digest == HASH_UNKNOWN)
74 {
75 error = "invalid --digest type";
76 goto usage;
77 }
78 continue;
79 case 'i':
80 file = arg;
81 continue;
82 case 'c':
83 cacert = arg;
84 continue;
85 case 'k':
86 cakey = arg;
87 continue;
88 case 'x':
89 keyid = arg;
90 continue;
91 case 'd':
92 dn = arg;
93 continue;
94 case 'a':
95 san->insert_last(san, identification_create_from_string(arg));
96 continue;
97 case 'l':
98 lifetime = atoi(arg);
99 if (!lifetime)
100 {
101 error = "invalid --lifetime value";
102 goto usage;
103 }
104 continue;
105 case 's':
106 hex = arg;
107 continue;
108 case 'b':
109 flags |= X509_CA;
110 continue;
111 case 'p':
112 pathlen = atoi(arg);
113 continue;
114 case 'e':
115 if (streq(arg, "serverAuth"))
116 {
117 flags |= X509_SERVER_AUTH;
118 }
119 else if (streq(arg, "clientAuth"))
120 {
121 flags |= X509_CLIENT_AUTH;
122 }
123 else if (streq(arg, "crlSign"))
124 {
125 flags |= X509_CRL_SIGN;
126 }
127 else if (streq(arg, "ocspSigning"))
128 {
129 flags |= X509_OCSP_SIGNER;
130 }
131 continue;
132 case 'f':
133 if (!get_form(arg, &form, CRED_CERTIFICATE))
134 {
135 return command_usage("invalid output format");
136 }
137 continue;
138 case 'u':
139 cdps->insert_last(cdps, arg);
140 continue;
141 case 'I':
142 crl_issuer = identification_create_from_string(arg);
143 continue;
144 case 'o':
145 ocsp->insert_last(ocsp, arg);
146 continue;
147 case EOF:
148 break;
149 default:
150 error = "invalid --issue option";
151 goto usage;
152 }
153 break;
154 }
155
156 if (!pkcs10 && !dn)
157 {
158 error = "--dn is required";
159 goto usage;
160 }
161 if (!cacert)
162 {
163 error = "--cacert is required";
164 goto usage;
165 }
166 if (!cakey && !keyid)
167 {
168 error = "--cakey or --keyid is required";
169 goto usage;
170 }
171 if (dn)
172 {
173 id = identification_create_from_string(dn);
174 if (id->get_type(id) != ID_DER_ASN1_DN)
175 {
176 error = "supplied --dn is not a distinguished name";
177 goto end;
178 }
179 }
180
181 DBG2(DBG_LIB, "Reading ca certificate:");
182 ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
183 BUILD_FROM_FILE, cacert, BUILD_END);
184 if (!ca)
185 {
186 error = "parsing CA certificate failed";
187 goto end;
188 }
189 x509 = (x509_t*)ca;
190 if (!(x509->get_flags(x509) & X509_CA))
191 {
192 error = "CA certificate misses CA basicConstraint";
193 goto end;
194 }
195 public = ca->get_public_key(ca);
196 if (!public)
197 {
198 error = "extracting CA certificate public key failed";
199 goto end;
200 }
201
202 DBG2(DBG_LIB, "Reading ca private key:");
203 if (cakey)
204 {
205 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
206 public->get_type(public),
207 BUILD_FROM_FILE, cakey, BUILD_END);
208 }
209 else
210 {
211 chunk_t chunk;
212
213 chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
214 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
215 BUILD_PKCS11_KEYID, chunk, BUILD_END);
216 free(chunk.ptr);
217 }
218 if (!private)
219 {
220 error = "loading CA private key failed";
221 goto end;
222 }
223 if (!private->belongs_to(private, public))
224 {
225 error = "CA private key does not match CA certificate";
226 goto end;
227 }
228 public->destroy(public);
229
230 if (hex)
231 {
232 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
233 }
234 else
235 {
236 rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
237
238 if (!rng)
239 {
240 error = "no random number generator found";
241 goto end;
242 }
243 rng->allocate_bytes(rng, 8, &serial);
244 while (*serial.ptr == 0x00)
245 {
246 /* we don't accept a serial number with leading zeroes */
247 rng->get_bytes(rng, 1, serial.ptr);
248 }
249 rng->destroy(rng);
250 }
251
252 if (pkcs10)
253 {
254 enumerator_t *enumerator;
255 identification_t *subjectAltName;
256 pkcs10_t *req;
257
258 DBG2(DBG_LIB, "Reading certificate request");
259 if (file)
260 {
261 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
262 CERT_PKCS10_REQUEST,
263 BUILD_FROM_FILE, file, BUILD_END);
264 }
265 else
266 {
267 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
268 CERT_PKCS10_REQUEST,
269 BUILD_FROM_FD, 0, BUILD_END);
270 }
271 if (!cert_req)
272 {
273 error = "parsing certificate request failed";
274 goto end;
275 }
276
277 /* If not set yet use subject from PKCS#10 certificate request as DN */
278 if (!id)
279 {
280 id = cert_req->get_subject(cert_req);
281 id = id->clone(id);
282 }
283
284 /* Add subjectAltNames from PKCS#10 certificate request */
285 req = (pkcs10_t*)cert_req;
286 enumerator = req->create_subjectAltName_enumerator(req);
287 while (enumerator->enumerate(enumerator, &subjectAltName))
288 {
289 san->insert_last(san, subjectAltName->clone(subjectAltName));
290 }
291 enumerator->destroy(enumerator);
292
293 /* Use public key from PKCS#10 certificate request */
294 public = cert_req->get_public_key(cert_req);
295 }
296 else
297 {
298 DBG2(DBG_LIB, "Reading public key:");
299 if (file)
300 {
301 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
302 BUILD_FROM_FILE, file, BUILD_END);
303 }
304 else
305 {
306 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
307 BUILD_FROM_FD, 0, BUILD_END);
308 }
309 }
310 if (!public)
311 {
312 error = "parsing public key failed";
313 goto end;
314 }
315
316 not_before = time(NULL);
317 not_after = not_before + lifetime * 24 * 60 * 60;
318
319 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
320 BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
321 BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
322 BUILD_NOT_BEFORE_TIME, not_before, BUILD_DIGEST_ALG, digest,
323 BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
324 BUILD_SUBJECT_ALTNAMES, san, BUILD_X509_FLAG, flags,
325 BUILD_PATHLEN, pathlen,
326 BUILD_CRL_ISSUER, crl_issuer,
327 BUILD_CRL_DISTRIBUTION_POINTS, cdps,
328 BUILD_OCSP_ACCESS_LOCATIONS, ocsp, BUILD_END);
329 if (!cert)
330 {
331 error = "generating certificate failed";
332 goto end;
333 }
334 if (!cert->get_encoding(cert, form, &encoding))
335 {
336 error = "encoding certificate failed";
337 goto end;
338 }
339 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
340 {
341 error = "writing certificate key failed";
342 goto end;
343 }
344
345 end:
346 DESTROY_IF(id);
347 DESTROY_IF(cert_req);
348 DESTROY_IF(cert);
349 DESTROY_IF(ca);
350 DESTROY_IF(public);
351 DESTROY_IF(private);
352 san->destroy_offset(san, offsetof(identification_t, destroy));
353 cdps->destroy(cdps);
354 ocsp->destroy(ocsp);
355 DESTROY_IF(crl_issuer);
356 free(encoding.ptr);
357 free(serial.ptr);
358
359 if (error)
360 {
361 fprintf(stderr, "%s\n", error);
362 return 1;
363 }
364 return 0;
365
366 usage:
367 san->destroy_offset(san, offsetof(identification_t, destroy));
368 cdps->destroy(cdps);
369 ocsp->destroy(ocsp);
370 DESTROY_IF(crl_issuer);
371 return command_usage(error);
372 }
373
374 /**
375 * Register the command.
376 */
377 static void __attribute__ ((constructor))reg()
378 {
379 command_register((command_t) {
380 issue, 'i', "issue",
381 "issue a certificate using a CA certificate and key",
382 {"[--in file] [--type pub|pkcs10] --cakey file | --cakeyid hex",
383 " --cacert file --dn subject-dn [--san subjectAltName]+",
384 "[--lifetime days] [--serial hex] [--crl uri]+ [--ocsp uri]+",
385 "[--ca] [--pathlen len] [--flag serverAuth|clientAuth|crlSign|ocspSigning]+",
386 "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
387 {
388 {"help", 'h', 0, "show usage information"},
389 {"in", 'i', 1, "public key/request file to issue, default: stdin"},
390 {"type", 't', 1, "type of input, default: pub"},
391 {"cacert", 'c', 1, "CA certificate file"},
392 {"cakey", 'k', 1, "CA private key file"},
393 {"cakeyid", 'x', 1, "keyid on smartcard of CA private key"},
394 {"dn", 'd', 1, "distinguished name to include as subject"},
395 {"san", 'a', 1, "subjectAltName to include in certificate"},
396 {"lifetime", 'l', 1, "days the certificate is valid, default: 1095"},
397 {"serial", 's', 1, "serial number in hex, default: random"},
398 {"ca", 'b', 0, "include CA basicConstraint, default: no"},
399 {"pathlen", 'p', 1, "set path length constraint"},
400 {"flag", 'e', 1, "include extendedKeyUsage flag"},
401 {"crl", 'u', 1, "CRL distribution point URI to include"},
402 {"crlissuer", 'I', 1, "CRL Issuer for CRL at distribution point"},
403 {"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
404 {"digest", 'g', 1, "digest for signature creation, default: sha1"},
405 {"outform", 'f', 1, "encoding of generated cert, default: der"},
406 }
407 });
408 }
409