]> git.ipfire.org Git - thirdparty/strongswan.git/blame - src/pki/commands/issue.c
Support different encoding types in certificate.get_encoding()
[thirdparty/strongswan.git] / src / pki / commands / issue.c
CommitLineData
6be68cc1
MW
1/*
2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * for more details.
14 */
15
16#include <time.h>
17
18#include "pki.h"
19
5a4dee6d 20#include <debug.h>
6be68cc1 21#include <utils/linked_list.h>
6be68cc1
MW
22#include <credentials/certificates/certificate.h>
23#include <credentials/certificates/x509.h>
f03e0e91 24#include <credentials/certificates/pkcs10.h>
6be68cc1
MW
25
26/**
27 * Issue a certificate using a CA certificate and key
28 */
ae7452e8 29static int issue()
6be68cc1
MW
30{
31 hash_algorithm_t digest = HASH_SHA1;
f03e0e91 32 certificate_t *cert_req = NULL, *cert = NULL, *ca =NULL;
6be68cc1
MW
33 private_key_t *private = NULL;
34 public_key_t *public = NULL;
f03e0e91 35 bool pkcs10 = FALSE;
6be68cc1
MW
36 char *file = NULL, *dn = NULL, *hex = NULL, *cacert = NULL, *cakey = NULL;
37 char *error = NULL;
38 identification_t *id = NULL;
06a8df11 39 linked_list_t *san, *cdps, *ocsp;
a2cf26f1 40 int lifetime = 1095;
3e33ae10 41 int pathlen = X509_NO_PATH_LEN_CONSTRAINT;
6be68cc1
MW
42 chunk_t serial = chunk_empty;
43 chunk_t encoding = chunk_empty;
44 time_t not_before, not_after;
45 x509_flag_t flags = 0;
46 x509_t *x509;
ae7452e8 47 char *arg;
6be68cc1 48
6be68cc1 49 san = linked_list_create();
3a7bd9bd 50 cdps = linked_list_create();
06a8df11 51 ocsp = linked_list_create();
6be68cc1
MW
52
53 while (TRUE)
54 {
ae7452e8 55 switch (command_getopt(&arg))
6be68cc1
MW
56 {
57 case 'h':
58 goto usage;
6be68cc1 59 case 't':
ae7452e8 60 if (streq(arg, "pkcs10"))
f03e0e91
AS
61 {
62 pkcs10 = TRUE;
63 }
ae7452e8 64 else if (!streq(arg, "pub"))
6be68cc1
MW
65 {
66 error = "invalid input type";
67 goto usage;
68 }
69 continue;
70 case 'g':
ae7452e8 71 digest = get_digest(arg);
6be68cc1
MW
72 if (digest == HASH_UNKNOWN)
73 {
74 error = "invalid --digest type";
75 goto usage;
76 }
77 continue;
78 case 'i':
ae7452e8 79 file = arg;
6be68cc1
MW
80 continue;
81 case 'c':
ae7452e8 82 cacert = arg;
6be68cc1
MW
83 continue;
84 case 'k':
ae7452e8 85 cakey = arg;
6be68cc1
MW
86 continue;
87 case 'd':
ae7452e8 88 dn = arg;
6be68cc1
MW
89 continue;
90 case 'a':
ae7452e8 91 san->insert_last(san, identification_create_from_string(arg));
6be68cc1
MW
92 continue;
93 case 'l':
ae7452e8 94 lifetime = atoi(arg);
6be68cc1
MW
95 if (!lifetime)
96 {
97 error = "invalid --lifetime value";
98 goto usage;
99 }
100 continue;
101 case 's':
ae7452e8 102 hex = arg;
6be68cc1
MW
103 continue;
104 case 'b':
105 flags |= X509_CA;
106 continue;
3e33ae10
AS
107 case 'p':
108 pathlen = atoi(arg);
109 continue;
ce40bf5d 110 case 'f':
408e46a3
AS
111 if (streq(arg, "serverAuth"))
112 {
113 flags |= X509_SERVER_AUTH;
114 }
7eab4a1b
MW
115 else if (streq(arg, "clientAuth"))
116 {
117 flags |= X509_CLIENT_AUTH;
118 }
408e46a3 119 else if (streq(arg, "ocspSigning"))
ce40bf5d
AS
120 {
121 flags |= X509_OCSP_SIGNER;
122 }
123 continue;
06a8df11 124 case 'u':
ae7452e8 125 cdps->insert_last(cdps, arg);
06a8df11
MW
126 continue;
127 case 'o':
ae7452e8 128 ocsp->insert_last(ocsp, arg);
06a8df11 129 continue;
6be68cc1
MW
130 case EOF:
131 break;
132 default:
133 error = "invalid --issue option";
134 goto usage;
135 }
136 break;
137 }
138
f03e0e91 139 if (!pkcs10 && !dn)
6be68cc1
MW
140 {
141 error = "--dn is required";
142 goto usage;
143 }
144 if (!cacert)
145 {
146 error = "--cacert is required";
147 goto usage;
148 }
149 if (!cakey)
150 {
151 error = "--cakey is required";
152 goto usage;
153 }
f03e0e91 154 if (dn)
6be68cc1 155 {
f03e0e91
AS
156 id = identification_create_from_string(dn);
157 if (id->get_type(id) != ID_DER_ASN1_DN)
158 {
159 error = "supplied --dn is not a distinguished name";
160 goto end;
161 }
6be68cc1 162 }
5a4dee6d 163
8b0e0910 164 DBG2(DBG_LIB, "Reading ca certificate:");
6be68cc1
MW
165 ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
166 BUILD_FROM_FILE, cacert, BUILD_END);
167 if (!ca)
168 {
169 error = "parsing CA certificate failed";
170 goto end;
171 }
172 x509 = (x509_t*)ca;
173 if (!(x509->get_flags(x509) & X509_CA))
174 {
175 error = "CA certificate misses CA basicConstraint";
176 goto end;
177 }
6be68cc1
MW
178 public = ca->get_public_key(ca);
179 if (!public)
180 {
181 error = "extracting CA certificate public key failed";
182 goto end;
183 }
5a4dee6d 184
8b0e0910 185 DBG2(DBG_LIB, "Reading ca private key:");
6be68cc1
MW
186 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
187 public->get_type(public),
188 BUILD_FROM_FILE, cakey, BUILD_END);
189 if (!private)
190 {
191 error = "parsing CA private key failed";
192 goto end;
193 }
194 if (!private->belongs_to(private, public))
195 {
196 error = "CA private key does not match CA certificate";
197 goto end;
198 }
199 public->destroy(public);
200
6be68cc1
MW
201 if (hex)
202 {
203 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
204 }
205 else
206 {
207 rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
208
209 if (!rng)
210 {
211 error = "no random number generator found";
212 goto end;
213 }
214 rng->allocate_bytes(rng, 8, &serial);
c0df187c
AS
215 while (*serial.ptr == 0x00)
216 {
217 /* we don't accept a serial number with leading zeroes */
218 rng->get_bytes(rng, 1, serial.ptr);
219 }
6be68cc1
MW
220 rng->destroy(rng);
221 }
f03e0e91
AS
222
223 if (pkcs10)
224 {
225 enumerator_t *enumerator;
226 identification_t *subjectAltName;
227 pkcs10_t *req;
228
8b0e0910 229 DBG2(DBG_LIB, "Reading certificate request");
f03e0e91
AS
230 if (file)
231 {
232 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
233 CERT_PKCS10_REQUEST,
234 BUILD_FROM_FILE, file, BUILD_END);
235 }
236 else
237 {
238 cert_req = lib->creds->create(lib->creds, CRED_CERTIFICATE,
239 CERT_PKCS10_REQUEST,
240 BUILD_FROM_FD, 0, BUILD_END);
241 }
242 if (!cert_req)
243 {
244 error = "parsing certificate request failed";
245 goto end;
246 }
247
248 /* If not set yet use subject from PKCS#10 certificate request as DN */
249 if (!id)
250 {
251 id = cert_req->get_subject(cert_req);
252 id = id->clone(id);
253 }
254
255 /* Add subjectAltNames from PKCS#10 certificate request */
256 req = (pkcs10_t*)cert_req;
257 enumerator = req->create_subjectAltName_enumerator(req);
258 while (enumerator->enumerate(enumerator, &subjectAltName))
259 {
260 san->insert_last(san, subjectAltName->clone(subjectAltName));
261 }
262 enumerator->destroy(enumerator);
263
264 /* Use public key from PKCS#10 certificate request */
265 public = cert_req->get_public_key(cert_req);
266 }
267 else
268 {
8b0e0910 269 DBG2(DBG_LIB, "Reading public key:");
f03e0e91
AS
270 if (file)
271 {
272 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
273 BUILD_FROM_FILE, file, BUILD_END);
274 }
275 else
276 {
277 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
278 BUILD_FROM_FD, 0, BUILD_END);
279 }
280 }
281 if (!public)
282 {
283 error = "parsing public key failed";
284 goto end;
285 }
286
6be68cc1
MW
287 not_before = time(NULL);
288 not_after = not_before + lifetime * 24 * 60 * 60;
4fdb9f6f 289
6be68cc1
MW
290 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
291 BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
292 BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
293 BUILD_NOT_BEFORE_TIME, not_before, BUILD_DIGEST_ALG, digest,
294 BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
295 BUILD_SUBJECT_ALTNAMES, san, BUILD_X509_FLAG, flags,
3e33ae10 296 BUILD_PATHLEN, pathlen,
06a8df11
MW
297 BUILD_CRL_DISTRIBUTION_POINTS, cdps,
298 BUILD_OCSP_ACCESS_LOCATIONS, ocsp, BUILD_END);
6be68cc1
MW
299 if (!cert)
300 {
301 error = "generating certificate failed";
302 goto end;
303 }
0406eeaa 304 if (!cert->get_encoding(cert, CERT_ASN1_DER, &encoding))
6be68cc1
MW
305 {
306 error = "encoding certificate failed";
307 goto end;
308 }
309 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
310 {
311 error = "writing certificate key failed";
312 goto end;
313 }
314
315end:
316 DESTROY_IF(id);
f03e0e91 317 DESTROY_IF(cert_req);
6be68cc1
MW
318 DESTROY_IF(cert);
319 DESTROY_IF(ca);
320 DESTROY_IF(public);
321 DESTROY_IF(private);
322 san->destroy_offset(san, offsetof(identification_t, destroy));
3a7bd9bd 323 cdps->destroy(cdps);
06a8df11 324 ocsp->destroy(ocsp);
6be68cc1
MW
325 free(encoding.ptr);
326 free(serial.ptr);
327
328 if (error)
329 {
330 fprintf(stderr, "%s\n", error);
331 return 1;
332 }
333 return 0;
334
335usage:
336 san->destroy_offset(san, offsetof(identification_t, destroy));
3a7bd9bd 337 cdps->destroy(cdps);
06a8df11 338 ocsp->destroy(ocsp);
3ce9438b 339 return command_usage(error);
6be68cc1
MW
340}
341
342/**
343 * Register the command.
344 */
345static void __attribute__ ((constructor))reg()
346{
3ce9438b 347 command_register((command_t) {
6be68cc1
MW
348 issue, 'i', "issue",
349 "issue a certificate using a CA certificate and key",
350 {"[--in file] [--type pub|pkcs10]",
06a8df11 351 " --cacert file --cakey file --dn subject-dn [--san subjectAltName]+",
3e33ae10 352 "[--lifetime days] [--serial hex] [--crl uri]+ [--ocsp uri]+",
7eab4a1b 353 "[--ca] [--pathlen len] [--flag serverAuth|clientAuth|ocspSigning]+",
ae7452e8 354 "[--digest md5|sha1|sha224|sha256|sha384|sha512]"},
6be68cc1
MW
355 {
356 {"help", 'h', 0, "show usage information"},
357 {"in", 'i', 1, "public key/request file to issue, default: stdin"},
358 {"type", 't', 1, "type of input, default: pub"},
359 {"cacert", 'c', 1, "CA certificate file"},
360 {"cakey", 'k', 1, "CA private key file"},
361 {"dn", 'd', 1, "distinguished name to include as subject"},
362 {"san", 'a', 1, "subjectAltName to include in certificate"},
a2cf26f1 363 {"lifetime",'l', 1, "days the certificate is valid, default: 1095"},
6be68cc1
MW
364 {"serial", 's', 1, "serial number in hex, default: random"},
365 {"ca", 'b', 0, "include CA basicConstraint, default: no"},
3e33ae10 366 {"pathlen", 'p', 1, "set path length constraint"},
ce40bf5d 367 {"flag", 'f', 1, "include extendedKeyUsage flag"},
06a8df11 368 {"crl", 'u', 1, "CRL distribution point URI to include"},
4da11016 369 {"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
6be68cc1 370 {"digest", 'g', 1, "digest for signature creation, default: sha1"},
6be68cc1
MW
371 }
372 });
373}
374