pki tool can issue certificates with CRL distribution points
[strongswan.git] / src / pki / commands / issue.c
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
20 #include <utils/linked_list.h>
21 #include <utils/optionsfrom.h>
22 #include <credentials/certificates/certificate.h>
23 #include <credentials/certificates/x509.h>
24
25 /**
26 * Issue a certificate using a CA certificate and key
27 */
28 static int issue(int argc, char *argv[])
29 {
30 hash_algorithm_t digest = HASH_SHA1;
31 certificate_t *cert = NULL, *ca =NULL;
32 private_key_t *private = NULL;
33 public_key_t *public = NULL;
34 char *file = NULL, *dn = NULL, *hex = NULL, *cacert = NULL, *cakey = NULL;
35 char *error = NULL;
36 identification_t *id = NULL;
37 linked_list_t *san, *cdps;
38 int lifetime = 1080;
39 chunk_t serial = chunk_empty;
40 chunk_t encoding = chunk_empty;
41 time_t not_before, not_after;
42 x509_flag_t flags = 0;
43 x509_t *x509;
44 options_t *options;
45
46 options = options_create();
47 san = linked_list_create();
48 cdps = linked_list_create();
49
50 while (TRUE)
51 {
52 switch (getopt_long(argc, argv, "", command_opts, NULL))
53 {
54 case 'h':
55 goto usage;
56 case '+':
57 if (!options->from(options, optarg, &argc, &argv, optind))
58 {
59 error = "invalid options file";
60 goto usage;
61 }
62 continue;
63 case 't':
64 if (!streq(optarg, "pub"))
65 {
66 error = "invalid input type";
67 goto usage;
68 }
69 continue;
70 case 'g':
71 digest = get_digest(optarg);
72 if (digest == HASH_UNKNOWN)
73 {
74 error = "invalid --digest type";
75 goto usage;
76 }
77 continue;
78 case 'i':
79 file = optarg;
80 continue;
81 case 'c':
82 cacert = optarg;
83 continue;
84 case 'k':
85 cakey = optarg;
86 continue;
87 case 'u':
88 cdps->insert_last(cdps, optarg);
89 continue;
90 case 'd':
91 dn = optarg;
92 continue;
93 case 'a':
94 san->insert_last(san, identification_create_from_string(optarg));
95 continue;
96 case 'l':
97 lifetime = atoi(optarg);
98 if (!lifetime)
99 {
100 error = "invalid --lifetime value";
101 goto usage;
102 }
103 continue;
104 case 's':
105 hex = optarg;
106 continue;
107 case 'b':
108 flags |= X509_CA;
109 continue;
110 case EOF:
111 break;
112 default:
113 error = "invalid --issue option";
114 goto usage;
115 }
116 break;
117 }
118
119 if (!dn)
120 {
121 error = "--dn is required";
122 goto usage;
123 }
124 if (!cacert)
125 {
126 error = "--cacert is required";
127 goto usage;
128 }
129 if (!cakey)
130 {
131 error = "--cakey is required";
132 goto usage;
133 }
134 id = identification_create_from_string(dn);
135 if (id->get_type(id) != ID_DER_ASN1_DN)
136 {
137 error = "supplied --dn is not a distinguished name";
138 goto end;
139 }
140 ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
141 BUILD_FROM_FILE, cacert, BUILD_END);
142 if (!ca)
143 {
144 error = "parsing CA certificate failed";
145 goto end;
146 }
147 x509 = (x509_t*)ca;
148 if (!(x509->get_flags(x509) & X509_CA))
149 {
150 error = "CA certificate misses CA basicConstraint";
151 goto end;
152 }
153
154 public = ca->get_public_key(ca);
155 if (!public)
156 {
157 error = "extracting CA certificate public key failed";
158 goto end;
159 }
160 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
161 public->get_type(public),
162 BUILD_FROM_FILE, cakey, BUILD_END);
163 if (!private)
164 {
165 error = "parsing CA private key failed";
166 goto end;
167 }
168 if (!private->belongs_to(private, public))
169 {
170 error = "CA private key does not match CA certificate";
171 goto end;
172 }
173 public->destroy(public);
174
175 if (file)
176 {
177 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
178 BUILD_FROM_FILE, file, BUILD_END);
179 }
180 else
181 {
182 public = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_ANY,
183 BUILD_FROM_FD, 0, BUILD_END);
184 }
185 if (!public)
186 {
187 error = "parsing public key failed";
188 goto end;
189 }
190
191 if (hex)
192 {
193 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
194 }
195 else
196 {
197 rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
198
199 if (!rng)
200 {
201 error = "no random number generator found";
202 goto end;
203 }
204 rng->allocate_bytes(rng, 8, &serial);
205 rng->destroy(rng);
206 }
207 not_before = time(NULL);
208 not_after = not_before + lifetime * 24 * 60 * 60;
209 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
210 BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
211 BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
212 BUILD_NOT_BEFORE_TIME, not_before, BUILD_DIGEST_ALG, digest,
213 BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial,
214 BUILD_SUBJECT_ALTNAMES, san, BUILD_X509_FLAG, flags,
215 BUILD_CRL_DISTRIBUTION_POINTS, cdps, BUILD_END);
216 if (!cert)
217 {
218 error = "generating certificate failed";
219 goto end;
220 }
221 encoding = cert->get_encoding(cert);
222 if (!encoding.ptr)
223 {
224 error = "encoding certificate failed";
225 goto end;
226 }
227 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
228 {
229 error = "writing certificate key failed";
230 goto end;
231 }
232
233 end:
234 DESTROY_IF(id);
235 DESTROY_IF(cert);
236 DESTROY_IF(ca);
237 DESTROY_IF(public);
238 DESTROY_IF(private);
239 san->destroy_offset(san, offsetof(identification_t, destroy));
240 cdps->destroy(cdps);
241 options->destroy(options);
242 free(encoding.ptr);
243 free(serial.ptr);
244
245 if (error)
246 {
247 fprintf(stderr, "%s\n", error);
248 return 1;
249 }
250 return 0;
251
252 usage:
253 san->destroy_offset(san, offsetof(identification_t, destroy));
254 cdps->destroy(cdps);
255 options->destroy(options);
256 return command_usage(error);
257 }
258
259 /**
260 * Register the command.
261 */
262 static void __attribute__ ((constructor))reg()
263 {
264 command_register((command_t) {
265 issue, 'i', "issue",
266 "issue a certificate using a CA certificate and key",
267 {"[--in file] [--type pub|pkcs10]",
268 " --cacert file --cakey file [--cdp uri]+",
269 " --dn subject-dn [--san subjectAltName]+",
270 "[--lifetime days] [--serial hex] [--ca]",
271 "[--digest md5|sha1|sha224|sha256|sha384|sha512]",
272 "[--options file]"},
273 {
274 {"help", 'h', 0, "show usage information"},
275 {"in", 'i', 1, "public key/request file to issue, default: stdin"},
276 {"type", 't', 1, "type of input, default: pub"},
277 {"cacert", 'c', 1, "CA certificate file"},
278 {"cakey", 'k', 1, "CA private key file"},
279 {"cdp", 'u', 1, "CRL distribution point URI to include"},
280 {"dn", 'd', 1, "distinguished name to include as subject"},
281 {"san", 'a', 1, "subjectAltName to include in certificate"},
282 {"lifetime",'l', 1, "days the certificate is valid, default: 1080"},
283 {"serial", 's', 1, "serial number in hex, default: random"},
284 {"ca", 'b', 0, "include CA basicConstraint, default: no"},
285 {"digest", 'g', 1, "digest for signature creation, default: sha1"},
286 {"options", '+', 1, "read command line options from file"},
287 }
288 });
289 }
290