summaryrefslogtreecommitdiff
path: root/src/pki
diff options
context:
space:
mode:
Diffstat (limited to 'src/pki')
-rw-r--r--src/pki/Makefile.am1
-rw-r--r--src/pki/Makefile.in8
-rw-r--r--src/pki/command.c4
-rw-r--r--src/pki/command.h4
-rw-r--r--src/pki/commands/acert.c292
-rw-r--r--src/pki/commands/issue.c34
-rw-r--r--src/pki/commands/print.c95
-rw-r--r--src/pki/commands/self.c33
-rw-r--r--src/pki/commands/signcrl.c28
-rw-r--r--src/pki/man/Makefile.am1
-rw-r--r--src/pki/man/Makefile.in8
-rw-r--r--src/pki/man/pki---acert.1.in130
-rw-r--r--src/pki/man/pki---issue.1.in27
-rw-r--r--src/pki/man/pki---print.1.in5
-rw-r--r--src/pki/man/pki---self.1.in27
-rw-r--r--src/pki/man/pki---signcrl.1.in27
-rw-r--r--src/pki/man/pki.1.in4
-rw-r--r--src/pki/pki.c53
-rw-r--r--src/pki/pki.h17
19 files changed, 760 insertions, 38 deletions
diff --git a/src/pki/Makefile.am b/src/pki/Makefile.am
index efbed9b2b..266802cf7 100644
--- a/src/pki/Makefile.am
+++ b/src/pki/Makefile.am
@@ -11,6 +11,7 @@ pki_SOURCES = pki.c pki.h command.c command.h \
commands/self.c \
commands/print.c \
commands/signcrl.c \
+ commands/acert.c \
commands/pkcs7.c \
commands/verify.c
diff --git a/src/pki/Makefile.in b/src/pki/Makefile.in
index 461d958da..2dd91e801 100644
--- a/src/pki/Makefile.in
+++ b/src/pki/Makefile.in
@@ -107,7 +107,8 @@ am_pki_OBJECTS = pki.$(OBJEXT) command.$(OBJEXT) \
commands/keyid.$(OBJEXT) commands/pub.$(OBJEXT) \
commands/req.$(OBJEXT) commands/self.$(OBJEXT) \
commands/print.$(OBJEXT) commands/signcrl.$(OBJEXT) \
- commands/pkcs7.$(OBJEXT) commands/verify.$(OBJEXT)
+ commands/acert.$(OBJEXT) commands/pkcs7.$(OBJEXT) \
+ commands/verify.$(OBJEXT)
pki_OBJECTS = $(am_pki_OBJECTS)
pki_DEPENDENCIES = $(top_builddir)/src/libstrongswan/libstrongswan.la
AM_V_lt = $(am__v_lt_@AM_V@)
@@ -386,7 +387,6 @@ nm_LIBS = @nm_LIBS@
nm_ca_dir = @nm_ca_dir@
nm_plugins = @nm_plugins@
oldincludedir = @oldincludedir@
-openac_plugins = @openac_plugins@
pcsclite_CFLAGS = @pcsclite_CFLAGS@
pcsclite_LIBS = @pcsclite_LIBS@
pdfdir = @pdfdir@
@@ -436,6 +436,7 @@ pki_SOURCES = pki.c pki.h command.c command.h \
commands/self.c \
commands/print.c \
commands/signcrl.c \
+ commands/acert.c \
commands/pkcs7.c \
commands/verify.c
@@ -549,6 +550,8 @@ commands/print.$(OBJEXT): commands/$(am__dirstamp) \
commands/$(DEPDIR)/$(am__dirstamp)
commands/signcrl.$(OBJEXT): commands/$(am__dirstamp) \
commands/$(DEPDIR)/$(am__dirstamp)
+commands/acert.$(OBJEXT): commands/$(am__dirstamp) \
+ commands/$(DEPDIR)/$(am__dirstamp)
commands/pkcs7.$(OBJEXT): commands/$(am__dirstamp) \
commands/$(DEPDIR)/$(am__dirstamp)
commands/verify.$(OBJEXT): commands/$(am__dirstamp) \
@@ -567,6 +570,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pki.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/acert.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/gen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/issue.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@commands/$(DEPDIR)/keyid.Po@am__quote@
diff --git a/src/pki/command.c b/src/pki/command.c
index b6966ee0b..075a2279a 100644
--- a/src/pki/command.c
+++ b/src/pki/command.c
@@ -200,7 +200,7 @@ int command_usage(char *error)
fprintf(out, "usage:\n");
if (active == help_idx)
{
- for (i = 0; cmds[i].cmd; i++)
+ for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
{
fprintf(out, " pki --%-7s (-%c) %s\n",
cmds[i].cmd, cmds[i].op, cmds[i].description);
@@ -263,7 +263,7 @@ int command_dispatch(int c, char *v[])
build_opts();
op = getopt_long(c, v, command_optstring, command_opts, NULL);
- for (i = 0; cmds[i].cmd; i++)
+ for (i = 0; i < MAX_COMMANDS && cmds[i].cmd; i++)
{
if (cmds[i].op == op)
{
diff --git a/src/pki/command.h b/src/pki/command.h
index 737f4658d..9cf036bf2 100644
--- a/src/pki/command.h
+++ b/src/pki/command.h
@@ -24,12 +24,12 @@
/**
* Maximum number of commands (+1).
*/
-#define MAX_COMMANDS 11
+#define MAX_COMMANDS 12
/**
* Maximum number of options in a command (+3)
*/
-#define MAX_OPTIONS 32
+#define MAX_OPTIONS 36
/**
* Maximum number of usage summary lines (+1)
diff --git a/src/pki/commands/acert.c b/src/pki/commands/acert.c
new file mode 100644
index 000000000..d49365db5
--- /dev/null
+++ b/src/pki/commands/acert.c
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2009 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include <time.h>
+#include <errno.h>
+
+#include "pki.h"
+
+#include <utils/debug.h>
+#include <asn1/asn1.h>
+#include <collections/linked_list.h>
+#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/ac.h>
+
+/**
+ * Issue an attribute certificate
+ */
+static int acert()
+{
+ cred_encoding_type_t form = CERT_ASN1_DER;
+ hash_algorithm_t digest = HASH_SHA1;
+ certificate_t *ac = NULL, *cert = NULL, *issuer =NULL;
+ private_key_t *private = NULL;
+ public_key_t *public = NULL;
+ char *file = NULL, *hex = NULL, *issuercert = NULL, *issuerkey = NULL;
+ char *error = NULL, *keyid = NULL;
+ linked_list_t *groups;
+ chunk_t serial = chunk_empty, encoding = chunk_empty;
+ time_t not_before, not_after, lifetime = 24 * 60 * 60;
+ char *datenb = NULL, *datena = NULL, *dateform = NULL;
+ rng_t *rng;
+ char *arg;
+
+ groups = linked_list_create();
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ goto usage;
+ case 'g':
+ digest = enum_from_name(hash_algorithm_short_names, arg);
+ if (digest == -1)
+ {
+ error = "invalid --digest type";
+ goto usage;
+ }
+ continue;
+ case 'i':
+ file = arg;
+ continue;
+ case 'm':
+ groups->insert_last(groups, arg);
+ continue;
+ case 'c':
+ issuercert = arg;
+ continue;
+ case 'k':
+ issuerkey = arg;
+ continue;
+ case 'x':
+ keyid = arg;
+ continue;
+ case 'l':
+ lifetime = atoi(arg) * 60 * 60;
+ if (!lifetime)
+ {
+ error = "invalid --lifetime value";
+ goto usage;
+ }
+ continue;
+ case 'D':
+ dateform = arg;
+ continue;
+ case 'F':
+ datenb = arg;
+ continue;
+ case 'T':
+ datena = arg;
+ continue;
+ case 's':
+ hex = arg;
+ continue;
+ case 'f':
+ if (!get_form(arg, &form, CRED_CERTIFICATE))
+ {
+ error = "invalid output format";
+ goto usage;
+ }
+ continue;
+ case EOF:
+ break;
+ default:
+ error = "invalid --acert option";
+ goto usage;
+ }
+ break;
+ }
+
+ if (!calculate_lifetime(dateform, datenb, datena, lifetime,
+ &not_before, &not_after))
+ {
+ error = "invalid --not-before/after datetime";
+ goto usage;
+ }
+
+ if (!issuercert)
+ {
+ error = "--issuercert is required";
+ goto usage;
+ }
+ if (!issuerkey && !keyid)
+ {
+ error = "--issuerkey or --issuerkeyid is required";
+ goto usage;
+ }
+
+ issuer = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_FROM_FILE, issuercert, BUILD_END);
+ if (!issuer)
+ {
+ error = "parsing issuer certificate failed";
+ goto end;
+ }
+ public = issuer->get_public_key(issuer);
+ if (!public)
+ {
+ error = "extracting issuer certificate public key failed";
+ goto end;
+ }
+ if (issuerkey)
+ {
+ private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+ public->get_type(public),
+ BUILD_FROM_FILE, issuerkey, BUILD_END);
+ }
+ else
+ {
+ chunk_t chunk;
+
+ chunk = chunk_from_hex(chunk_create(keyid, strlen(keyid)), NULL);
+ private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_ANY,
+ BUILD_PKCS11_KEYID, chunk, BUILD_END);
+ free(chunk.ptr);
+ }
+ if (!private)
+ {
+ error = "loading issuer private key failed";
+ goto end;
+ }
+ if (!private->belongs_to(private, public))
+ {
+ error = "issuer private key does not match issuer certificate";
+ goto end;
+ }
+
+ if (hex)
+ {
+ serial = chunk_from_hex(chunk_create(hex, strlen(hex)), NULL);
+ }
+ else
+ {
+ rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+ if (!rng)
+ {
+ error = "no random number generator found";
+ goto end;
+ }
+ if (!rng_allocate_bytes_not_zero(rng, 8, &serial, FALSE))
+ {
+ error = "failed to generate serial number";
+ rng->destroy(rng);
+ goto end;
+ }
+ serial.ptr[0] &= 0x7F;
+ rng->destroy(rng);
+ }
+
+ if (file)
+ {
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_FROM_FILE, file, BUILD_END);
+ }
+ else
+ {
+ if (!chunk_from_fd(0, &encoding))
+ {
+ fprintf(stderr, "%s: ", strerror(errno));
+ error = "reading public key failed";
+ goto end;
+ }
+ cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_BLOB, encoding, BUILD_END);
+ chunk_free(&encoding);
+ }
+ if (!cert)
+ {
+ error = "parsing user certificate failed";
+ goto end;
+ }
+
+ ac = lib->creds->create(lib->creds,
+ CRED_CERTIFICATE, CERT_X509_AC,
+ BUILD_CERT, cert,
+ BUILD_NOT_BEFORE_TIME, not_before,
+ BUILD_NOT_AFTER_TIME, not_after,
+ BUILD_SERIAL, serial,
+ BUILD_AC_GROUP_STRINGS, groups,
+ BUILD_SIGNING_CERT, issuer,
+ BUILD_SIGNING_KEY, private,
+ BUILD_END);
+ if (!ac)
+ {
+ error = "generating attribute certificate failed";
+ goto end;
+ }
+ if (!ac->get_encoding(ac, form, &encoding))
+ {
+ error = "encoding attribute certificate failed";
+ goto end;
+ }
+ if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
+ {
+ error = "writing attribute certificate key failed";
+ goto end;
+ }
+
+end:
+ DESTROY_IF(ac);
+ DESTROY_IF(cert);
+ DESTROY_IF(issuer);
+ DESTROY_IF(public);
+ DESTROY_IF(private);
+ groups->destroy(groups);
+ free(encoding.ptr);
+ free(serial.ptr);
+
+ if (error)
+ {
+ fprintf(stderr, "%s\n", error);
+ return 1;
+ }
+ return 0;
+
+usage:
+ groups->destroy(groups);
+ return command_usage(error);
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ acert, 'z', "acert",
+ "issue an attribute certificate",
+ {"[--in file] [--group name]* --issuerkey file|--issuerkeyid hex",
+ " --issuercert file [--serial hex] [--lifetime hours]",
+ " [--not-before datetime] [--not-after datetime] [--dateform form]",
+ "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"in", 'i', 1, "holder certificate, default: stdin"},
+ {"group", 'm', 1, "group membership string to include"},
+ {"issuercert", 'c', 1, "issuer certificate file"},
+ {"issuerkey", 'k', 1, "issuer private key file"},
+ {"issuerkeyid", 'x', 1, "keyid on smartcard of issuer private key"},
+ {"serial", 's', 1, "serial number in hex, default: random"},
+ {"lifetime", 'l', 1, "hours the acert is valid, default: 24"},
+ {"not-before", 'F', 1, "date/time the validity of the AC starts"},
+ {"not-after", 'T', 1, "date/time the validity of the AC ends"},
+ {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
+ {"digest", 'g', 1, "digest for signature creation, default: sha1"},
+ {"outform", 'f', 1, "encoding of generated cert, default: der"},
+ }
+ });
+}
diff --git a/src/pki/commands/issue.c b/src/pki/commands/issue.c
index d5c33b89f..d03326e3d 100644
--- a/src/pki/commands/issue.c
+++ b/src/pki/commands/issue.c
@@ -72,8 +72,8 @@ static int issue()
int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT;
chunk_t serial = chunk_empty;
chunk_t encoding = chunk_empty;
- time_t lifetime = 1095;
- time_t not_before, not_after;
+ time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60;
+ char *datenb = NULL, *datena = NULL, *dateform = NULL;
x509_flag_t flags = 0;
x509_t *x509;
x509_cdp_t *cdp = NULL;
@@ -132,13 +132,22 @@ static int issue()
san->insert_last(san, identification_create_from_string(arg));
continue;
case 'l':
- lifetime = atoi(arg);
+ lifetime = atoi(arg) * 24 * 60 * 60;
if (!lifetime)
{
error = "invalid --lifetime value";
goto usage;
}
continue;
+ case 'D':
+ dateform = arg;
+ continue;
+ case 'F':
+ datenb = arg;
+ continue;
+ case 'T':
+ datena = arg;
+ continue;
case 's':
hex = arg;
continue;
@@ -242,6 +251,10 @@ static int issue()
{
flags |= X509_OCSP_SIGNER;
}
+ else if (streq(arg, "msSmartcardLogon"))
+ {
+ flags |= X509_MS_SMARTCARD_LOGON;
+ }
continue;
case 'f':
if (!get_form(arg, &form, CRED_CERTIFICATE))
@@ -285,6 +298,12 @@ static int issue()
error = "--cakey or --keyid is required";
goto usage;
}
+ if (!calculate_lifetime(dateform, datenb, datena, lifetime,
+ &not_before, &not_after))
+ {
+ error = "invalid --not-before/after datetime";
+ goto usage;
+ }
if (dn && *dn)
{
id = identification_create_from_string(dn);
@@ -363,6 +382,7 @@ static int issue()
rng->destroy(rng);
goto end;
}
+ serial.ptr[0] &= 0x7F;
rng->destroy(rng);
}
@@ -454,9 +474,6 @@ static int issue()
chunk_from_chars(ASN1_SEQUENCE, 0));
}
- not_before = time(NULL);
- not_after = not_before + lifetime * 24 * 60 * 60;
-
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
BUILD_PUBLIC_KEY, public, BUILD_SUBJECT, id,
@@ -536,7 +553,7 @@ static void __attribute__ ((constructor))reg()
{"[--in file] [--type pub|pkcs10] --cakey file|--cakeyid hex",
" --cacert file [--dn subject-dn] [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--ca] [--pathlen len]",
- "[--flag serverAuth|clientAuth|crlSign|ocspSigning]+",
+ "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+",
"[--crl uri [--crlissuer i]]+ [--ocsp uri]+ [--nc-permitted name]",
"[--nc-excluded name] [--policy-mapping issuer-oid:subject-oid]",
"[--policy-explicit len] [--policy-inhibit len] [--policy-any len]",
@@ -552,6 +569,9 @@ static void __attribute__ ((constructor))reg()
{"dn", 'd', 1, "distinguished name to include as subject"},
{"san", 'a', 1, "subjectAltName to include in certificate"},
{"lifetime", 'l', 1, "days the certificate is valid, default: 1095"},
+ {"not-before", 'F', 1, "date/time the validity of the cert starts"},
+ {"not-after", 'T', 1, "date/time the validity of the cert ends"},
+ {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"pathlen", 'p', 1, "set path length constraint"},
diff --git a/src/pki/commands/print.c b/src/pki/commands/print.c
index 077c1ef3e..15ace035d 100644
--- a/src/pki/commands/print.c
+++ b/src/pki/commands/print.c
@@ -16,9 +16,11 @@
#include "pki.h"
#include <asn1/asn1.h>
+#include <asn1/oid.h>
#include <credentials/certificates/certificate.h>
#include <credentials/certificates/x509.h>
#include <credentials/certificates/crl.h>
+#include <credentials/certificates/ac.h>
#include <selectors/traffic_selector.h>
#include <time.h>
@@ -138,6 +140,10 @@ static void print_x509(x509_t *x509)
{
printf("iKEIntermediate ");
}
+ if (flags & X509_MS_SMARTCARD_LOGON)
+ {
+ printf("msSmartcardLogon ");
+ }
if (flags & X509_SELF_SIGNED)
{
printf("self-signed ");
@@ -388,6 +394,85 @@ static void print_crl(crl_t *crl)
}
/**
+ * Print AC specific information
+ */
+static void print_ac(ac_t *ac)
+{
+ ac_group_type_t type;
+ identification_t *id;
+ enumerator_t *groups;
+ chunk_t chunk;
+ bool first = TRUE;
+
+ chunk = chunk_skip_zero(ac->get_serial(ac));
+ printf("serial: %#B\n", &chunk);
+
+ id = ac->get_holderIssuer(ac);
+ if (id)
+ {
+ printf("hissuer: \"%Y\"\n", id);
+ }
+ chunk = chunk_skip_zero(ac->get_holderSerial(ac));
+ if (chunk.ptr)
+ {
+ printf("hserial: %#B\n", &chunk);
+ }
+ groups = ac->create_group_enumerator(ac);
+ while (groups->enumerate(groups, &type, &chunk))
+ {
+ int oid;
+ char *str;
+
+ if (first)
+ {
+ printf("groups: ");
+ first = FALSE;
+ }
+ else
+ {
+ printf(" ");
+ }
+ switch (type)
+ {
+ case AC_GROUP_TYPE_STRING:
+ printf("%.*s", (int)chunk.len, chunk.ptr);
+ break;
+ case AC_GROUP_TYPE_OID:
+ oid = asn1_known_oid(chunk);
+ if (oid == OID_UNKNOWN)
+ {
+ str = asn1_oid_to_string(chunk);
+ if (str)
+ {
+ printf("%s", str);
+ free(str);
+ }
+ else
+ {
+ printf("OID:%#B", &chunk);
+ }
+ }
+ else
+ {
+ printf("%s", oid_names[oid].name);
+ }
+ break;
+ case AC_GROUP_TYPE_OCTETS:
+ printf("%#B", &chunk);
+ break;
+ }
+ printf("\n");
+ }
+ groups->destroy(groups);
+
+ chunk = ac->get_authKeyIdentifier(ac);
+ if (chunk.ptr)
+ {
+ printf("authkey: %#B\n", &chunk);
+ }
+}
+
+/**
* Print certificate information
*/
static void print_cert(certificate_t *cert)
@@ -432,6 +517,9 @@ static void print_cert(certificate_t *cert)
case CERT_X509_CRL:
print_crl((crl_t*)cert);
break;
+ case CERT_X509_AC:
+ print_ac((ac_t*)cert);
+ break;
default:
printf("parsing certificate subtype %N not implemented\n",
certificate_type_names, cert->get_type(cert));
@@ -472,6 +560,11 @@ static int print()
type = CRED_CERTIFICATE;
subtype = CERT_X509_CRL;
}
+ else if (streq(arg, "ac"))
+ {
+ type = CRED_CERTIFICATE;
+ subtype = CERT_X509_AC;
+ }
else if (streq(arg, "pub"))
{
type = CRED_PUBLIC_KEY;
@@ -558,7 +651,7 @@ static void __attribute__ ((constructor))reg()
command_register((command_t)
{ print, 'a', "print",
"print a credential in a human readable form",
- {"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509|crl]"},
+ {"[--in file] [--type rsa-priv|ecdsa-priv|pub|x509|crl|ac]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "input file, default: stdin"},
diff --git a/src/pki/commands/self.c b/src/pki/commands/self.c
index c28c9c291..a35a42b89 100644
--- a/src/pki/commands/self.c
+++ b/src/pki/commands/self.c
@@ -60,8 +60,8 @@ static int self()
int inhibit_mapping = X509_NO_CONSTRAINT, require_explicit = X509_NO_CONSTRAINT;
chunk_t serial = chunk_empty;
chunk_t encoding = chunk_empty;
- time_t lifetime = 1095;
- time_t not_before, not_after;
+ time_t not_before, not_after, lifetime = 1095 * 24 * 60 * 60;
+ char *datenb = NULL, *datena = NULL, *dateform = NULL;
x509_flag_t flags = 0;
x509_cert_policy_t *policy = NULL;
char *arg;
@@ -115,13 +115,22 @@ static int self()
san->insert_last(san, identification_create_from_string(arg));
continue;
case 'l':
- lifetime = atoi(arg);
+ lifetime = atoi(arg) * 24 * 60 * 60;
if (!lifetime)
{
error = "invalid --lifetime value";
goto usage;
}
continue;
+ case 'D':
+ dateform = arg;
+ continue;
+ case 'F':
+ datenb = arg;
+ continue;
+ case 'T':
+ datena = arg;
+ continue;
case 's':
hex = arg;
continue;
@@ -225,6 +234,10 @@ static int self()
{
flags |= X509_OCSP_SIGNER;
}
+ else if (streq(arg, "msSmartcardLogon"))
+ {
+ flags |= X509_MS_SMARTCARD_LOGON;
+ }
continue;
case 'f':
if (!get_form(arg, &form, CRED_CERTIFICATE))
@@ -250,6 +263,12 @@ static int self()
error = "--dn is required";
goto usage;
}
+ if (!calculate_lifetime(dateform, datenb, datena, lifetime,
+ &not_before, &not_after))
+ {
+ error = "invalid --not-before/after datetime";
+ goto usage;
+ }
id = identification_create_from_string(dn);
if (id->get_type(id) != ID_DER_ASN1_DN)
{
@@ -314,10 +333,9 @@ static int self()
rng->destroy(rng);
goto end;
}
+ serial.ptr[0] &= 0x7F;
rng->destroy(rng);
}
- not_before = time(NULL);
- not_after = not_before + lifetime * 24 * 60 * 60;
cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_SIGNING_KEY, private, BUILD_PUBLIC_KEY, public,
BUILD_SUBJECT, id, BUILD_NOT_BEFORE_TIME, not_before,
@@ -391,7 +409,7 @@ static void __attribute__ ((constructor))reg()
{" [--in file|--keyid hex] [--type rsa|ecdsa]",
" --dn distinguished-name [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+",
- "[--flag serverAuth|clientAuth|crlSign|ocspSigning]+",
+ "[--flag serverAuth|clientAuth|crlSign|ocspSigning|msSmartcardLogon]+",
"[--nc-permitted name] [--nc-excluded name]",
"[--policy-map issuer-oid:subject-oid]",
"[--policy-explicit len] [--policy-inhibit len] [--policy-any len]",
@@ -405,6 +423,9 @@ static void __attribute__ ((constructor))reg()
{"dn", 'd', 1, "subject and issuer distinguished name"},
{"san", 'a', 1, "subjectAltName to include in certificate"},
{"lifetime", 'l', 1, "days the certificate is valid, default: 1095"},
+ {"not-before", 'F', 1, "date/time the validity of the cert starts"},
+ {"not-after", 'T', 1, "date/time the validity of the cert ends"},
+ {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"pathlen", 'p', 1, "set path length constraint"},
diff --git a/src/pki/commands/signcrl.c b/src/pki/commands/signcrl.c
index 4f9dd291d..c9eebbf59 100644
--- a/src/pki/commands/signcrl.c
+++ b/src/pki/commands/signcrl.c
@@ -124,7 +124,8 @@ static int sign_crl()
int serial_len = 0;
crl_reason_t reason = CRL_REASON_UNSPECIFIED;
time_t thisUpdate, nextUpdate, date = time(NULL);
- time_t lifetime = 15;
+ time_t lifetime = 15 * 24 * 60 * 60;
+ char *datetu = NULL, *datenu = NULL, *dateform = NULL;
linked_list_t *list, *cdps;
enumerator_t *enumerator, *lastenum = NULL;
x509_cdp_t *cdp;
@@ -161,13 +162,22 @@ static int sign_crl()
lastupdate = arg;
continue;
case 'l':
- lifetime = atoi(arg);
+ lifetime = atoi(arg) * 24 * 60 * 60;
if (!lifetime)
{
- error = "invalid lifetime";
+ error = "invalid --lifetime value";
goto usage;
}
continue;
+ case 'D':
+ dateform = arg;
+ continue;
+ case 'F':
+ datetu = arg;
+ continue;
+ case 'T':
+ datenu = arg;
+ continue;
case 'z':
serial_len = read_serial(arg, serial, sizeof(serial));
if (serial_len < 0)
@@ -275,6 +285,12 @@ static int sign_crl()
error = "--cakey or --keyid is required";
goto usage;
}
+ if (!calculate_lifetime(dateform, datetu, datenu, lifetime,
+ &thisUpdate, &nextUpdate))
+ {
+ error = "invalid --this/next-update datetime";
+ goto usage;
+ }
ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
BUILD_FROM_FILE, cacert, BUILD_END);
@@ -321,9 +337,6 @@ static int sign_crl()
goto error;
}
- thisUpdate = time(NULL);
- nextUpdate = thisUpdate + lifetime * 24 * 60 * 60;
-
if (basecrl)
{
lastcrl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
@@ -442,6 +455,9 @@ static void __attribute__ ((constructor))reg()
{"cakey", 'k', 1, "CA private key file"},
{"cakeyid", 'x', 1, "keyid on smartcard of CA private key"},
{"lifetime", 'l', 1, "days the CRL gets a nextUpdate, default: 15"},
+ {"this-update", 'F', 1, "date/time the validity of the CRL starts"},
+ {"next-update", 'T', 1, "date/time the validity of the CRL ends"},
+ {"dateform", 'D', 1, "strptime(3) input format, default: %d.%m.%y %T"},
{"lastcrl", 'a', 1, "CRL of lastUpdate to copy revocations from"},
{"basecrl", 'b', 1, "base CRL to create a delta CRL for"},
{"crluri", 'u', 1, "freshest delta CRL URI to include"},
diff --git a/src/pki/man/Makefile.am b/src/pki/man/Makefile.am
index 618bd4093..4c901ae3c 100644
--- a/src/pki/man/Makefile.am
+++ b/src/pki/man/Makefile.am
@@ -4,6 +4,7 @@ man1_MANS = \
pki---self.1 \
pki---issue.1 \
pki---signcrl.1 \
+ pki---acert.1 \
pki---req.1 \
pki---pkcs7.1 \
pki---keyid.1 \
diff --git a/src/pki/man/Makefile.in b/src/pki/man/Makefile.in
index edbde85b5..5d901a87e 100644
--- a/src/pki/man/Makefile.in
+++ b/src/pki/man/Makefile.in
@@ -84,7 +84,7 @@ DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
$(srcdir)/pki---pkcs7.1.in $(srcdir)/pki---print.1.in \
$(srcdir)/pki---pub.1.in $(srcdir)/pki---req.1.in \
$(srcdir)/pki---self.1.in $(srcdir)/pki---signcrl.1.in \
- $(srcdir)/pki---verify.1.in
+ $(srcdir)/pki---acert.1.in $(srcdir)/pki---verify.1.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \
$(top_srcdir)/m4/config/ltoptions.m4 \
@@ -102,7 +102,7 @@ mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES = pki.1 pki---gen.1 pki---issue.1 pki---keyid.1 \
pki---pkcs7.1 pki---print.1 pki---pub.1 pki---req.1 \
- pki---self.1 pki---signcrl.1 pki---verify.1
+ pki---self.1 pki---signcrl.1 pki---acert.1 pki---verify.1
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -325,7 +325,6 @@ nm_LIBS = @nm_LIBS@
nm_ca_dir = @nm_ca_dir@
nm_plugins = @nm_plugins@
oldincludedir = @oldincludedir@
-openac_plugins = @openac_plugins@
pcsclite_CFLAGS = @pcsclite_CFLAGS@
pcsclite_LIBS = @pcsclite_LIBS@
pdfdir = @pdfdir@
@@ -371,6 +370,7 @@ man1_MANS = \
pki---self.1 \
pki---issue.1 \
pki---signcrl.1 \
+ pki---acert.1 \
pki---req.1 \
pki---pkcs7.1 \
pki---keyid.1 \
@@ -432,6 +432,8 @@ pki---self.1: $(top_builddir)/config.status $(srcdir)/pki---self.1.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
pki---signcrl.1: $(top_builddir)/config.status $(srcdir)/pki---signcrl.1.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+pki---acert.1: $(top_builddir)/config.status $(srcdir)/pki---acert.1.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
pki---verify.1: $(top_builddir)/config.status $(srcdir)/pki---verify.1.in
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
diff --git a/src/pki/man/pki---acert.1.in b/src/pki/man/pki---acert.1.in
new file mode 100644
index 000000000..ec1d8be6e
--- /dev/null
+++ b/src/pki/man/pki---acert.1.in
@@ -0,0 +1,130 @@
+.TH "PKI \-\-ACERT" 1 "2014-02-05" "@PACKAGE_VERSION@" "strongSwan"
+.
+.SH "NAME"
+.
+pki \-\-acert \- Issue an attribute certificate
+.
+.SH "SYNOPSIS"
+.
+.SY pki\ \-\-acert
+.OP \-\-in file
+.OP \-\-group membership
+.BI \-\-issuerkey\~ file |\-\-issuerkeyid\~ hex
+.BI \-\-issuercert\~ file
+.OP \-\-lifetime hours
+.OP \-\-not-before datetime
+.OP \-\-not-after datetime
+.OP \-\-serial hex
+.OP \-\-digest digest
+.OP \-\-outform encoding
+.OP \-\-debug level
+.YS
+.
+.SY pki\ \-\-acert
+.BI \-\-options\~ file
+.YS
+.
+.SY "pki \-\-acert"
+.B \-h
+|
+.B \-\-help
+.YS
+.
+.SH "DESCRIPTION"
+.
+This sub-command of
+.BR pki (1)
+is used to issue an attribute certificate using an issuer certificate with its
+private key and the holder certificate.
+.
+.SH "OPTIONS"
+.
+.TP
+.B "\-h, \-\-help"
+Print usage information with a summary of the available options.
+.TP
+.BI "\-v, \-\-debug " level
+Set debug level, default: 1.
+.TP
+.BI "\-+, \-\-options " file
+Read command line options from \fIfile\fR.
+.TP
+.BI "\-i, \-\-in " file
+Holder certificate to issue an attribute certificate for. If not given the
+certificate is read from \fISTDIN\fR.
+.TP
+.BI "\-m, \-\-group " membership
+Group membership the attribute certificate shall certify. The specified group
+is included as a string. To include multiple groups, the option can be repeated.
+.TP
+.BI "\-k, \-\-issuerkey " file
+Issuer private key file. Either this or
+.B \-\-issuerkeyid
+is required.
+.TP
+.BI "\-x, \-\-issuerkeyid " hex
+Key ID of a issuer private key on a smartcard. Either this or
+.B \-\-issuerkey
+is required.
+.TP
+.BI "\-c, \-\-issuercert " file
+Issuer certificate file. Required.
+.TP
+.BI "\-l, \-\-lifetime " hours
+Hours the attribute certificate is valid, default: 24. Ignored if both
+an absolute start and end time are given.
+.TP
+.BI "\-F, \-\-not-before " datetime
+Absolute time when the validity of the AC begins. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-T, \-\-not-after " datetime
+Absolute time when the validity of the AC ends. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-D, \-\-dateform " form
+strptime(3) format for the
+.B \-\-not\-before
+and
+.B \-\-not\-after
+options, default:
+.B %d.%m.%y %T
+.TP
+.BI "\-s, \-\-serial " hex
+Serial number in hex. It is randomly allocated by default.
+.TP
+.BI "\-g, \-\-digest " digest
+Digest to use for signature creation. One of \fImd5\fR, \fIsha1\fR,
+\fIsha224\fR, \fIsha256\fR, \fIsha384\fR, or \fIsha512\fR. Defaults to
+\fIsha1\fR.
+.TP
+.BI "\-f, \-\-outform " encoding
+Encoding of the created certificate file. Either \fIder\fR (ASN.1 DER) or
+\fIpem\fR (Base64 PEM), defaults to \fIder\fR.
+.
+.SH "EXAMPLES"
+.
+To save repetitive typing, command line options can be stored in files.
+Lets assume
+.I acert.opt
+contains the following contents:
+.PP
+.EX
+ --issuercert aacert.der --issuerkey aakey.der --digest sha256 --lifetime 4
+.EE
+.PP
+Then the following command can be used to issue an attribute certificate based
+on a holder certificate and the options above:
+.PP
+.EX
+ pki --acert --options acert.opt --in holder.der --group sales --group finance -f pem
+.EE
+.PP
+.
+.SH "SEE ALSO"
+.
+.BR pki (1)
diff --git a/src/pki/man/pki---issue.1.in b/src/pki/man/pki---issue.1.in
index 3fad1ae8a..375cb2fe4 100644
--- a/src/pki/man/pki---issue.1.in
+++ b/src/pki/man/pki---issue.1.in
@@ -14,6 +14,8 @@ pki \-\-issue \- Issue a certificate using a CA certificate and key
.OP \-\-dn subject-dn
.OP \-\-san subjectAltName
.OP \-\-lifetime days
+.OP \-\-not-before datetime
+.OP \-\-not-after datetime
.OP \-\-serial hex
.OP \-\-flag flag
.OP \-\-digest digest
@@ -88,7 +90,28 @@ Subject distinguished name (DN) of the issued certificate.
subjectAltName extension to include in certificate. Can be used multiple times.
.TP
.BI "\-l, \-\-lifetime " days
-Days the certificate is valid, default: 1095.
+Days the certificate is valid, default: 1095. Ignored if both
+an absolute start and end time are given.
+.TP
+.BI "\-F, \-\-not-before " datetime
+Absolute time when the validity of the certificate begins. The datetime format
+is defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-T, \-\-not-after " datetime
+Absolute time when the validity of the certificate ends. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-D, \-\-dateform " form
+strptime(3) format for the
+.B \-\-not\-before
+and
+.B \-\-not\-after
+options, default:
+.B %d.%m.%y %T
.TP
.BI "\-s, \-\-serial " hex
Serial number in hex. It is randomly allocated by default.
@@ -176,4 +199,4 @@ given PKCS#10 certificate request and the options above:
.
.SH "SEE ALSO"
.
-.BR pki (1) \ No newline at end of file
+.BR pki (1)
diff --git a/src/pki/man/pki---print.1.in b/src/pki/man/pki---print.1.in
index 8d3345edc..434d4ea16 100644
--- a/src/pki/man/pki---print.1.in
+++ b/src/pki/man/pki---print.1.in
@@ -46,8 +46,9 @@ Input file. If not given the input is read from \fISTDIN\fR.
.BI "\-t, \-\-type " type
Type of input. One of \fIrsa-priv\fR (RSA private key), \fIecdsa-priv\fR (ECDSA
private key), \fIpub\fR (public key), \fIx509\fR (X.509 certificate), \fIcrl\fR
-(Certificate Revocation List, CRL), defaults to \fIx509\fR.
+(Certificate Revocation List, CRL), \fIac\fR (Attribute Certificate),
+defaults to \fIx509\fR.
.
.SH "SEE ALSO"
.
-.BR pki (1) \ No newline at end of file
+.BR pki (1)
diff --git a/src/pki/man/pki---self.1.in b/src/pki/man/pki---self.1.in
index ee42cf9a0..5e6e78bd0 100644
--- a/src/pki/man/pki---self.1.in
+++ b/src/pki/man/pki---self.1.in
@@ -14,6 +14,8 @@ pki \-\-self \- Create a self-signed certificate
.BI \-\-dn\~ distinguished-name
.OP \-\-san subjectAltName
.OP \-\-lifetime days
+.OP \-\-not-before datetime
+.OP \-\-not-after datetime
.OP \-\-serial hex
.OP \-\-flag flag
.OP \-\-digest digest
@@ -75,7 +77,28 @@ Subject and issuer distinguished name (DN). Required.
subjectAltName extension to include in certificate. Can be used multiple times.
.TP
.BI "\-l, \-\-lifetime " days
-Days the certificate is valid, default: 1095.
+Days the certificate is valid, default: 1095. Ignored if both
+an absolute start and end time are given.
+.TP
+.BI "\-F, \-\-not-before " datetime
+Absolute time when the validity of the certificate begins. The datetime format
+is defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-T, \-\-not-after " datetime
+Absolute time when the validity of the certificate ends. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-D, \-\-dateform " form
+strptime(3) format for the
+.B \-\-not\-before
+and
+.B \-\-not\-after
+options, default:
+.B %d.%m.%y %T
.TP
.BI "\-s, \-\-serial " hex
Serial number in hex. It is randomly allocated by default.
@@ -145,4 +168,4 @@ Generate a self-signed certificate using the given RSA key:
.
.SH "SEE ALSO"
.
-.BR pki (1) \ No newline at end of file
+.BR pki (1)
diff --git a/src/pki/man/pki---signcrl.1.in b/src/pki/man/pki---signcrl.1.in
index 6ba96f6bc..bd6cba547 100644
--- a/src/pki/man/pki---signcrl.1.in
+++ b/src/pki/man/pki---signcrl.1.in
@@ -10,6 +10,8 @@ pki \-\-signcrl \- Issue a Certificate Revocation List (CRL) using a CA certific
.BI \-\-cakey\~ file |\-\-cakeyid\~ hex
.BI \-\-cacert\~ file
.OP \-\-lifetime days
+.OP \-\-this-update datetime
+.OP \-\-next-update datetime
.OP \-\-lastcrl crl
.OP \-\-basecrl crl
.OP \-\-crluri uri
@@ -62,7 +64,28 @@ is required.
CA certificate file. Required.
.TP
.BI "\-l, \-\-lifetime " days
-Days until the CRL gets a nextUpdate, default: 15.
+Days until the CRL gets a nextUpdate, default: 15. Ignored if both
+an absolute start and end time are given.
+.TP
+.BI "\-F, \-\-this-update " datetime
+Absolute time when the validity of the CRL begins. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-T, \-\-next-update " datetime
+Absolute time when the validity of the CRL end. The datetime format is
+defined by the
+.B \-\-dateform
+option.
+.TP
+.BI "\-D, \-\-dateform " form
+strptime(3) format for the
+.B \-\-this\-update
+and
+.B \-\-next\-update
+options, default:
+.B %d.%m.%y %T
.TP
.BI "\-a, \-\-lastcrl " crl
CRL of lastUpdate to copy revocations from.
@@ -121,4 +144,4 @@ number, but no reason:
.PP
.SH "SEE ALSO"
.
-.BR pki (1) \ No newline at end of file
+.BR pki (1)
diff --git a/src/pki/man/pki.1.in b/src/pki/man/pki.1.in
index 8dfc53af3..f347031b4 100644
--- a/src/pki/man/pki.1.in
+++ b/src/pki/man/pki.1.in
@@ -49,6 +49,9 @@ Issue a certificate using a CA certificate and key.
.B "\-c, \-\-signcrl"
Issue a CRL using a CA certificate and key.
.TP
+.B "\-z, \-\-acert"
+Issue an attribute certificate.
+.TP
.B "\-r, \-\-req"
Create a PKCS#10 certificate request.
.TP
@@ -148,6 +151,7 @@ certificates with the \-\-crl option.
.BR pki\ \-\-self (1),
.BR pki\ \-\-issue (1),
.BR pki\ \-\-signcrl (1),
+.BR pki\ \-\-acert (1),
.BR pki\ \-\-req (1),
.BR pki\ \-\-pkcs7 (1),
.BR pki\ \-\-keyid (1),
diff --git a/src/pki/pki.c b/src/pki/pki.c
index eb614dd7f..ae4ef1cb0 100644
--- a/src/pki/pki.c
+++ b/src/pki/pki.c
@@ -13,9 +13,11 @@
* for more details.
*/
+#define _GNU_SOURCE
#include "command.h"
#include "pki.h"
+#include <time.h>
#include <unistd.h>
#include <utils/debug.h>
@@ -102,6 +104,56 @@ bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type)
}
/**
+ * See header
+ */
+bool calculate_lifetime(char *format, char *nbstr, char *nastr, time_t span,
+ time_t *nb, time_t *na)
+{
+ struct tm tm;
+ time_t now;
+ char *end;
+
+ if (!format)
+ {
+ format = "%d.%m.%y %T";
+ }
+
+ now = time(NULL);
+
+ localtime_r(&now, &tm);
+ if (nbstr)
+ {
+ end = strptime(nbstr, format, &tm);
+ if (end == NULL || *end != '\0')
+ {
+ return FALSE;
+ }
+ }
+ *nb = mktime(&tm);
+
+ localtime_r(&now, &tm);
+ if (nastr)
+ {
+ end = strptime(nastr, format, &tm);
+ if (end == NULL || *end != '\0')
+ {
+ return FALSE;
+ }
+ }
+ *na = mktime(&tm);
+
+ if (!nbstr && nastr)
+ {
+ *nb = *na - span;
+ }
+ else if (!nastr)
+ {
+ *na = *nb + span;
+ }
+ return TRUE;
+}
+
+/**
* Callback credential set pki uses
*/
static callback_cred_t *cb_set;
@@ -188,4 +240,3 @@ int main(int argc, char *argv[])
atexit(remove_callback);
return command_dispatch(argc, argv);
}
-
diff --git a/src/pki/pki.h b/src/pki/pki.h
index 09c50c6c2..616fac44a 100644
--- a/src/pki/pki.h
+++ b/src/pki/pki.h
@@ -33,4 +33,21 @@
*/
bool get_form(char *form, cred_encoding_type_t *enc, credential_type_t type);
+/**
+ * Calculate start/end lifetime for certificates.
+ *
+ * If both nbstr and nastr are given, span is ignored. Otherwise missing
+ * arguments are calculated, or assumed to be now.
+ *
+ * @param format strptime() format, NULL for default: %d.%m.%y %T
+ * @param nbstr string describing notBefore datetime, or NULL
+ * @param nastr string describing notAfter datetime, or NULL
+ * @param span lifetime span, from notBefore to notAfter
+ * @param nb calculated notBefore time
+ * @param na calculated notAfter time
+ * @return TRUE of nb/na calculated successfully
+ */
+bool calculate_lifetime(char *format, char *nbstr, char *nastr, time_t span,
+ time_t *nb, time_t *na);
+
#endif /** PKI_H_ @}*/