pki: Edited keyid parameter use in various pki man pages and usage outputs
[strongswan.git] / src / pki / commands / issue.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Copyright (C) 2015-2017 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <time.h>
18 #include <errno.h>
19
20 #include "pki.h"
21
22 #include <utils/debug.h>
23 #include <asn1/asn1.h>
24 #include <collections/linked_list.h>
25 #include <credentials/certificates/certificate.h>
26 #include <credentials/certificates/x509.h>
27 #include <credentials/certificates/pkcs10.h>
28
29 /**
30 * Free cert policy with OID
31 */
32 static void destroy_cert_policy(x509_cert_policy_t *policy)
33 {
34 free(policy->oid.ptr);
35 free(policy);
36 }
37
38 /**
39 * Free policy mapping
40 */
41 static void destroy_policy_mapping(x509_policy_mapping_t *mapping)
42 {
43 free(mapping->issuer.ptr);
44 free(mapping->subject.ptr);
45 free(mapping);
46 }
47
48 /**
49 * Free a CRL DistributionPoint
50 */
51 static void destroy_cdp(x509_cdp_t *this)
52 {
53 DESTROY_IF(this->issuer);
54 free(this);
55 }
56
57 /**
58 * Issue a certificate using a CA certificate and key
59 */
60 static int issue()
61 {
62 cred_encoding_type_t form = CERT_ASN1_DER;
63 hash_algorithm_t digest = HASH_UNKNOWN;
64 certificate_t *cert_req = NULL, *cert = NULL, *ca =NULL;
65 private_key_t *private = NULL;
66 public_key_t *public = NULL;
67 credential_type_t type = CRED_PUBLIC_KEY;
68 key_type_t subtype = KEY_ANY;
69 bool pkcs10 = FALSE;
70 char *file = NULL, *dn = NULL, *hex = NULL, *cacert = NULL, *cakey = NULL;
71 char *error = NULL, *keyid = NULL;
72 identification_t *id = NULL;
73 linked_list_t *san, *cdps, *ocsp, *permitted, *excluded, *policies, *mappings;
74 linked_list_t *addrblocks;
75 int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT;
76 int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT;
77 chunk_t serial = chunk_empty;
78 chunk_t encoding = chunk_empty;
79 time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60;
80 char *datenb = NULL, *datena = NULL, *dateform = NULL;
81 x509_flag_t flags = 0;
82 x509_t *x509;
83 x509_cdp_t *cdp = NULL;
84 x509_cert_policy_t *policy = NULL;
85 traffic_selector_t *ts;
86 char *arg;
87
88 san = linked_list_create();
89 cdps = linked_list_create();
90 ocsp = linked_list_create();
91 permitted = linked_list_create();
92 excluded = linked_list_create();
93 policies = linked_list_create();
94 mappings = linked_list_create();
95 addrblocks = linked_list_create();
96
97 while (TRUE)
98 {
99 switch (command_getopt(&arg))
100 {
101 case 'h':
102 goto usage;
103 case 't':
104 if (streq(arg, "pkcs10"))
105 {
106 pkcs10 = TRUE;
107 }
108 else if (streq(arg, "rsa"))
109 {
110 type = CRED_PRIVATE_KEY;
111 subtype = KEY_RSA;
112 }
113 else if (streq(arg, "ecdsa"))
114 {
115 type = CRED_PRIVATE_KEY;
116 subtype = KEY_ECDSA;
117 }
118 else if (streq(arg, "ed25519"))
119 {
120 type = CRED_PRIVATE_KEY;
121 subtype = KEY_ED25519;
122 }
123 else if (streq(arg, "bliss"))
124 {
125 type = CRED_PRIVATE_KEY;
126 subtype = KEY_BLISS;
127 }
128 else if (streq(arg, "priv"))
129 {
130 type = CRED_PRIVATE_KEY;
131 subtype = KEY_ANY;
132 }
133 else if (!streq(arg, "pub"))
134 {
135 error = "invalid input type";
136 goto usage;
137 }
138 continue;
139 case 'g':
140 if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
141 {
142 error = "invalid --digest type";
143 goto usage;
144 }
145 continue;
146 case 'i':
147 file = arg;
148 continue;
149 case 'c':
150 cacert = arg;
151 continue;
152 case 'k':
153 cakey = arg;
154 continue;
155 case 'x':
156 keyid = arg;
157 continue;
158 case 'd':
159 dn = arg;
160 continue;
161 case 'a':
162 san->insert_last(san, identification_create_from_string(arg));
163 continue;
164 case 'l':
165 lifetime = atoi(arg) * 24 * 60 * 60;
166 if (!lifetime)
167 {
168 error = "invalid --lifetime value";
169 goto usage;
170 }
171 continue;
172 case 'D':
173 dateform = arg;
174 continue;
175 case 'F':
176 datenb = arg;
177 continue;
178 case 'T':
179 datena = arg;
180 continue;
181 case 's':
182 hex = arg;
183 continue;
184 case 'b':
185 flags |= X509_CA;
186 continue;
187 case 'p':
188 pathlen = atoi(arg);
189 continue;
190 case 'B':
191 ts = parse_ts(arg);
192 if (!ts)
193 {
194 error = "invalid addressBlock";
195 goto usage;
196 }
197 addrblocks->insert_last(addrblocks, ts);
198 continue;
199 case 'n':
200 permitted->insert_last(permitted,
201 identification_create_from_string(arg));
202 continue;
203 case 'N':
204 excluded->insert_last(excluded,
205 identification_create_from_string(arg));
206 continue;
207 case 'P':
208 {
209 chunk_t oid;
210
211 oid = asn1_oid_from_string(arg);
212 if (!oid.len)
213 {
214 error = "--cert-policy OID invalid";
215 goto usage;
216 }
217 INIT(policy,
218 .oid = oid,
219 );
220 policies->insert_last(policies, policy);
221 continue;
222 }
223 case 'C':
224 if (!policy)
225 {
226 error = "--cps-uri must follow a --cert-policy";
227 goto usage;
228 }
229 policy->cps_uri = arg;
230 continue;
231 case 'U':
232 if (!policy)
233 {
234 error = "--user-notice must follow a --cert-policy";
235 goto usage;
236 }
237 policy->unotice_text = arg;
238 continue;
239 case 'M':
240 {
241 char *pos = strchr(arg, ':');
242 x509_policy_mapping_t *mapping;
243 chunk_t subject_oid, issuer_oid;
244
245 if (pos)
246 {
247 *pos++ = '\0';
248 issuer_oid = asn1_oid_from_string(arg);
249 subject_oid = asn1_oid_from_string(pos);
250 }
251 if (!pos || !issuer_oid.len || !subject_oid.len)
252 {
253 error = "--policy-map OIDs invalid";
254 goto usage;
255 }
256 INIT(mapping,
257 .issuer = issuer_oid,
258 .subject = subject_oid,
259 );
260 mappings->insert_last(mappings, mapping);
261 continue;
262 }
263 case 'E':
264 require_explicit = atoi(arg);
265 continue;
266 case 'H':
267 inhibit_mapping = atoi(arg);
268 continue;
269 case 'A':
270 inhibit_any = atoi(arg);
271 continue;
272 case 'e':
273 if (streq(arg, "serverAuth"))
274 {
275 flags |= X509_SERVER_AUTH;
276 }
277 else if (streq(arg, "clientAuth"))
278 {
279 flags |= X509_CLIENT_AUTH;
280 }
281 else if (streq(arg, "ikeIntermediate"))
282 {
283 flags |= X509_IKE_INTERMEDIATE;
284 }
285 else if (streq(arg, "crlSign"))
286 {
287 flags |= X509_CRL_SIGN;
288 }
289 else if (streq(arg, "ocspSigning"))
290 {
291 flags |= X509_OCSP_SIGNER;
292 }
293 else if (streq(arg, "msSmartcardLogon"))
294 {
295 flags |= X509_MS_SMARTCARD_LOGON;
296 }
297 continue;
298 case 'f':
299 if (!get_form(arg, &form, CRED_CERTIFICATE))
300 {
301 error = "invalid output format";
302 goto usage;
303 }
304 continue;
305 case 'u':
306 INIT(cdp,
307 .uri = arg,
308 );
309 cdps->insert_last(cdps, cdp);
310 continue;
311 case 'I':
312 if (!cdp || cdp->issuer)
313 {
314 error = "--crlissuer must follow a --crl";
315 goto usage;
316 }
317 cdp->issuer = identification_create_from_string(arg);
318 continue;
319 case 'o':
320 ocsp->insert_last(ocsp, arg);
321 continue;
322 case EOF:
323 break;
324 default:
325 error = "invalid --issue option";
326 goto usage;
327 }
328 break;
329 }
330
331 if (!cacert)
332 {
333 error = "--cacert is required";
334 goto usage;
335 }
336 if (!cakey && !keyid)
337 {
338 error = "--cakey or --keyid is required";
339 goto usage;
340 }
341 if (!calculate_lifetime(dateform, datenb, datena, lifetime,
342 &not_before, &not_after))
343 {
344 error = "invalid --not-before/after datetime";
345 goto usage;
346 }
347 if (dn && *dn)
348 {
349 id = identification_create_from_string(dn);
350 if (id->get_type(id) != ID_DER_ASN1_DN)
351 {
352 error = "supplied --dn is not a distinguished name";
353 goto end;
354 }
355 }
356
357 DBG2(DBG_LIB, "Reading ca certificate:");
358 ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
359 BUILD_FROM_FILE, cacert, BUILD_END);
360 if (!ca)
361 {
362 error = "parsing CA certificate failed";
363 goto end;
364 }
365 x509 = (x509_t*)ca;
366 if (!(x509->get_flags(x509) & X509_CA))
367 {
368 error = "CA certificate misses CA basicConstraint";
369 goto end;
370 }
371 public = ca->get_public_key(ca);
372 if (!public)
373 {
374 error = "extracting CA certificate public key failed";
375 goto end;
376 }
377
378 DBG2(DBG_LIB, "Reading ca private key:");
379 if (cakey)
380 {
381 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
382 public->get_type(public),
383 BUILD_FROM_FILE, cakey, BUILD_END);
384 }
385 else
386 {
387 chunk_t chunk;
388
389 chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
390 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
391 BUILD_PKCS11_KEYID, chunk, BUILD_END);
392 free(chunk.ptr);
393 }
394 if (!private)
395 {
396 error = "loading CA private key failed";
397 goto end;
398 }
399 if (digest == HASH_UNKNOWN)
400 {
401 digest = get_default_digest(private);
402 }
403 if (!private->belongs_to(private, public))
404 {
405 error = "CA private key does not match CA certificate";
406 goto end;
407 }
408 public->destroy(public);
409
410 if (hex)
411 {
412 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
413 }
414 else
415 {
416 rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
417
418 if (!rng)
419 {
420 error = "no random number generator found";
421 goto end;
422 }
423 if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
424 {
425 error = "failed to generate serial number";
426 rng->destroy(rng);
427 goto end;
428 }
429 serial.ptr[0] &= 0x7F;
430 rng->destroy(rng);
431 }
432
433 if (pkcs10)
434 {
435 enumerator_t *enumerator;
436 identification_t *subjectAltName;
437 pkcs10_t *req;
438
439 DBG2(DBG_LIB, "Reading certificate request");
440 if (file)
441 {
442 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
443 CERT_PKCS10_REQUEST,
444 BUILD_FROM_FILE, file, BUILD_END);
445 }
446 else
447 {
448 chunk_t chunk;
449
450 set_file_mode(stdin, CERT_ASN1_DER);
451 if (!chunk_from_fd(0, &chunk))
452 {
453 fprintf(stderr, "%s: ", strerror(errno));
454 error = "reading certificate request failed";
455 goto end;
456 }
457 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
458 CERT_PKCS10_REQUEST,
459 BUILD_BLOB, chunk, BUILD_END);
460 free(chunk.ptr);
461 }
462 if (!cert_req)
463 {
464 error = "parsing certificate request failed";
465 goto end;
466 }
467
468 /* If not set yet use subject from PKCS#10 certificate request as DN */
469 if (!id)
470 {
471 id = cert_req->get_subject(cert_req);
472 id = id->clone(id);
473 }
474
475 /* Add subjectAltNames from PKCS#10 certificate request */
476 req = (pkcs10_t*)cert_req;
477 enumerator = req->create_subjectAltName_enumerator(req);
478 while (enumerator->enumerate(enumerator, &subjectAltName))
479 {
480 san->insert_last(san, subjectAltName->clone(subjectAltName));
481 }
482 enumerator->destroy(enumerator);
483
484 /* Use public key from PKCS#10 certificate request */
485 public = cert_req->get_public_key(cert_req);
486 }
487 else
488 {
489 DBG2(DBG_LIB, "Reading key:");
490 if (file)
491 {
492 public = lib->creds->create(lib->creds, type, subtype,
493 BUILD_FROM_FILE, file, BUILD_END);
494 }
495 else
496 {
497 chunk_t chunk;
498
499 if (!chunk_from_fd(0, &chunk))
500 {
501 fprintf(stderr, "%s: ", strerror(errno));
502 error = "reading key failed";
503 goto end;
504 }
505 public = lib->creds->create(lib->creds, type, subtype,
506 BUILD_BLOB, chunk, BUILD_END);
507 free(chunk.ptr);
508 }
509 if (public && type == CRED_PRIVATE_KEY)
510 {
511 private_key_t *priv = (private_key_t*)public;
512 public = priv->get_public_key(priv);
513 priv->destroy(priv);
514 }
515 }
516 if (!public)
517 {
518 error = "parsing public key failed";
519 goto end;
520 }
521
522 if (!id)
523 {
524 id = identification_create_from_encoding(ID_DER_ASN1_DN,
525 chunk_from_chars(ASN1_SEQUENCE, 0));
526 }
527
528 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
529 BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
530 BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
531 BUILD_NOT_BEFORE_TIME, not_before, BUILD_DIGEST_ALG, digest,
532 BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
533 BUILD_SUBJECT_ALTNAMES, san, BUILD_X509_FLAG, flags,
534 BUILD_PATHLEN, pathlen, BUILD_ADDRBLOCKS, addrblocks,
535 BUILD_CRL_DISTRIBUTION_POINTS, cdps,
536 BUILD_OCSP_ACCESS_LOCATIONS, ocsp,
537 BUILD_PERMITTED_NAME_CONSTRAINTS, permitted,
538 BUILD_EXCLUDED_NAME_CONSTRAINTS, excluded,
539 BUILD_CERTIFICATE_POLICIES, policies,
540 BUILD_POLICY_MAPPINGS, mappings,
541 BUILD_POLICY_REQUIRE_EXPLICIT, require_explicit,
542 BUILD_POLICY_INHIBIT_MAPPING, inhibit_mapping,
543 BUILD_POLICY_INHIBIT_ANY, inhibit_any,
544 BUILD_END);
545 if (!cert)
546 {
547 error = "generating certificate failed";
548 goto end;
549 }
550 if (!cert->get_encoding(cert, form, &encoding))
551 {
552 error = "encoding certificate failed";
553 goto end;
554 }
555 set_file_mode(stdout, form);
556 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
557 {
558 error = "writing certificate key failed";
559 goto end;
560 }
561
562 end:
563 DESTROY_IF(id);
564 DESTROY_IF(cert_req);
565 DESTROY_IF(cert);
566 DESTROY_IF(ca);
567 DESTROY_IF(public);
568 DESTROY_IF(private);
569 san->destroy_offset(san, offsetof(identification_t, destroy));
570 permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
571 excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
572 addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
573 policies->destroy_function(policies, (void*)destroy_cert_policy);
574 mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
575 cdps->destroy_function(cdps, (void*)destroy_cdp);
576 ocsp->destroy(ocsp);
577 free(encoding.ptr);
578 free(serial.ptr);
579
580 if (error)
581 {
582 fprintf(stderr, "%s\n", error);
583 return 1;
584 }
585 return 0;
586
587 usage:
588 san->destroy_offset(san, offsetof(identification_t, destroy));
589 permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
590 excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
591 addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
592 policies->destroy_function(policies, (void*)destroy_cert_policy);
593 mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
594 cdps->destroy_function(cdps, (void*)destroy_cdp);
595 ocsp->destroy(ocsp);
596 return command_usage(error);
597 }
598
599 /**
600 * Register the command.
601 */
602 static void __attribute__ ((constructor))reg()
603 {
604 command_register((command_t) {
605 issue, 'i', "issue",
606 "issue a certificate using a CA certificate and key",
607 {"[--in file] [--type pub|pkcs10|priv|rsa|ecdsa|ed25519|bliss] --cakey file|--cakeyid hex",
608 " --cacert file [--dn subject-dn] [--san subjectAltName]+",
609 "[--lifetime days] [--serial hex] [--ca] [--pathlen len]",
610 "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+",
611 "[--crl uri [--crlissuer i]]+ [--ocsp uri]+ [--nc-permitted name]",
612 "[--nc-excluded name] [--policy-mapping issuer-oid:subject-oid]",
613 "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]",
614 "[--cert-policy oid [--cps-uri uri] [--user-notice text]]+",
615 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
616 "[--outform der|pem]"},
617 {
618 {"help", 'h', 0, "show usage information"},
619 {"in", 'i', 1, "key/request file to issue, default: stdin"},
620 {"type", 't', 1, "type of input, default: pub"},
621 {"cacert", 'c', 1, "CA certificate file"},
622 {"cakey", 'k', 1, "CA private key file"},
623 {"cakeyid", 'x', 1, "smartcard or TPM CA private key object handle"},
624 {"dn", 'd', 1, "distinguished name to include as subject"},
625 {"san", 'a', 1, "subjectAltName to include in certificate"},
626 {"lifetime", 'l', 1, "days the certificate is valid, default: 1095"},
627 {"not-before", 'F', 1, "date/time the validity of the cert starts"},
628 {"not-after", 'T', 1, "date/time the validity of the cert ends"},
629 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
630 {"serial", 's', 1, "serial number in hex, default: random"},
631 {"ca", 'b', 0, "include CA basicConstraint, default: no"},
632 {"pathlen", 'p', 1, "set path length constraint"},
633 {"addrblock", 'B', 1, "RFC 3779 addrBlock to include"},
634 {"nc-permitted", 'n', 1, "add permitted NameConstraint"},
635 {"nc-excluded", 'N', 1, "add excluded NameConstraint"},
636 {"cert-policy", 'P', 1, "certificatePolicy OID to include"},
637 {"cps-uri", 'C', 1, "Certification Practice statement URI for certificatePolicy"},
638 {"user-notice", 'U', 1, "user notice for certificatePolicy"},
639 {"policy-mapping", 'M', 1, "policyMapping from issuer to subject OID"},
640 {"policy-explicit", 'E', 1, "requireExplicitPolicy constraint"},
641 {"policy-inhibit", 'H', 1, "inhibitPolicyMapping constraint"},
642 {"policy-any", 'A', 1, "inhibitAnyPolicy constraint"},
643 {"flag", 'e', 1, "include extendedKeyUsage flag"},
644 {"crl", 'u', 1, "CRL distribution point URI to include"},
645 {"crlissuer", 'I', 1, "CRL Issuer for CRL at distribution point"},
646 {"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
647 {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
648 {"outform", 'f', 1, "encoding of generated cert, default: der"},
649 }
650 });
651 }