2 * Copyright (C) 2009 Martin Willi
3 * Hochschule fuer Technik Rapperswil
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>.
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
21 #include <utils/debug.h>
22 #include <asn1/asn1.h>
23 #include <collections/linked_list.h>
24 #include <credentials/certificates/certificate.h>
25 #include <credentials/certificates/x509.h>
26 #include <credentials/certificates/ac.h>
29 * Issue an attribute certificate
33 cred_encoding_type_t form
= CERT_ASN1_DER
;
34 hash_algorithm_t digest
= HASH_SHA1
;
35 certificate_t
*ac
= NULL
, *cert
= NULL
, *issuer
=NULL
;
36 private_key_t
*private = NULL
;
37 public_key_t
*public = NULL
;
38 char *file
= NULL
, *hex
= NULL
, *issuercert
= NULL
, *issuerkey
= NULL
;
39 char *error
= NULL
, *keyid
= NULL
;
40 linked_list_t
*groups
;
41 chunk_t serial
= chunk_empty
, encoding
= chunk_empty
;
42 time_t not_before
, not_after
, lifetime
= 24 * 60 * 60;
43 char *datenb
= NULL
, *datena
= NULL
, *dateform
= NULL
;
47 groups
= linked_list_create();
51 switch (command_getopt(&arg
))
56 if (!enum_from_name(hash_algorithm_short_names
, arg
, &digest
))
58 error
= "invalid --digest type";
66 groups
->insert_last(groups
, arg
);
78 lifetime
= atoi(arg
) * 60 * 60;
81 error
= "invalid --lifetime value";
98 if (!get_form(arg
, &form
, CRED_CERTIFICATE
))
100 error
= "invalid output format";
107 error
= "invalid --acert option";
113 if (!calculate_lifetime(dateform
, datenb
, datena
, lifetime
,
114 ¬_before
, ¬_after
))
116 error
= "invalid --not-before/after datetime";
122 error
= "--issuercert is required";
125 if (!issuerkey
&& !keyid
)
127 error
= "--issuerkey or --issuerkeyid is required";
131 issuer
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
132 BUILD_FROM_FILE
, issuercert
, BUILD_END
);
135 error
= "parsing issuer certificate failed";
138 public = issuer
->get_public_key(issuer
);
141 error
= "extracting issuer certificate public key failed";
146 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
,
147 public->get_type(public),
148 BUILD_FROM_FILE
, issuerkey
, BUILD_END
);
154 chunk
= chunk_from_hex(chunk_create(keyid
, strlen(keyid
)), NULL
);
155 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
, KEY_ANY
,
156 BUILD_PKCS11_KEYID
, chunk
, BUILD_END
);
161 error
= "loading issuer private key failed";
164 if (!private->belongs_to(private, public))
166 error
= "issuer private key does not match issuer certificate";
172 serial
= chunk_from_hex(chunk_create(hex
, strlen(hex
)), NULL
);
176 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
179 error
= "no random number generator found";
182 if (!rng_allocate_bytes_not_zero(rng
, 8, &serial
, FALSE
))
184 error
= "failed to generate serial number";
188 serial
.ptr
[0] &= 0x7F;
194 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
195 BUILD_FROM_FILE
, file
, BUILD_END
);
199 set_file_mode(stdin
, CERT_ASN1_DER
);
200 if (!chunk_from_fd(0, &encoding
))
202 fprintf(stderr
, "%s: ", strerror(errno
));
203 error
= "reading public key failed";
206 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
207 BUILD_BLOB
, encoding
, BUILD_END
);
208 chunk_free(&encoding
);
212 error
= "parsing user certificate failed";
216 ac
= lib
->creds
->create(lib
->creds
,
217 CRED_CERTIFICATE
, CERT_X509_AC
,
219 BUILD_NOT_BEFORE_TIME
, not_before
,
220 BUILD_NOT_AFTER_TIME
, not_after
,
221 BUILD_SERIAL
, serial
,
222 BUILD_AC_GROUP_STRINGS
, groups
,
223 BUILD_SIGNING_CERT
, issuer
,
224 BUILD_SIGNING_KEY
, private,
228 error
= "generating attribute certificate failed";
231 if (!ac
->get_encoding(ac
, form
, &encoding
))
233 error
= "encoding attribute certificate failed";
236 set_file_mode(stdout
, form
);
237 if (fwrite(encoding
.ptr
, encoding
.len
, 1, stdout
) != 1)
239 error
= "writing attribute certificate key failed";
249 groups
->destroy(groups
);
255 fprintf(stderr
, "%s\n", error
);
261 groups
->destroy(groups
);
262 return command_usage(error
);
266 * Register the command.
268 static void __attribute__ ((constructor
))reg()
270 command_register((command_t
) {
272 "issue an attribute certificate",
273 {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
274 " --issuercert file [--serial hex] [--lifetime hours]",
275 " [--not-before datetime] [--not-after datetime] [--dateform form]",
276 "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
278 {"help", 'h', 0, "show usage information"},
279 {"in", 'i', 1, "holder certificate, default: stdin"},
280 {"group", 'm', 1, "group membership string to include"},
281 {"issuercert", 'c', 1, "issuer certificate file"},
282 {"issuerkey", 'k', 1, "issuer private key file"},
283 {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"},
284 {"serial", 's', 1, "serial number in hex, default: random"},
285 {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
286 {"not-before", 'F', 1, "date/time the validity of the AC starts"},
287 {"not-after", 'T', 1, "date/time the validity of the AC ends"},
288 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
289 {"digest", 'g', 1, "digest for signature creation, default: sha1"},
290 {"outform", 'f', 1, "encoding of generated cert, default: der"},