]>
Commit | Line | Data |
---|---|---|
6be68cc1 MW |
1 | /* |
2 | * Copyright (C) 2009 Martin Willi | |
df6441a1 | 3 | * Copyright (C) 2015-2019 Andreas Steffen |
19ef2aec TB |
4 | * |
5 | * Copyright (C) secunet Security Networks AG | |
6be68cc1 MW |
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> | |
1c4a3459 | 19 | #include <errno.h> |
6be68cc1 MW |
20 | |
21 | #include "pki.h" | |
22 | ||
12642a68 | 23 | #include <collections/linked_list.h> |
6be68cc1 MW |
24 | #include <credentials/certificates/certificate.h> |
25 | #include <credentials/certificates/x509.h> | |
b6c371fb | 26 | #include <selectors/traffic_selector.h> |
6c3ac044 MW |
27 | #include <asn1/asn1.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 | } | |
6be68cc1 | 37 | |
3ffc9d9a MW |
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 | ||
6be68cc1 MW |
48 | /** |
49 | * Create a self signed certificate. | |
50 | */ | |
ae7452e8 | 51 | static int self() |
6be68cc1 | 52 | { |
b5b95c75 | 53 | cred_encoding_type_t form = CERT_ASN1_DER; |
3207193c | 54 | key_type_t type = KEY_ANY; |
2872f778 | 55 | hash_algorithm_t digest = HASH_UNKNOWN; |
d57af8dd | 56 | signature_params_t *scheme = NULL; |
6be68cc1 MW |
57 | certificate_t *cert = NULL; |
58 | private_key_t *private = NULL; | |
59 | public_key_t *public = NULL; | |
efab7313 | 60 | char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL, *keyid = NULL; |
6be68cc1 | 61 | identification_t *id = NULL; |
3ffc9d9a | 62 | linked_list_t *san, *ocsp, *permitted, *excluded, *policies, *mappings; |
b6c371fb | 63 | linked_list_t *addrblocks; |
6a339fff | 64 | int pathlen = X509_NO_CONSTRAINT, inhibit_any = X509_NO_CONSTRAINT; |
f673966b AS |
65 | int inhibit_mapping = X509_NO_CONSTRAINT; |
66 | int require_explicit = X509_NO_CONSTRAINT; | |
6be68cc1 MW |
67 | chunk_t serial = chunk_empty; |
68 | chunk_t encoding = chunk_empty; | |
df6441a1 | 69 | chunk_t critical_extension_oid = chunk_empty; |
aa8732eb MW |
70 | time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60; |
71 | char *datenb = NULL, *datena = NULL, *dateform = NULL; | |
6be68cc1 | 72 | x509_flag_t flags = 0; |
6c3ac044 | 73 | x509_cert_policy_t *policy = NULL; |
b6c371fb | 74 | traffic_selector_t *ts; |
ae7452e8 | 75 | char *arg; |
27a79326 TB |
76 | bool pss = lib->settings->get_bool(lib->settings, "%s.rsa_pss", FALSE, |
77 | lib->ns); | |
6be68cc1 | 78 | |
6be68cc1 | 79 | san = linked_list_create(); |
06a8df11 | 80 | ocsp = linked_list_create(); |
e6fbe593 MW |
81 | permitted = linked_list_create(); |
82 | excluded = linked_list_create(); | |
6c3ac044 | 83 | policies = linked_list_create(); |
3ffc9d9a | 84 | mappings = linked_list_create(); |
b6c371fb | 85 | addrblocks = linked_list_create(); |
6be68cc1 MW |
86 | |
87 | while (TRUE) | |
88 | { | |
ae7452e8 | 89 | switch (command_getopt(&arg)) |
6be68cc1 MW |
90 | { |
91 | case 'h': | |
92 | goto usage; | |
6be68cc1 | 93 | case 't': |
ae7452e8 | 94 | if (streq(arg, "rsa")) |
6be68cc1 MW |
95 | { |
96 | type = KEY_RSA; | |
97 | } | |
ae7452e8 | 98 | else if (streq(arg, "ecdsa")) |
6be68cc1 MW |
99 | { |
100 | type = KEY_ECDSA; | |
101 | } | |
35bc60cc AS |
102 | else if (streq(arg, "ed25519")) |
103 | { | |
104 | type = KEY_ED25519; | |
105 | } | |
878afdf9 TB |
106 | else if (streq(arg, "ed448")) |
107 | { | |
108 | type = KEY_ED448; | |
109 | } | |
05ccde0a TB |
110 | else if (streq(arg, "priv")) |
111 | { | |
112 | type = KEY_ANY; | |
113 | } | |
6be68cc1 MW |
114 | else |
115 | { | |
116 | error = "invalid input type"; | |
117 | goto usage; | |
118 | } | |
119 | continue; | |
120 | case 'g': | |
064fe9c9 | 121 | if (!enum_from_name(hash_algorithm_short_names, arg, &digest)) |
6be68cc1 MW |
122 | { |
123 | error = "invalid --digest type"; | |
124 | goto usage; | |
125 | } | |
126 | continue; | |
d57af8dd | 127 | case 'R': |
e2a26744 | 128 | if (!parse_rsa_padding(arg, &pss)) |
d57af8dd TB |
129 | { |
130 | error = "invalid RSA padding"; | |
131 | goto usage; | |
132 | } | |
133 | continue; | |
6be68cc1 | 134 | case 'i': |
ae7452e8 | 135 | file = arg; |
6be68cc1 | 136 | continue; |
efab7313 MW |
137 | case 'x': |
138 | keyid = arg; | |
139 | continue; | |
6be68cc1 | 140 | case 'd': |
ae7452e8 | 141 | dn = arg; |
6be68cc1 MW |
142 | continue; |
143 | case 'a': | |
ae7452e8 | 144 | san->insert_last(san, identification_create_from_string(arg)); |
6be68cc1 MW |
145 | continue; |
146 | case 'l': | |
aa8732eb | 147 | lifetime = atoi(arg) * 24 * 60 * 60; |
6be68cc1 MW |
148 | if (!lifetime) |
149 | { | |
150 | error = "invalid --lifetime value"; | |
151 | goto usage; | |
152 | } | |
153 | continue; | |
aa8732eb MW |
154 | case 'D': |
155 | dateform = arg; | |
156 | continue; | |
157 | case 'F': | |
158 | datenb = arg; | |
159 | continue; | |
160 | case 'T': | |
161 | datena = arg; | |
162 | continue; | |
6be68cc1 | 163 | case 's': |
ae7452e8 | 164 | hex = arg; |
6be68cc1 | 165 | continue; |
06a8df11 | 166 | case 'b': |
6be68cc1 MW |
167 | flags |= X509_CA; |
168 | continue; | |
3e33ae10 AS |
169 | case 'p': |
170 | pathlen = atoi(arg); | |
171 | continue; | |
b6c371fb MW |
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; | |
e6fbe593 MW |
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; | |
6c3ac044 MW |
189 | case 'P': |
190 | { | |
191 | chunk_t oid; | |
192 | ||
193 | oid = asn1_oid_from_string(arg); | |
194 | if (!oid.len) | |
195 | { | |
3ffc9d9a MW |
196 | error = "--cert-policy OID invalid"; |
197 | goto usage; | |
6c3ac044 MW |
198 | } |
199 | INIT(policy, | |
200 | .oid = oid, | |
201 | ); | |
202 | policies->insert_last(policies, policy); | |
203 | continue; | |
204 | } | |
205 | case 'C': | |
206 | if (!policy) | |
207 | { | |
3ffc9d9a MW |
208 | error = "--cps-uri must follow a --cert-policy"; |
209 | goto usage; | |
6c3ac044 MW |
210 | } |
211 | policy->cps_uri = arg; | |
212 | continue; | |
213 | case 'U': | |
214 | if (!policy) | |
215 | { | |
3ffc9d9a MW |
216 | error = "--user-notice must follow a --cert-policy"; |
217 | goto usage; | |
6c3ac044 MW |
218 | } |
219 | policy->unotice_text = arg; | |
220 | continue; | |
3ffc9d9a MW |
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 | } | |
a864eb37 | 245 | case 'E': |
b088fd4a | 246 | require_explicit = atoi(arg); |
a864eb37 MW |
247 | continue; |
248 | case 'H': | |
b088fd4a | 249 | inhibit_mapping = atoi(arg); |
a864eb37 | 250 | continue; |
6a339fff MW |
251 | case 'A': |
252 | inhibit_any = atoi(arg); | |
253 | continue; | |
b5b95c75 | 254 | case 'e': |
70ac7c43 MW |
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 | } | |
4bc4e8e1 TB |
263 | else if (streq(arg, "ikeIntermediate")) |
264 | { | |
265 | flags |= X509_IKE_INTERMEDIATE; | |
266 | } | |
bb0cda2f MW |
267 | else if (streq(arg, "crlSign")) |
268 | { | |
269 | flags |= X509_CRL_SIGN; | |
270 | } | |
70ac7c43 MW |
271 | else if (streq(arg, "ocspSigning")) |
272 | { | |
273 | flags |= X509_OCSP_SIGNER; | |
274 | } | |
98ae0492 AS |
275 | else if (streq(arg, "msSmartcardLogon")) |
276 | { | |
277 | flags |= X509_MS_SMARTCARD_LOGON; | |
278 | } | |
70ac7c43 | 279 | continue; |
b5b95c75 MW |
280 | case 'f': |
281 | if (!get_form(arg, &form, CRED_CERTIFICATE)) | |
282 | { | |
3ffc9d9a MW |
283 | error = "invalid output format"; |
284 | goto usage; | |
b5b95c75 MW |
285 | } |
286 | continue; | |
06a8df11 | 287 | case 'o': |
ae7452e8 | 288 | ocsp->insert_last(ocsp, arg); |
06a8df11 | 289 | continue; |
df6441a1 AS |
290 | case 'X': |
291 | chunk_free(&critical_extension_oid); | |
292 | critical_extension_oid = asn1_oid_from_string(arg); | |
293 | continue; | |
6be68cc1 MW |
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 | } | |
aa8732eb MW |
308 | if (!calculate_lifetime(dateform, datenb, datena, lifetime, |
309 | ¬_before, ¬_after)) | |
310 | { | |
311 | error = "invalid --not-before/after datetime"; | |
312 | goto usage; | |
313 | } | |
6be68cc1 MW |
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 | } | |
efab7313 MW |
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 | } | |
6be68cc1 MW |
334 | else |
335 | { | |
71c9565a TB |
336 | chunk_t chunk; |
337 | ||
13298719 | 338 | set_file_mode(stdin, CERT_ASN1_DER); |
1c4a3459 MW |
339 | if (!chunk_from_fd(0, &chunk)) |
340 | { | |
341 | fprintf(stderr, "%s: ", strerror(errno)); | |
342 | error = "reading private key failed"; | |
343 | goto end; | |
344 | } | |
6be68cc1 | 345 | private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, type, |
71c9565a TB |
346 | BUILD_BLOB, chunk, BUILD_END); |
347 | free(chunk.ptr); | |
6be68cc1 MW |
348 | } |
349 | if (!private) | |
350 | { | |
efab7313 | 351 | error = "loading private key failed"; |
6be68cc1 MW |
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 | } | |
c6cde0c8 | 364 | else if (!allocate_serial(8, &serial)) |
6be68cc1 | 365 | { |
c6cde0c8 TB |
366 | error = "failed to generate serial number"; |
367 | goto end; | |
6be68cc1 | 368 | } |
d57af8dd | 369 | scheme = get_signature_scheme(private, digest, pss); |
dd4bd21c TB |
370 | if (!scheme) |
371 | { | |
372 | error = "no signature scheme found"; | |
373 | goto end; | |
374 | } | |
d57af8dd | 375 | |
6be68cc1 MW |
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, | |
d57af8dd | 380 | BUILD_SIGNATURE_SCHEME, scheme, BUILD_X509_FLAG, flags, |
3e33ae10 | 381 | BUILD_PATHLEN, pathlen, BUILD_SUBJECT_ALTNAMES, san, |
b6c371fb | 382 | BUILD_ADDRBLOCKS, addrblocks, |
e6fbe593 MW |
383 | BUILD_OCSP_ACCESS_LOCATIONS, ocsp, |
384 | BUILD_PERMITTED_NAME_CONSTRAINTS, permitted, | |
6c3ac044 | 385 | BUILD_EXCLUDED_NAME_CONSTRAINTS, excluded, |
3ffc9d9a | 386 | BUILD_CERTIFICATE_POLICIES, policies, |
a864eb37 | 387 | BUILD_POLICY_MAPPINGS, mappings, |
b088fd4a MW |
388 | BUILD_POLICY_REQUIRE_EXPLICIT, require_explicit, |
389 | BUILD_POLICY_INHIBIT_MAPPING, inhibit_mapping, | |
390 | BUILD_POLICY_INHIBIT_ANY, inhibit_any, | |
df6441a1 | 391 | BUILD_CRITICAL_EXTENSION, critical_extension_oid, |
a864eb37 | 392 | BUILD_END); |
6be68cc1 MW |
393 | if (!cert) |
394 | { | |
395 | error = "generating certificate failed"; | |
396 | goto end; | |
397 | } | |
b5b95c75 | 398 | if (!cert->get_encoding(cert, form, &encoding)) |
6be68cc1 MW |
399 | { |
400 | error = "encoding certificate failed"; | |
401 | goto end; | |
402 | } | |
13298719 | 403 | set_file_mode(stdout, form); |
6be68cc1 MW |
404 | if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1) |
405 | { | |
406 | error = "writing certificate key failed"; | |
407 | goto end; | |
408 | } | |
409 | ||
410 | end: | |
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)); | |
e6fbe593 MW |
416 | permitted->destroy_offset(permitted, offsetof(identification_t, destroy)); |
417 | excluded->destroy_offset(excluded, offsetof(identification_t, destroy)); | |
b6c371fb | 418 | addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy)); |
6c3ac044 | 419 | policies->destroy_function(policies, (void*)destroy_cert_policy); |
3ffc9d9a | 420 | mappings->destroy_function(mappings, (void*)destroy_policy_mapping); |
06a8df11 | 421 | ocsp->destroy(ocsp); |
d57af8dd | 422 | signature_params_destroy(scheme); |
df6441a1 | 423 | free(critical_extension_oid.ptr); |
6be68cc1 MW |
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 | ||
434 | usage: | |
435 | san->destroy_offset(san, offsetof(identification_t, destroy)); | |
e6fbe593 MW |
436 | permitted->destroy_offset(permitted, offsetof(identification_t, destroy)); |
437 | excluded->destroy_offset(excluded, offsetof(identification_t, destroy)); | |
b6c371fb | 438 | addrblocks->destroy_offset(addrblocks, offsetof(traffic_selector_t, destroy)); |
6c3ac044 | 439 | policies->destroy_function(policies, (void*)destroy_cert_policy); |
3ffc9d9a | 440 | mappings->destroy_function(mappings, (void*)destroy_policy_mapping); |
06a8df11 | 441 | ocsp->destroy(ocsp); |
df6441a1 | 442 | free(critical_extension_oid.ptr); |
3ce9438b | 443 | return command_usage(error); |
6be68cc1 MW |
444 | } |
445 | ||
446 | /** | |
447 | * Register the command. | |
448 | */ | |
449 | static void __attribute__ ((constructor))reg() | |
450 | { | |
3ce9438b | 451 | command_register((command_t) { |
6be68cc1 MW |
452 | self, 's', "self", |
453 | "create a self signed certificate", | |
4833f29b | 454 | {"[--in file|--keyid hex] [--type rsa|ecdsa|ed25519|ed448|priv]", |
37ec7707 | 455 | "--dn distinguished-name [--san subjectAltName]+", |
abffb63f | 456 | "[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+", |
98ae0492 | 457 | "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+", |
e6fbe593 | 458 | "[--nc-permitted name] [--nc-excluded name]", |
3ffc9d9a | 459 | "[--policy-map issuer-oid:subject-oid]", |
6a339fff | 460 | "[--policy-explicit len] [--policy-inhibit len] [--policy-any len]", |
3a643b89 | 461 | "[--cert-policy oid [--cps-uri uri] [--user-notice text]]+", |
f6fede93 | 462 | "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]", |
df6441a1 | 463 | "[--rsa-padding pkcs1|pss] [--critical oid]", |
f6fede93 | 464 | "[--outform der|pem]"}, |
6be68cc1 | 465 | { |
a864eb37 MW |
466 | {"help", 'h', 0, "show usage information"}, |
467 | {"in", 'i', 1, "private key input file, default: stdin"}, | |
2d41e1c5 | 468 | {"keyid", 'x', 1, "smartcard or TPM private key object handle"}, |
05ccde0a | 469 | {"type", 't', 1, "type of input key, default: priv"}, |
a864eb37 MW |
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"}, | |
aa8732eb MW |
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"}, | |
a864eb37 MW |
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"}, | |
b6c371fb | 479 | {"addrblock", 'B', 1, "RFC 3779 addrBlock to include"}, |
a864eb37 MW |
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"}, | |
6a339fff | 488 | {"policy-any", 'A', 1, "inhibitAnyPolicy constraint"}, |
a864eb37 MW |
489 | {"flag", 'e', 1, "include extendedKeyUsage flag"}, |
490 | {"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"}, | |
2872f778 | 491 | {"digest", 'g', 1, "digest for signature creation, default: key-specific"}, |
d57af8dd | 492 | {"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"}, |
df6441a1 | 493 | {"critical", 'X', 1, "critical extension OID to include for test purposes"}, |
a864eb37 | 494 | {"outform", 'f', 1, "encoding of generated cert, default: der"}, |
6be68cc1 MW |
495 | } |
496 | }); | |
497 | } |