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