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_SHA256
;
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 (!private->belongs_to(private, public))
167 error
= "issuer private key does not match issuer certificate";
173 serial
= chunk_from_hex(chunk_create(hex
, strlen(hex
)), NULL
);
177 rng
= lib
->crypto
->create_rng(lib
->crypto
, RNG_WEAK
);
180 error
= "no random number generator found";
183 if (!rng_allocate_bytes_not_zero(rng
, 8, &serial
, FALSE
))
185 error
= "failed to generate serial number";
189 serial
.ptr
[0] &= 0x7F;
195 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
196 BUILD_FROM_FILE
, file
, BUILD_END
);
200 set_file_mode(stdin
, CERT_ASN1_DER
);
201 if (!chunk_from_fd(0, &encoding
))
203 fprintf(stderr
, "%s: ", strerror(errno
));
204 error
= "reading public key failed";
207 cert
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509
,
208 BUILD_BLOB
, encoding
, BUILD_END
);
209 chunk_free(&encoding
);
213 error
= "parsing user certificate failed";
217 ac
= lib
->creds
->create(lib
->creds
,
218 CRED_CERTIFICATE
, CERT_X509_AC
,
220 BUILD_NOT_BEFORE_TIME
, not_before
,
221 BUILD_NOT_AFTER_TIME
, not_after
,
222 BUILD_SERIAL
, serial
,
223 BUILD_AC_GROUP_STRINGS
, groups
,
224 BUILD_SIGNING_CERT
, issuer
,
225 BUILD_SIGNING_KEY
, private,
229 error
= "generating attribute certificate failed";
232 if (!ac
->get_encoding(ac
, form
, &encoding
))
234 error
= "encoding attribute certificate failed";
237 set_file_mode(stdout
, form
);
238 if (fwrite(encoding
.ptr
, encoding
.len
, 1, stdout
) != 1)
240 error
= "writing attribute certificate key failed";
250 groups
->destroy(groups
);
256 fprintf(stderr
, "%s\n", error
);
262 groups
->destroy(groups
);
263 return command_usage(error
);
267 * Register the command.
269 static void __attribute__ ((constructor
))reg()
271 command_register((command_t
) {
273 "issue an attribute certificate",
274 {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
275 " --issuercert file [--serial hex] [--lifetime hours]",
276 " [--not-before datetime] [--not-after datetime] [--dateform form]",
277 "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
279 {"help", 'h', 0, "show usage information"},
280 {"in", 'i', 1, "holder certificate, default: stdin"},
281 {"group", 'm', 1, "group membership string to include"},
282 {"issuercert", 'c', 1, "issuer certificate file"},
283 {"issuerkey", 'k', 1, "issuer private key file"},
284 {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"},
285 {"serial", 's', 1, "serial number in hex, default: random"},
286 {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
287 {"not-before", 'F', 1, "date/time the validity of the AC starts"},
288 {"not-after", 'T', 1, "date/time the validity of the AC ends"},
289 {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
290 {"digest", 'g', 1, "digest for signature creation, default: sha256"},
291 {"outform", 'f', 1, "encoding of generated cert, default: der"},