File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / strongswan / src / pki / commands / self.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 09:46:44 2020 UTC (4 years, 2 months ago) by misho
Branches: strongswan, MAIN
CVS tags: v5_9_2p0, v5_8_4p7, HEAD
Strongswan

    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 <collections/linked_list.h>
   23: #include <credentials/certificates/certificate.h>
   24: #include <credentials/certificates/x509.h>
   25: #include <selectors/traffic_selector.h>
   26: #include <asn1/asn1.h>
   27: 
   28: /**
   29:  * Free cert policy with OID
   30:  */
   31: static void destroy_cert_policy(x509_cert_policy_t *policy)
   32: {
   33: 	free(policy->oid.ptr);
   34: 	free(policy);
   35: }
   36: 
   37: /**
   38:  * Free policy mapping
   39:  */
   40: static void destroy_policy_mapping(x509_policy_mapping_t *mapping)
   41: {
   42: 	free(mapping->issuer.ptr);
   43: 	free(mapping->subject.ptr);
   44: 	free(mapping);
   45: }
   46: 
   47: /**
   48:  * Create a self signed certificate.
   49:  */
   50: static int self()
   51: {
   52: 	cred_encoding_type_t form = CERT_ASN1_DER;
   53: 	key_type_t type = KEY_ANY;
   54: 	hash_algorithm_t digest = HASH_UNKNOWN;
   55: 	signature_params_t *scheme = NULL;
   56: 	certificate_t *cert = NULL;
   57: 	private_key_t *private = NULL;
   58: 	public_key_t *public = NULL;
   59: 	char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL, *keyid = NULL;
   60: 	identification_t *id = NULL;
   61: 	linked_list_t *san, *ocsp, *permitted, *excluded, *policies, *mappings;
   62: 	linked_list_t *addrblocks;
   63: 	int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT;
   64: 	int inhibit_mapping = X509_NO_CONSTRAINT;
   65: 	int require_explicit = X509_NO_CONSTRAINT;
   66: 	chunk_t serial = chunk_empty;
   67: 	chunk_t encoding = chunk_empty;
   68: 	chunk_t critical_extension_oid = chunk_empty;
   69: 	time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60;
   70: 	char *datenb = NULL, *datena = NULL, *dateform = NULL;
   71: 	x509_flag_t flags = 0;
   72: 	x509_cert_policy_t *policy = NULL;
   73: 	traffic_selector_t *ts;
   74: 	char *arg;
   75: 	bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE,
   76: 									   lib->ns);
   77: 
   78: 	san = linked_list_create();
   79: 	ocsp = linked_list_create();
   80: 	permitted = linked_list_create();
   81: 	excluded = linked_list_create();
   82: 	policies = linked_list_create();
   83: 	mappings = linked_list_create();
   84: 	addrblocks = linked_list_create();
   85: 
   86: 	while (TRUE)
   87: 	{
   88: 		switch (command_getopt(&arg))
   89: 		{
   90: 			case 'h':
   91: 				goto usage;
   92: 			case 't':
   93: 				if (streq(arg, "rsa"))
   94: 				{
   95: 					type = KEY_RSA;
   96: 				}
   97: 				else if (streq(arg, "ecdsa"))
   98: 				{
   99: 					type = KEY_ECDSA;
  100: 				}
  101: 				else if (streq(arg, "ed25519"))
  102: 				{
  103: 					type = KEY_ED25519;
  104: 				}
  105: 				else if (streq(arg, "ed448"))
  106: 				{
  107: 					type = KEY_ED448;
  108: 				}
  109: 				else if (streq(arg, "bliss"))
  110: 				{
  111: 					type = KEY_BLISS;
  112: 				}
  113: 				else if (streq(arg, "priv"))
  114: 				{
  115: 					type = KEY_ANY;
  116: 				}
  117: 				else
  118: 				{
  119: 					error = "invalid input type";
  120: 					goto usage;
  121: 				}
  122: 				continue;
  123: 			case 'g':
  124: 				if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
  125: 				{
  126: 					error = "invalid --digest type";
  127: 					goto usage;
  128: 				}
  129: 				continue;
  130: 			case 'R':
  131: 				if (streq(arg, "pss"))
  132: 				{
  133: 					pss = TRUE;
  134: 				}
  135: 				else if (!streq(arg, "pkcs1"))
  136: 				{
  137: 					error = "invalid RSA padding";
  138: 					goto usage;
  139: 				}
  140: 				continue;
  141: 			case 'i':
  142: 				file = arg;
  143: 				continue;
  144: 			case 'x':
  145: 				keyid = arg;
  146: 				continue;
  147: 			case 'd':
  148: 				dn = arg;
  149: 				continue;
  150: 			case 'a':
  151: 				san->insert_last(san, identification_create_from_string(arg));
  152: 				continue;
  153: 			case 'l':
  154: 				lifetime = atoi(arg) * 24 * 60 * 60;
  155: 				if (!lifetime)
  156: 				{
  157: 					error = "invalid --lifetime value";
  158: 					goto usage;
  159: 				}
  160: 				continue;
  161: 			case 'D':
  162: 				dateform = arg;
  163: 				continue;
  164: 			case 'F':
  165: 				datenb = arg;
  166: 				continue;
  167: 			case 'T':
  168: 				datena = arg;
  169: 				continue;
  170: 			case 's':
  171: 				hex = arg;
  172: 				continue;
  173: 			case 'b':
  174: 				flags |= X509_CA;
  175: 				continue;
  176: 			case 'p':
  177: 				pathlen = atoi(arg);
  178: 				continue;
  179: 			case 'B':
  180: 				ts = parse_ts(arg);
  181: 				if (!ts)
  182: 				{
  183: 					error = "invalid addressBlock";
  184: 					goto usage;
  185: 				}
  186: 				addrblocks->insert_last(addrblocks, ts);
  187: 				continue;
  188: 			case 'n':
  189: 				permitted->insert_last(permitted,
  190: 									   identification_create_from_string(arg));
  191: 				continue;
  192: 			case 'N':
  193: 				excluded->insert_last(excluded,
  194: 									  identification_create_from_string(arg));
  195: 				continue;
  196: 			case 'P':
  197: 			{
  198: 				chunk_t oid;
  199: 
  200: 				oid = asn1_oid_from_string(arg);
  201: 				if (!oid.len)
  202: 				{
  203: 					error = "--cert-policy OID invalid";
  204: 					goto usage;
  205: 				}
  206: 				INIT(policy,
  207: 					.oid = oid,
  208: 				);
  209: 				policies->insert_last(policies, policy);
  210: 				continue;
  211: 			}
  212: 			case 'C':
  213: 				if (!policy)
  214: 				{
  215: 					error = "--cps-uri must follow a --cert-policy";
  216: 					goto usage;
  217: 				}
  218: 				policy->cps_uri = arg;
  219: 				continue;
  220: 			case 'U':
  221: 				if (!policy)
  222: 				{
  223: 					error = "--user-notice must follow a --cert-policy";
  224: 					goto usage;
  225: 				}
  226: 				policy->unotice_text = arg;
  227: 				continue;
  228: 			case 'M':
  229: 			{
  230: 				char *pos = strchr(arg, ':');
  231: 				x509_policy_mapping_t *mapping;
  232: 				chunk_t subject_oid, issuer_oid;
  233: 
  234: 				if (pos)
  235: 				{
  236: 					*pos++ = '\0';
  237: 					issuer_oid = asn1_oid_from_string(arg);
  238: 					subject_oid = asn1_oid_from_string(pos);
  239: 				}
  240: 				if (!pos || !issuer_oid.len || !subject_oid.len)
  241: 				{
  242: 					error = "--policy-map OIDs invalid";
  243: 					goto usage;
  244: 				}
  245: 				INIT(mapping,
  246: 					.issuer = issuer_oid,
  247: 					.subject = subject_oid,
  248: 				);
  249: 				mappings->insert_last(mappings, mapping);
  250: 				continue;
  251: 			}
  252: 			case 'E':
  253: 				require_explicit = atoi(arg);
  254: 				continue;
  255: 			case 'H':
  256: 				inhibit_mapping = atoi(arg);
  257: 				continue;
  258: 			case 'A':
  259: 				inhibit_any = atoi(arg);
  260: 				continue;
  261: 			case 'e':
  262: 				if (streq(arg, "serverAuth"))
  263: 				{
  264: 					flags |= X509_SERVER_AUTH;
  265: 				}
  266: 				else if (streq(arg, "clientAuth"))
  267: 				{
  268: 					flags |= X509_CLIENT_AUTH;
  269: 				}
  270: 				else if (streq(arg, "ikeIntermediate"))
  271: 				{
  272: 					flags |= X509_IKE_INTERMEDIATE;
  273: 				}
  274: 				else if (streq(arg, "crlSign"))
  275: 				{
  276: 					flags |= X509_CRL_SIGN;
  277: 				}
  278: 				else if (streq(arg, "ocspSigning"))
  279: 				{
  280: 					flags |= X509_OCSP_SIGNER;
  281: 				}
  282: 				else if (streq(arg, "msSmartcardLogon"))
  283: 				{
  284: 					flags |= X509_MS_SMARTCARD_LOGON;
  285: 				}
  286: 				continue;
  287: 			case 'f':
  288: 				if (!get_form(arg, &form, CRED_CERTIFICATE))
  289: 				{
  290: 					error = "invalid output format";
  291: 					goto usage;
  292: 				}
  293: 				continue;
  294: 			case 'o':
  295: 				ocsp->insert_last(ocsp, arg);
  296: 				continue;
  297: 			case 'X':
  298: 				chunk_free(&critical_extension_oid);
  299: 				critical_extension_oid = asn1_oid_from_string(arg);
  300: 				continue;
  301: 			case EOF:
  302: 				break;
  303: 			default:
  304: 				error = "invalid --self option";
  305: 				goto usage;
  306: 		}
  307: 		break;
  308: 	}
  309: 
  310: 	if (!dn)
  311: 	{
  312: 		error = "--dn is required";
  313: 		goto usage;
  314: 	}
  315: 	if (!calculate_lifetime(dateform, datenb, datena, lifetime,
  316: 							&not_before, &not_after))
  317: 	{
  318: 		error = "invalid --not-before/after datetime";
  319: 		goto usage;
  320: 	}
  321: 	id = identification_create_from_string(dn);
  322: 	if (id->get_type(id) != ID_DER_ASN1_DN)
  323: 	{
  324: 		error = "supplied --dn is not a distinguished name";
  325: 		goto end;
  326: 	}
  327: 	if (file)
  328: 	{
  329: 		private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
  330: 									 BUILD_FROM_FILE, file, BUILD_END);
  331: 	}
  332: 	else if (keyid)
  333: 	{
  334: 		chunk_t chunk;
  335: 
  336: 		chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
  337: 		private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
  338: 									 BUILD_PKCS11_KEYID, chunk, BUILD_END);
  339: 		free(chunk.ptr);
  340: 	}
  341: 	else
  342: 	{
  343: 		chunk_t chunk;
  344: 
  345: 		set_file_mode(stdin, CERT_ASN1_DER);
  346: 		if (!chunk_from_fd(0, &chunk))
  347: 		{
  348: 			fprintf(stderr, "%s: ", strerror(errno));
  349: 			error = "reading private key failed";
  350: 			goto end;
  351: 		}
  352: 		private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type,
  353: 									 BUILD_BLOB, chunk, BUILD_END);
  354: 		free(chunk.ptr);
  355: 	}
  356: 	if (!private)
  357: 	{
  358: 		error = "loading private key failed";
  359: 		goto end;
  360: 	}
  361: 	public = private->get_public_key(private);
  362: 	if (!public)
  363: 	{
  364: 		error = "extracting public key failed";
  365: 		goto end;
  366: 	}
  367: 	if (hex)
  368: 	{
  369: 		serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
  370: 	}
  371: 	else
  372: 	{
  373: 		rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
  374: 
  375: 		if (!rng)
  376: 		{
  377: 			error = "no random number generator found";
  378: 			goto end;
  379: 		}
  380: 		if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
  381: 		{
  382: 			error = "failed to generate serial number";
  383: 			rng->destroy(rng);
  384: 			goto end;
  385: 		}
  386: 		serial.ptr[0] &= 0x7F;
  387: 		rng->destroy(rng);
  388: 	}
  389: 	scheme = get_signature_scheme(private, digest, pss);
  390: 	if (!scheme)
  391: 	{
  392: 		error = "no signature scheme found";
  393: 		goto end;
  394: 	}
  395: 
  396: 	cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
  397: 						BUILD_SIGNING_KEY, private, BUILD_PUBLIC_KEY, public,
  398: 						BUILD_SUBJECT, id, BUILD_NOT_BEFORE_TIME, not_before,
  399: 						BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
  400: 						BUILD_SIGNATURE_SCHEME, scheme, BUILD_X509_FLAG, flags,
  401: 						BUILD_PATHLEN, pathlen, BUILD_SUBJECT_ALTNAMES, san,
  402: 						BUILD_ADDRBLOCKS, addrblocks,
  403: 						BUILD_OCSP_ACCESS_LOCATIONS, ocsp,
  404: 						BUILD_PERMITTED_NAME_CONSTRAINTS, permitted,
  405: 						BUILD_EXCLUDED_NAME_CONSTRAINTS, excluded,
  406: 						BUILD_CERTIFICATE_POLICIES, policies,
  407: 						BUILD_POLICY_MAPPINGS, mappings,
  408: 						BUILD_POLICY_REQUIRE_EXPLICIT, require_explicit,
  409: 						BUILD_POLICY_INHIBIT_MAPPING, inhibit_mapping,
  410: 						BUILD_POLICY_INHIBIT_ANY, inhibit_any,
  411: 						BUILD_CRITICAL_EXTENSION, critical_extension_oid,
  412: 						BUILD_END);
  413: 	if (!cert)
  414: 	{
  415: 		error = "generating certificate failed";
  416: 		goto end;
  417: 	}
  418: 	if (!cert->get_encoding(cert, form, &encoding))
  419: 	{
  420: 		error = "encoding certificate failed";
  421: 		goto end;
  422: 	}
  423: 	set_file_mode(stdout, form);
  424: 	if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
  425: 	{
  426: 		error = "writing certificate key failed";
  427: 		goto end;
  428: 	}
  429: 
  430: end:
  431: 	DESTROY_IF(id);
  432: 	DESTROY_IF(cert);
  433: 	DESTROY_IF(public);
  434: 	DESTROY_IF(private);
  435: 	san->destroy_offset(san, offsetof(identification_t, destroy));
  436: 	permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
  437: 	excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
  438: 	addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
  439: 	policies->destroy_function(policies, (void*)destroy_cert_policy);
  440: 	mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
  441: 	ocsp->destroy(ocsp);
  442: 	signature_params_destroy(scheme);
  443: 	free(critical_extension_oid.ptr);
  444: 	free(encoding.ptr);
  445: 	free(serial.ptr);
  446: 
  447: 	if (error)
  448: 	{
  449: 		fprintf(stderr, "%s\n", error);
  450: 		return 1;
  451: 	}
  452: 	return 0;
  453: 
  454: usage:
  455: 	san->destroy_offset(san, offsetof(identification_t, destroy));
  456: 	permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
  457: 	excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
  458: 	addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy));
  459: 	policies->destroy_function(policies, (void*)destroy_cert_policy);
  460: 	mappings->destroy_function(mappings, (void*)destroy_policy_mapping);
  461: 	ocsp->destroy(ocsp);
  462: 	free(critical_extension_oid.ptr);
  463: 	return command_usage(error);
  464: }
  465: 
  466: /**
  467:  * Register the command.
  468:  */
  469: static void __attribute__ ((constructor))reg()
  470: {
  471: 	command_register((command_t) {
  472: 		self, 's', "self",
  473: 		"create a self signed certificate",
  474: 		{"[--in file|--keyid hex] [--type rsa|ecdsa|ed25519|ed448|bliss|priv]",
  475: 		 " --dn distinguished-name [--san subjectAltName]+",
  476: 		 "[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+",
  477: 		 "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+",
  478: 		 "[--nc-permitted name] [--nc-excluded name]",
  479: 		 "[--policy-map issuer-oid:subject-oid]",
  480: 		 "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]",
  481: 		 "[--cert-policy oid [--cps-uri uri] [--user-notice text]]+",
  482: 		 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
  483: 		 "[--rsa-padding pkcs1|pss] [--critical oid]",
  484: 		 "[--outform der|pem]"},
  485: 		{
  486: 			{"help",			'h', 0, "show usage information"},
  487: 			{"in",				'i', 1, "private key input file, default: stdin"},
  488: 			{"keyid",			'x', 1, "smartcard or TPM private key object handle"},
  489: 			{"type",			't', 1, "type of input key, default: priv"},
  490: 			{"dn",				'd', 1, "subject and issuer distinguished name"},
  491: 			{"san",				'a', 1, "subjectAltName to include in certificate"},
  492: 			{"lifetime",		'l', 1, "days the certificate is valid, default: 1095"},
  493: 			{"not-before",		'F', 1, "date/time the validity of the cert starts"},
  494: 			{"not-after",		'T', 1, "date/time the validity of the cert ends"},
  495: 			{"dateform",		'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
  496: 			{"serial",			's', 1, "serial number in hex, default: random"},
  497: 			{"ca",				'b', 0, "include CA basicConstraint, default: no"},
  498: 			{"pathlen",			'p', 1, "set path length constraint"},
  499: 			{"addrblock",		'B', 1, "RFC 3779 addrBlock to include"},
  500: 			{"nc-permitted",	'n', 1, "add permitted NameConstraint"},
  501: 			{"nc-excluded",		'N', 1, "add excluded NameConstraint"},
  502: 			{"cert-policy",		'P', 1, "certificatePolicy OID to include"},
  503: 			{"cps-uri",			'C', 1, "Certification Practice statement URI for certificatePolicy"},
  504: 			{"user-notice",		'U', 1, "user notice for certificatePolicy"},
  505: 			{"policy-mapping",	'M', 1, "policyMapping from issuer to subject OID"},
  506: 			{"policy-explicit",	'E', 1, "requireExplicitPolicy constraint"},
  507: 			{"policy-inhibit",	'H', 1, "inhibitPolicyMapping constraint"},
  508: 			{"policy-any",		'A', 1, "inhibitAnyPolicy constraint"},
  509: 			{"flag",			'e', 1, "include extendedKeyUsage flag"},
  510: 			{"ocsp",			'o', 1, "OCSP AuthorityInfoAccess URI to include"},
  511: 			{"digest",			'g', 1, "digest for signature creation, default: key-specific"},
  512: 			{"rsa-padding",		'R', 1, "padding for RSA signatures, default: pkcs1"},
  513: 			{"critical",		'X', 1, "critical extension OID to include for test purposes"},
  514: 			{"outform",			'f', 1, "encoding of generated cert, default: der"},
  515: 		}
  516: 	});
  517: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>