2 * Copyright (C) 2009 Martin Willi
3 * Copyright (C) 2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
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>.
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
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>
30 * Issue an attribute certificate
34 cred_encoding_type_t form
= CERT_ASN1_DER
;
35 hash_algorithm_t digest
= HASH_UNKNOWN
;
36 certificate_t
*ac
= NULL
, *cert
= NULL
, *issuer
=NULL
;
37 private_key_t
*private = NULL
;
38 public_key_t
*public = NULL
;
39 char *file
= NULL
, *hex
= NULL
, *issuercert
= NULL
, *issuerkey
= NULL
;
40 char *error
= NULL
, *keyid
= NULL
;
41 linked_list_t
*groups
;
42 chunk_t serial
= chunk_empty
, encoding
= chunk_empty
;
43 time_t not_before
, not_after
, lifetime
= 24 * 60 * 60;
44 char *datenb
= NULL
, *datena
= NULL
, *dateform
= NULL
;
48 groups
= linked_list_create();
52 switch (command_getopt(&arg
))
57 if (!enum_from_name(hash_algorithm_short_names
, arg
, &digest
))
59 error
= "invalid --digest type";
67 groups
->insert_last(groups
, arg
);
79 lifetime
= atoi(arg
) * 60 * 60;
82 error
= "invalid --lifetime value";
99 if (!get_form(arg
, &form
, CRED_CERTIFICATE
))
101 error
= "invalid output format";
108 error
= "invalid --acert option";
114 if (!calculate_lifetime(dateform
, datenb
, datena
, lifetime
,
115 ¬_before
, ¬_after
))
117 error
= "invalid --not-before/after datetime";
123 error
= "--issuercert is required";
126 if (!issuerkey
&& !keyid
)
128 error
= "--issuerkey or --issuerkeyid is required";
132 issuer
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
133 BUILD_FROM_FILE
, issuercert
, BUILD_END
);
136 error
= "parsing issuer certificate failed";
139 public = issuer
->get_public_key(issuer
);
142 error
= "extracting issuer certificate public key failed";
147 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
,
148 public->get_type(public),
149 BUILD_FROM_FILE
, issuerkey
, BUILD_END
);
155 chunk
= chunk_from_hex(chunk_create(keyid
, strlen(keyid
)), NULL
);
156 private = lib
->creds
->create(lib
->creds
, CRED_PRIVATE_KEY
, KEY_ANY
,
157 BUILD_PKCS11_KEYID
, chunk
, BUILD_END
);
162 error
= "loading issuer private key failed";
165 if (digest
== HASH_UNKNOWN
)
167 digest
= get_default_digest(private);
169 if (!private->belongs_to(private, public))
171 error
= "issuer private key does not match issuer certificate";
177 serial
= chunk_from_hex(chunk_create(hex
, strlen(hex
)), NULL
);
181 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
184 error
= "no random number generator found";
187 if (!rng_allocate_bytes_not_zero(rng
, 8, &serial
, FALSE
))
189 error
= "failed to generate serial number";
193 serial
.ptr
[0] &= 0x7F;
199 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
200 BUILD_FROM_FILE
, file
, BUILD_END
);
204 set_file_mode(stdin
, CERT_ASN1_DER
);
205 if (!chunk_from_fd(0, &encoding
))
207 fprintf(stderr
, "%s: ", strerror(errno
));
208 error
= "reading public key failed";
211 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
212 BUILD_BLOB
, encoding
, BUILD_END
);
213 chunk_free(&encoding
);
217 error
= "parsing user certificate failed";
221 ac
= lib
->creds
->create(lib
->creds
,
222 CRED_CERTIFICATE
, CERT_X509_AC
,
224 BUILD_NOT_BEFORE_TIME
, not_before
,
225 BUILD_NOT_AFTER_TIME
, not_after
,
226 BUILD_SERIAL
, serial
,
227 BUILD_AC_GROUP_STRINGS
, groups
,
228 BUILD_SIGNING_CERT
, issuer
,
229 BUILD_SIGNING_KEY
, private,
233 error
= "generating attribute certificate failed";
236 if (!ac
->get_encoding(ac
, form
, &encoding
))
238 error
= "encoding attribute certificate failed";
241 set_file_mode(stdout
, form
);
242 if (fwrite(encoding
.ptr
, encoding
.len
, 1, stdout
) != 1)
244 error
= "writing attribute certificate key failed";
254 groups
->destroy(groups
);
260 fprintf(stderr
, "%s\n", error
);
266 groups
->destroy(groups
);
267 return command_usage(error
);
271 * Register the command.
273 static void __attribute__ ((constructor
))reg()
275 command_register((command_t
) {
277 "issue an attribute certificate",
278 {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
279 " --issuercert file [--serial hex] [--lifetime hours]",
280 " [--not-before datetime] [--not-after datetime] [--dateform form]",
281 "[--digest md5|sha1|sha224|sha256|sha384|sha512|sha3_224|sha3_256|sha3_384|sha3_512]",
282 "[--outform der|pem]"},
284 {"help", 'h', 0, "show usage information"},
285 {"in", 'i', 1, "holder certificate, default: stdin"},
286 {"group", 'm', 1, "group membership string to include"},
287 {"issuercert", 'c', 1, "issuer certificate file"},
288 {"issuerkey", 'k', 1, "issuer private key file"},
289 {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"},
290 {"serial", 's', 1, "serial number in hex, default: random"},
291 {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
292 {"not-before", 'F', 1, "date/time the validity of the AC starts"},
293 {"not-after", 'T', 1, "date/time the validity of the AC ends"},
294 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
295 {"digest", 'g', 1, "digest for signature creation, default: key-specific"},
296 {"outform", 'f', 1, "encoding of generated cert, default: der"},