pki: Optionally generate RSA/PSS signatures
[strongswan.git] / src / pki / commands / acert.c
1 /*
2 * Copyright (C) 2009 Martin Willi
3 * Copyright (C) 2015-2017 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
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>
18 #include <errno.h>
19
20 #include "pki.h"
21
22 #include <utils/debug.h>
23 #include <asn1/asn1.h>
24 #include <collections/linked_list.h>
25 #include <credentials/certificates/certificate.h>
26 #include <credentials/certificates/x509.h>
27 #include <credentials/certificates/ac.h>
28
29 /**
30 * Issue an attribute certificate
31 */
32 static int acert()
33 {
34 cred_encoding_type_t form = CERT_ASN1_DER;
35 hash_algorithm_t digest = HASH_UNKNOWN;
36 signature_params_t *scheme = NULL;
37 certificate_t *ac = NULL, *cert = NULL, *issuer =NULL;
38 private_key_t *private = NULL;
39 public_key_t *public = NULL;
40 char *file = NULL, *hex = NULL, *issuercert = NULL, *issuerkey = NULL;
41 char *error = NULL, *keyid = NULL;
42 linked_list_t *groups;
43 chunk_t serial = chunk_empty, encoding = chunk_empty;
44 time_t not_before, not_after, lifetime = 24 * 60 * 60;
45 char *datenb = NULL, *datena = NULL, *dateform = NULL;
46 rng_t *rng;
47 char *arg;
48 bool pss = FALSE;
49
50 groups = linked_list_create();
51
52 while (TRUE)
53 {
54 switch (command_getopt(&arg))
55 {
56 case 'h':
57 goto usage;
58 case 'g':
59 if (!enum_from_name(hash_algorithm_short_names, arg, &digest))
60 {
61 error = "invalid --digest type";
62 goto usage;
63 }
64 continue;
65 case 'R':
66 if (streq(arg, "pss"))
67 {
68 pss = TRUE;
69 }
70 else if (!streq(arg, "pkcs1"))
71 {
72 error = "invalid RSA padding";
73 goto usage;
74 }
75 continue;
76 case 'i':
77 file = arg;
78 continue;
79 case 'm':
80 groups->insert_last(groups, arg);
81 continue;
82 case 'c':
83 issuercert = arg;
84 continue;
85 case 'k':
86 issuerkey = arg;
87 continue;
88 case 'x':
89 keyid = arg;
90 continue;
91 case 'l':
92 lifetime = atoi(arg) * 60 * 60;
93 if (!lifetime)
94 {
95 error = "invalid --lifetime value";
96 goto usage;
97 }
98 continue;
99 case 'D':
100 dateform = arg;
101 continue;
102 case 'F':
103 datenb = arg;
104 continue;
105 case 'T':
106 datena = arg;
107 continue;
108 case 's':
109 hex = arg;
110 continue;
111 case 'f':
112 if (!get_form(arg, &form, CRED_CERTIFICATE))
113 {
114 error = "invalid output format";
115 goto usage;
116 }
117 continue;
118 case EOF:
119 break;
120 default:
121 error = "invalid --acert option";
122 goto usage;
123 }
124 break;
125 }
126
127 if (!calculate_lifetime(dateform, datenb, datena, lifetime,
128 &not_before, &not_after))
129 {
130 error = "invalid --not-before/after datetime";
131 goto usage;
132 }
133
134 if (!issuercert)
135 {
136 error = "--issuercert is required";
137 goto usage;
138 }
139 if (!issuerkey && !keyid)
140 {
141 error = "--issuerkey or --issuerkeyid is required";
142 goto usage;
143 }
144
145 issuer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
146 BUILD_FROM_FILE, issuercert, BUILD_END);
147 if (!issuer)
148 {
149 error = "parsing issuer certificate failed";
150 goto end;
151 }
152 public = issuer->get_public_key(issuer);
153 if (!public)
154 {
155 error = "extracting issuer certificate public key failed";
156 goto end;
157 }
158 if (issuerkey)
159 {
160 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
161 public->get_type(public),
162 BUILD_FROM_FILE, issuerkey, BUILD_END);
163 }
164 else
165 {
166 chunk_t chunk;
167
168 chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
169 private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
170 BUILD_PKCS11_KEYID, chunk, BUILD_END);
171 free(chunk.ptr);
172 }
173 if (!private)
174 {
175 error = "loading issuer private key failed";
176 goto end;
177 }
178 if (!private->belongs_to(private, public))
179 {
180 error = "issuer private key does not match issuer certificate";
181 goto end;
182 }
183
184 if (hex)
185 {
186 serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
187 }
188 else
189 {
190 rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
191 if (!rng)
192 {
193 error = "no random number generator found";
194 goto end;
195 }
196 if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
197 {
198 error = "failed to generate serial number";
199 rng->destroy(rng);
200 goto end;
201 }
202 serial.ptr[0] &= 0x7F;
203 rng->destroy(rng);
204 }
205
206 if (file)
207 {
208 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
209 BUILD_FROM_FILE, file, BUILD_END);
210 }
211 else
212 {
213 set_file_mode(stdin, CERT_ASN1_DER);
214 if (!chunk_from_fd(0, &encoding))
215 {
216 fprintf(stderr, "%s: ", strerror(errno));
217 error = "reading public key failed";
218 goto end;
219 }
220 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
221 BUILD_BLOB, encoding, BUILD_END);
222 chunk_free(&encoding);
223 }
224 if (!cert)
225 {
226 error = "parsing user certificate failed";
227 goto end;
228 }
229 scheme = get_signature_scheme(private, digest, pss);
230
231 ac = lib->creds->create(lib->creds,
232 CRED_CERTIFICATE, CERT_X509_AC,
233 BUILD_CERT, cert,
234 BUILD_NOT_BEFORE_TIME, not_before,
235 BUILD_NOT_AFTER_TIME, not_after,
236 BUILD_SERIAL, serial,
237 BUILD_AC_GROUP_STRINGS, groups,
238 BUILD_SIGNING_CERT, issuer,
239 BUILD_SIGNING_KEY, private,
240 BUILD_SIGNATURE_SCHEME, scheme,
241 BUILD_END);
242 if (!ac)
243 {
244 error = "generating attribute certificate failed";
245 goto end;
246 }
247 if (!ac->get_encoding(ac, form, &encoding))
248 {
249 error = "encoding attribute certificate failed";
250 goto end;
251 }
252 set_file_mode(stdout, form);
253 if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
254 {
255 error = "writing attribute certificate key failed";
256 goto end;
257 }
258
259 end:
260 DESTROY_IF(ac);
261 DESTROY_IF(cert);
262 DESTROY_IF(issuer);
263 DESTROY_IF(public);
264 DESTROY_IF(private);
265 groups->destroy(groups);
266 signature_params_destroy(scheme);
267 free(encoding.ptr);
268 free(serial.ptr);
269
270 if (error)
271 {
272 fprintf(stderr, "%s\n", error);
273 return 1;
274 }
275 return 0;
276
277 usage:
278 groups->destroy(groups);
279 return command_usage(error);
280 }
281
282 /**
283 * Register the command.
284 */
285 static void __attribute__ ((constructor))reg()
286 {
287 command_register((command_t) {
288 acert, 'z', "acert",
289 "issue an attribute certificate",
290 {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
291 " --issuercert file [--serial hex] [--lifetime hours]",
292 " [--not-before datetime] [--not-after datetime] [--dateform form]",
293 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
294 "[--rsa-padding pkcs1|pss]",
295 "[--outform der|pem]"},
296 {
297 {"help", 'h', 0, "show usage information"},
298 {"in", 'i', 1, "holder certificate, default: stdin"},
299 {"group", 'm', 1, "group membership string to include"},
300 {"issuercert", 'c', 1, "issuer certificate file"},
301 {"issuerkey", 'k', 1, "issuer private key file"},
302 {"issuerkeyid", 'x', 1, "smartcard or TPM issuer private key object handle"},
303 {"serial", 's', 1, "serial number in hex, default: random"},
304 {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
305 {"not-before", 'F', 1, "date/time the validity of the AC starts"},
306 {"not-after", 'T', 1, "date/time the validity of the AC ends"},
307 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
308 {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
309 {"rsa-padding", 'R', 1, "padding for RSA signatures, default: pkcs1"},
310 {"outform", 'f', 1, "encoding of generated cert, default: der"},
311 }
312 });
313 }