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