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