summaryrefslogtreecommitdiff
path: root/src/pki/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/pki/commands')
-rw-r--r--src/pki/commands/gen.c4
-rw-r--r--src/pki/commands/issue.c21
-rw-r--r--src/pki/commands/keyid.c12
-rw-r--r--src/pki/commands/print.c368
-rw-r--r--src/pki/commands/pub.c4
-rw-r--r--src/pki/commands/req.c13
-rw-r--r--src/pki/commands/self.c33
-rw-r--r--src/pki/commands/signcrl.c382
8 files changed, 812 insertions, 25 deletions
diff --git a/src/pki/commands/gen.c b/src/pki/commands/gen.c
index b2769da54..33d9cf35d 100644
--- a/src/pki/commands/gen.c
+++ b/src/pki/commands/gen.c
@@ -20,7 +20,7 @@
*/
static int gen()
{
- key_encoding_type_t form = KEY_PRIV_ASN1_DER;
+ cred_encoding_type_t form = PRIVKEY_ASN1_DER;
key_type_t type = KEY_RSA;
u_int size = 0;
private_key_t *key;
@@ -48,7 +48,7 @@ static int gen()
}
continue;
case 'f':
- if (!get_form(arg, &form, FALSE))
+ if (!get_form(arg, &form, CRED_PRIVATE_KEY))
{
return command_usage("invalid key output format");
}
diff --git a/src/pki/commands/issue.c b/src/pki/commands/issue.c
index fcd758f87..2002cd555 100644
--- a/src/pki/commands/issue.c
+++ b/src/pki/commands/issue.c
@@ -28,6 +28,7 @@
*/
static int issue()
{
+ cred_encoding_type_t form = CERT_ASN1_DER;
hash_algorithm_t digest = HASH_SHA1;
certificate_t *cert_req = NULL, *cert = NULL, *ca =NULL;
private_key_t *private = NULL;
@@ -37,7 +38,7 @@ static int issue()
char *error = NULL;
identification_t *id = NULL;
linked_list_t *san, *cdps, *ocsp;
- int lifetime = 1080;
+ int lifetime = 1095;
int pathlen = X509_NO_PATH_LEN_CONSTRAINT;
chunk_t serial = chunk_empty;
chunk_t encoding = chunk_empty;
@@ -107,7 +108,7 @@ static int issue()
case 'p':
pathlen = atoi(arg);
continue;
- case 'f':
+ case 'e':
if (streq(arg, "serverAuth"))
{
flags |= X509_SERVER_AUTH;
@@ -121,6 +122,12 @@ static int issue()
flags |= X509_OCSP_SIGNER;
}
continue;
+ case 'f':
+ if (!get_form(arg, &form, CRED_CERTIFICATE))
+ {
+ return command_usage("invalid output format");
+ }
+ continue;
case 'u':
cdps->insert_last(cdps, arg);
continue;
@@ -301,8 +308,7 @@ static int issue()
error = "generating certificate failed";
goto end;
}
- encoding = cert->get_encoding(cert);
- if (!encoding.ptr)
+ if (!cert->get_encoding(cert, form, &encoding))
{
error = "encoding certificate failed";
goto end;
@@ -352,7 +358,7 @@ static void __attribute__ ((constructor))reg()
" --cacert file --cakey file --dn subject-dn [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--crl uri]+ [--ocsp uri]+",
"[--ca] [--pathlen len] [--flag serverAuth|clientAuth|ocspSigning]+",
- "[--digest md5|sha1|sha224|sha256|sha384|sha512]"},
+ "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "public key/request file to issue, default: stdin"},
@@ -361,14 +367,15 @@ static void __attribute__ ((constructor))reg()
{"cakey", 'k', 1, "CA private key file"},
{"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: 1080"},
+ {"lifetime",'l', 1, "days the certificate is valid, default: 1095"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"pathlen", 'p', 1, "set path length constraint"},
- {"flag", 'f', 1, "include extendedKeyUsage flag"},
+ {"flag", 'e', 1, "include extendedKeyUsage flag"},
{"crl", 'u', 1, "CRL distribution point URI to include"},
{"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
{"digest", 'g', 1, "digest for signature creation, default: sha1"},
+ {"outform", 'f', 1, "encoding of generated cert, default: der"},
}
});
}
diff --git a/src/pki/commands/keyid.c b/src/pki/commands/keyid.c
index c15c1193e..6d2f7b915 100644
--- a/src/pki/commands/keyid.c
+++ b/src/pki/commands/keyid.c
@@ -99,11 +99,11 @@ static int keyid()
if (type == CRED_PRIVATE_KEY)
{
private = cred;
- if (private->get_fingerprint(private, KEY_ID_PUBKEY_SHA1, &id))
+ if (private->get_fingerprint(private, KEYID_PUBKEY_SHA1, &id))
{
printf("subjectKeyIdentifier: %#B\n", &id);
}
- if (private->get_fingerprint(private, KEY_ID_PUBKEY_INFO_SHA1, &id))
+ if (private->get_fingerprint(private, KEYID_PUBKEY_INFO_SHA1, &id))
{
printf("subjectPublicKeyInfo hash: %#B\n", &id);
}
@@ -112,11 +112,11 @@ static int keyid()
else if (type == CRED_PUBLIC_KEY)
{
public = cred;
- if (public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &id))
+ if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &id))
{
printf("subjectKeyIdentifier: %#B\n", &id);
}
- if (public->get_fingerprint(public, KEY_ID_PUBKEY_INFO_SHA1, &id))
+ if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &id))
{
printf("subjectPublicKeyInfo hash: %#B\n", &id);
}
@@ -131,11 +131,11 @@ static int keyid()
fprintf(stderr, "extracting public key from certificate failed");
return 1;
}
- if (public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &id))
+ if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &id))
{
printf("subjectKeyIdentifier: %#B\n", &id);
}
- if (public->get_fingerprint(public, KEY_ID_PUBKEY_INFO_SHA1, &id))
+ if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &id))
{
printf("subjectPublicKeyInfo hash: %#B\n", &id);
}
diff --git a/src/pki/commands/print.c b/src/pki/commands/print.c
new file mode 100644
index 000000000..6d5462783
--- /dev/null
+++ b/src/pki/commands/print.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * 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 "pki.h"
+
+#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/x509.h>
+#include <selectors/traffic_selector.h>
+
+#include <time.h>
+
+/**
+ * Print public key information
+ */
+static void print_pubkey(public_key_t *key)
+{
+ chunk_t chunk;
+
+ printf("pubkey: %N %d bits\n", key_type_names, key->get_type(key),
+ key->get_keysize(key) * 8);
+ if (key->get_fingerprint(key, KEYID_PUBKEY_INFO_SHA1, &chunk))
+ {
+ printf("keyid: %#B\n", &chunk);
+ }
+ if (key->get_fingerprint(key, KEYID_PUBKEY_SHA1, &chunk))
+ {
+ printf("subjkey: %#B\n", &chunk);
+ }
+}
+
+/**
+ * Print private key information
+ */
+static void print_key(private_key_t *key)
+{
+ public_key_t *public;
+
+ public = key->get_public_key(key);
+ if (public)
+ {
+ printf("private key with:\n");
+ print_pubkey(public);
+ public->destroy(public);
+ }
+ else
+ {
+ printf("extracting public from private key failed\n");
+ }
+}
+
+/**
+ * Print X509 specific certificate information
+ */
+static void print_x509(x509_t *x509)
+{
+ enumerator_t *enumerator;
+ identification_t *id;
+ traffic_selector_t *block;
+ chunk_t chunk;
+ bool first;
+ char *uri;
+ int len;
+ x509_flag_t flags;
+
+ chunk = x509->get_serial(x509);
+ printf("serial: %#B\n", &chunk);
+
+ first = TRUE;
+ enumerator = x509->create_subjectAltName_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &id))
+ {
+ if (first)
+ {
+ printf("altNames: ");
+ first = FALSE;
+ }
+ else
+ {
+ printf(", ");
+ }
+ printf("%Y", id);
+ }
+ if (!first)
+ {
+ printf("\n");
+ }
+ enumerator->destroy(enumerator);
+
+ flags = x509->get_flags(x509);
+ printf("flags: ");
+ if (flags & X509_CA)
+ {
+ printf("CA ");
+ }
+ if (flags & X509_AA)
+ {
+ printf("AA ");
+ }
+ if (flags & X509_OCSP_SIGNER)
+ {
+ printf("OCSP ");
+ }
+ if (flags & X509_AA)
+ {
+ printf("AA ");
+ }
+ if (flags & X509_SERVER_AUTH)
+ {
+ printf("serverAuth ");
+ }
+ if (flags & X509_CLIENT_AUTH)
+ {
+ printf("clientAuth ");
+ }
+ if (flags & X509_SELF_SIGNED)
+ {
+ printf("self-signed ");
+ }
+ printf("\n");
+
+ first = TRUE;
+ enumerator = x509->create_crl_uri_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ if (first)
+ {
+ printf("CRL URIs: %s\n", uri);
+ first = FALSE;
+ }
+ else
+ {
+ printf(" %s\n", uri);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ first = TRUE;
+ enumerator = x509->create_ocsp_uri_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &uri))
+ {
+ if (first)
+ {
+ printf("OCSP URIs: %s\n", uri);
+ first = FALSE;
+ }
+ else
+ {
+ printf(" %s\n", uri);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ len = x509->get_pathLenConstraint(x509);
+ if (len != X509_NO_PATH_LEN_CONSTRAINT)
+ {
+ printf("pathlen: %d\n", len);
+ }
+
+ chunk = x509->get_authKeyIdentifier(x509);
+ if (chunk.ptr)
+ {
+ printf("authkeyId: %#B\n", &chunk);
+ }
+
+ chunk = x509->get_subjectKeyIdentifier(x509);
+ if (chunk.ptr)
+ {
+ printf("subjkeyId: %#B\n", &chunk);
+ }
+ if (x509->get_flags(x509) & X509_IP_ADDR_BLOCKS)
+ {
+ first = TRUE;
+ printf("addresses: ");
+ enumerator = x509->create_ipAddrBlock_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &block))
+ {
+ if (first)
+ {
+ first = FALSE;
+ }
+ else
+ {
+ printf(", ");
+ }
+ printf("%R", block);
+ }
+ enumerator->destroy(enumerator);
+ printf("\n");
+ }
+}
+
+/**
+ * Print certificate information
+ */
+static void print_cert(certificate_t *cert)
+{
+ time_t now, notAfter, notBefore;
+ public_key_t *key;
+
+ now = time(NULL);
+
+ printf("cert: %N\n", certificate_type_names, cert->get_type(cert));
+ printf("subject: \"%Y\"\n", cert->get_subject(cert));
+ printf("issuer: \"%Y\"\n", cert->get_issuer(cert));
+
+ cert->get_validity(cert, &now, &notBefore, &notAfter);
+ printf("validity: not before %T, ", &notBefore, FALSE);
+ if (now < notBefore)
+ {
+ printf("not valid yet (valid in %V)\n", &now, &notBefore);
+ }
+ else
+ {
+ printf("ok\n");
+ }
+ printf(" not after %T, ", &notAfter, FALSE);
+ if (now > notAfter)
+ {
+ printf("expired (%V ago)\n", &now, &notAfter);
+ }
+ else
+ {
+ printf("ok (expires in %V)\n", &now, &notAfter);
+ }
+
+ switch (cert->get_type(cert))
+ {
+ case CERT_X509:
+ print_x509((x509_t*)cert);
+ break;
+ default:
+ printf("parsing certificate subtype %N not implemented\n",
+ certificate_type_names, cert->get_type(cert));
+ break;
+ }
+
+ key = cert->get_public_key(cert);
+ if (key)
+ {
+ print_pubkey(key);
+ key->destroy(key);
+ }
+ else
+ {
+ printf("unable to extract public key\n");
+ }
+}
+
+/**
+ * Print a credential in a human readable form
+ */
+static int print()
+{
+ credential_type_t type = CRED_CERTIFICATE;
+ int subtype = CERT_X509;
+ void *cred;
+ char *arg, *file = NULL;
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ return command_usage(NULL);
+ case 't':
+ if (streq(arg, "x509"))
+ {
+ type = CRED_CERTIFICATE;
+ subtype = CERT_X509;
+ }
+ else if (streq(arg, "pub"))
+ {
+ type = CRED_PUBLIC_KEY;
+ subtype = KEY_ANY;
+ }
+ else if (streq(arg, "rsa-priv"))
+ {
+ type = CRED_PRIVATE_KEY;
+ subtype = KEY_RSA;
+ }
+ else if (streq(arg, "ecdsa-priv"))
+ {
+ type = CRED_PRIVATE_KEY;
+ subtype = KEY_ECDSA;
+ }
+ else
+ {
+ return command_usage( "invalid input type");
+ }
+ continue;
+ case 'i':
+ file = arg;
+ continue;
+ case EOF:
+ break;
+ default:
+ return command_usage("invalid --print option");
+ }
+ break;
+ }
+ if (file)
+ {
+ cred = lib->creds->create(lib->creds, type, subtype,
+ BUILD_FROM_FILE, file, BUILD_END);
+ }
+ else
+ {
+ cred = lib->creds->create(lib->creds, type, subtype,
+ BUILD_FROM_FD, 0, BUILD_END);
+ }
+ if (!cred)
+ {
+ fprintf(stderr, "parsing input failed\n");
+ return 1;
+ }
+
+ if (type == CRED_CERTIFICATE)
+ {
+ certificate_t *cert = (certificate_t*)cred;
+
+ print_cert(cert);
+ cert->destroy(cert);
+ }
+ if (type == CRED_PUBLIC_KEY)
+ {
+ public_key_t *key = (public_key_t*)cred;
+
+ print_pubkey(key);
+ key->destroy(key);
+ }
+ if (type == CRED_PRIVATE_KEY)
+ {
+ private_key_t *key = (private_key_t*)cred;
+
+ print_key(key);
+ key->destroy(key);
+ }
+ return 0;
+}
+
+/**
+ * Register the command.
+ */
+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]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"in", 'i', 1, "input file, default: stdin"},
+ {"type", 't', 1, "type of credential, default: x509"},
+ }
+ });
+}
diff --git a/src/pki/commands/pub.c b/src/pki/commands/pub.c
index de0444c1a..fc2614c7d 100644
--- a/src/pki/commands/pub.c
+++ b/src/pki/commands/pub.c
@@ -23,7 +23,7 @@
*/
static int pub()
{
- key_encoding_type_t form = KEY_PUB_SPKI_ASN1_DER;
+ cred_encoding_type_t form = PUBKEY_SPKI_ASN1_DER;
credential_type_t type = CRED_PRIVATE_KEY;
int subtype = KEY_RSA;
certificate_t *cert;
@@ -67,7 +67,7 @@ static int pub()
}
continue;
case 'f':
- if (!get_form(arg, &form, TRUE))
+ if (!get_form(arg, &form, CRED_PUBLIC_KEY))
{
return command_usage("invalid output format");
}
diff --git a/src/pki/commands/req.c b/src/pki/commands/req.c
index 8335f2595..a1ae2f515 100644
--- a/src/pki/commands/req.c
+++ b/src/pki/commands/req.c
@@ -27,6 +27,7 @@
*/
static int req()
{
+ cred_encoding_type_t form = CERT_ASN1_DER;
key_type_t type = KEY_RSA;
hash_algorithm_t digest = HASH_SHA1;
certificate_t *cert = NULL;
@@ -81,6 +82,12 @@ static int req()
case 'p':
challenge_password = chunk_create(arg, strlen(arg));
continue;
+ case 'f':
+ if (!get_form(arg, &form, CRED_CERTIFICATE))
+ {
+ return command_usage("invalid output format");
+ }
+ continue;
case EOF:
break;
default:
@@ -128,8 +135,7 @@ static int req()
error = "generating certificate request failed";
goto end;
}
- encoding = cert->get_encoding(cert);
- if (!encoding.ptr)
+ if (!cert->get_encoding(cert, form, &encoding))
{
error = "encoding certificate request failed";
goto end;
@@ -170,7 +176,7 @@ static void __attribute__ ((constructor))reg()
{"[--in file] [--type rsa|ecdsa]",
" --dn distinguished-name [--san subjectAltName]+",
"[--password challengePassword]",
- "[--digest md5|sha1|sha224|sha256|sha384|sha512]"},
+ "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "private key input file, default: stdin"},
@@ -179,6 +185,7 @@ static void __attribute__ ((constructor))reg()
{"san", 'a', 1, "subjectAltName to include in cert request"},
{"password",'p', 1, "challengePassword to include in cert request"},
{"digest", 'g', 1, "digest for signature creation, default: sha1"},
+ {"outform", 'f', 1, "encoding of generated request, default: der"},
}
});
}
diff --git a/src/pki/commands/self.c b/src/pki/commands/self.c
index d283daa6a..71776c745 100644
--- a/src/pki/commands/self.c
+++ b/src/pki/commands/self.c
@@ -26,6 +26,7 @@
*/
static int self()
{
+ cred_encoding_type_t form = CERT_ASN1_DER;
key_type_t type = KEY_RSA;
hash_algorithm_t digest = HASH_SHA1;
certificate_t *cert = NULL;
@@ -34,7 +35,7 @@ static int self()
char *file = NULL, *dn = NULL, *hex = NULL, *error = NULL;
identification_t *id = NULL;
linked_list_t *san, *ocsp;
- int lifetime = 1080;
+ int lifetime = 1095;
int pathlen = X509_NO_PATH_LEN_CONSTRAINT;
chunk_t serial = chunk_empty;
chunk_t encoding = chunk_empty;
@@ -100,6 +101,26 @@ static int self()
case 'p':
pathlen = atoi(arg);
continue;
+ case 'e':
+ if (streq(arg, "serverAuth"))
+ {
+ flags |= X509_SERVER_AUTH;
+ }
+ else if (streq(arg, "clientAuth"))
+ {
+ flags |= X509_CLIENT_AUTH;
+ }
+ else if (streq(arg, "ocspSigning"))
+ {
+ flags |= X509_OCSP_SIGNER;
+ }
+ continue;
+ case 'f':
+ if (!get_form(arg, &form, CRED_CERTIFICATE))
+ {
+ return command_usage("invalid output format");
+ }
+ continue;
case 'o':
ocsp->insert_last(ocsp, arg);
continue;
@@ -179,8 +200,7 @@ static int self()
error = "generating certificate failed";
goto end;
}
- encoding = cert->get_encoding(cert);
- if (!encoding.ptr)
+ if (!cert->get_encoding(cert, form, &encoding))
{
error = "encoding certificate failed";
goto end;
@@ -225,19 +245,22 @@ static void __attribute__ ((constructor))reg()
{"[--in file] [--type rsa|ecdsa]",
" --dn distinguished-name [--san subjectAltName]+",
"[--lifetime days] [--serial hex] [--ca] [--ocsp uri]+",
- "[--digest md5|sha1|sha224|sha256|sha384|sha512]"},
+ "[--flag serverAuth|clientAuth|ocspSigning]+",
+ "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
{
{"help", 'h', 0, "show usage information"},
{"in", 'i', 1, "private key input file, default: stdin"},
{"type", 't', 1, "type of input key, default: rsa"},
{"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: 1080"},
+ {"lifetime",'l', 1, "days the certificate is valid, default: 1095"},
{"serial", 's', 1, "serial number in hex, default: random"},
{"ca", 'b', 0, "include CA basicConstraint, default: no"},
{"pathlen", 'p', 1, "set path length constraint"},
+ {"flag", 'e', 1, "include extendedKeyUsage flag"},
{"ocsp", 'o', 1, "OCSP AuthorityInfoAccess URI to include"},
{"digest", 'g', 1, "digest for signature creation, default: sha1"},
+ {"outform", 'f', 1, "encoding of generated cert, default: der"},
}
});
}
diff --git a/src/pki/commands/signcrl.c b/src/pki/commands/signcrl.c
new file mode 100644
index 000000000..b7163a153
--- /dev/null
+++ b/src/pki/commands/signcrl.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * 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 "pki.h"
+
+#include <debug.h>
+#include <utils/linked_list.h>
+#include <credentials/certificates/certificate.h>
+#include <credentials/certificates/x509.h>
+#include <credentials/certificates/crl.h>
+
+
+/**
+ * Entry for a revoked certificate
+ */
+typedef struct {
+ chunk_t serial;
+ crl_reason_t reason;
+ time_t date;
+} revoked_t;
+
+/**
+ * Add a revocation to the list
+ */
+static void add_revoked(linked_list_t *list,
+ chunk_t serial, crl_reason_t reason, time_t date)
+{
+ revoked_t *revoked;
+
+ INIT(revoked,
+ .serial = chunk_clone(serial),
+ .reason = reason,
+ .date = date,
+ );
+ list->insert_last(list, revoked);
+}
+
+/**
+ * Destroy a reason entry
+ */
+static void revoked_destroy(revoked_t *revoked)
+{
+ free(revoked->serial.ptr);
+ free(revoked);
+}
+
+/**
+ * Filter for revoked enumerator
+ */
+static bool filter(void *data, revoked_t **revoked, chunk_t *serial, void *p2,
+ time_t *date, void *p3, crl_reason_t *reason)
+{
+ *serial = (*revoked)->serial;
+ *date = (*revoked)->date;
+ *reason = (*revoked)->reason;
+ return TRUE;
+}
+
+/**
+ * Extract the serial of a certificate, write it into buf
+ */
+static int read_serial(char *file, char *buf, int buflen)
+{
+ certificate_t *cert;
+ x509_t *x509;
+ chunk_t serial;
+
+ x509 = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_FROM_FILE, file, BUILD_END);
+ cert = &x509->interface;
+ if (!cert)
+ {
+ return -1;
+ }
+ serial = x509->get_serial(x509);
+ if (serial.len == 0 || serial.len > buflen)
+ {
+ cert->destroy(cert);
+ return -2;
+ }
+ memcpy(buf, serial.ptr, serial.len);
+ cert->destroy(cert);
+ return serial.len;
+}
+
+/**
+ * Sign a CRL
+ */
+static int sign_crl()
+{
+ cred_encoding_type_t form = CERT_ASN1_DER;
+ private_key_t *private = NULL;
+ public_key_t *public = NULL;
+ certificate_t *ca = NULL, *crl = NULL;
+ crl_t *lastcrl = NULL;
+ x509_t *x509;
+ hash_algorithm_t digest = HASH_SHA1;
+ char *arg, *cacert = NULL, *cakey = NULL, *lastupdate = NULL, *error = NULL;
+ char serial[512], crl_serial[8];
+ int serial_len = 0;
+ crl_reason_t reason = CRL_REASON_UNSPECIFIED;
+ time_t thisUpdate, nextUpdate, date = time(NULL);
+ int lifetime = 15;
+ linked_list_t *list;
+ enumerator_t *enumerator, *lastenum = NULL;
+ chunk_t encoding = chunk_empty;
+
+ list = linked_list_create();
+
+ memset(crl_serial, 0, sizeof(crl_serial));
+
+ while (TRUE)
+ {
+ switch (command_getopt(&arg))
+ {
+ case 'h':
+ goto usage;
+ case 'g':
+ digest = get_digest(arg);
+ if (digest == HASH_UNKNOWN)
+ {
+ error = "invalid --digest type";
+ goto usage;
+ }
+ continue;
+ case 'c':
+ cacert = arg;
+ continue;
+ case 'k':
+ cakey = arg;
+ continue;
+ case 'a':
+ lastupdate = arg;
+ continue;
+ case 'l':
+ lifetime = atoi(arg);
+ if (!lifetime)
+ {
+ error = "invalid lifetime";
+ goto usage;
+ }
+ continue;
+ case 'z':
+ serial_len = read_serial(arg, serial, sizeof(serial));
+ if (serial_len < 0)
+ {
+ snprintf(serial, sizeof(serial),
+ "parsing certificate '%s' failed", arg);
+ error = serial;
+ goto error;
+ }
+ add_revoked(list, chunk_create(serial, serial_len), reason, date);
+ date = time(NULL);
+ serial_len = 0;
+ reason = CRL_REASON_UNSPECIFIED;
+ continue;
+ case 's':
+ {
+ chunk_t chunk;
+ int hex_len;
+
+ hex_len = strlen(arg);
+ if ((hex_len / 2) + (hex_len % 2) > sizeof(serial))
+ {
+ error = "invalid serial";
+ goto usage;
+ }
+ chunk = chunk_from_hex(chunk_create(arg, hex_len), serial);
+ serial_len = chunk.len;
+ add_revoked(list, chunk_create(serial, serial_len), reason, date);
+ date = time(NULL);
+ serial_len = 0;
+ reason = CRL_REASON_UNSPECIFIED;
+ continue;
+ }
+ case 'r':
+ if (streq(arg, "key-compromise"))
+ {
+ reason = CRL_REASON_KEY_COMPROMISE;
+ }
+ else if (streq(arg, "ca-compromise"))
+ {
+ reason = CRL_REASON_CA_COMPROMISE;
+ }
+ else if (streq(arg, "affiliation-changed"))
+ {
+ reason = CRL_REASON_AFFILIATION_CHANGED;
+ }
+ else if (streq(arg, "superseded"))
+ {
+ reason = CRL_REASON_SUPERSEDED;
+ }
+ else if (streq(arg, "cessation-of-operation"))
+ {
+ reason = CRL_REASON_CESSATION_OF_OPERATON;
+ }
+ else if (streq(arg, "certificate-hold"))
+ {
+ reason = CRL_REASON_CERTIFICATE_HOLD;
+ }
+ else
+ {
+ return command_usage( "invalid revocation reason");
+ }
+ continue;
+ case 'd':
+ date = atol(arg);
+ if (!date)
+ {
+ error = "invalid date";
+ goto usage;
+ }
+ continue;
+ case 'f':
+ if (!get_form(arg, &form, CRED_CERTIFICATE))
+ {
+ return command_usage("invalid output format");
+ }
+ continue;
+ case EOF:
+ break;
+ default:
+ error = "invalid --signcrl option";
+ goto usage;
+ }
+ break;
+ }
+
+ if (!cacert)
+ {
+ error = "--cacert is required";
+ goto usage;
+ }
+ if (!cakey)
+ {
+ error = "--cakey is required";
+ goto usage;
+ }
+
+ ca = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
+ BUILD_FROM_FILE, cacert, BUILD_END);
+ if (!ca)
+ {
+ error = "parsing CA certificate failed";
+ goto error;
+ }
+ x509 = (x509_t*)ca;
+ if (!(x509->get_flags(x509) & X509_CA))
+ {
+ error = "CA certificate misses CA basicConstraint";
+ goto error;
+ }
+ public = ca->get_public_key(ca);
+ if (!public)
+ {
+ error = "extracting CA certificate public key failed";
+ goto error;
+ }
+ private = lib->creds->create(lib->creds, CRED_PRIVATE_KEY,
+ public->get_type(public),
+ BUILD_FROM_FILE, cakey, BUILD_END);
+ if (!private)
+ {
+ error = "parsing CA private key failed";
+ goto error;
+ }
+ if (!private->belongs_to(private, public))
+ {
+ error = "CA private key does not match CA certificate";
+ goto error;
+ }
+
+ thisUpdate = time(NULL);
+ nextUpdate = thisUpdate + lifetime * 24 * 60 * 60;
+
+ if (lastupdate)
+ {
+ lastcrl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ BUILD_FROM_FILE, lastupdate, BUILD_END);
+ if (!lastcrl)
+ {
+ error = "loading lastUpdate CRL failed";
+ goto error;
+ }
+ memcpy(crl_serial, lastcrl->get_serial(lastcrl).ptr,
+ min(lastcrl->get_serial(lastcrl).len, sizeof(crl_serial)));
+ lastenum = lastcrl->create_enumerator(lastcrl);
+ }
+
+ chunk_increment(chunk_create(crl_serial, sizeof(crl_serial)));
+
+ enumerator = enumerator_create_filter(list->create_enumerator(list),
+ (void*)filter, NULL, NULL);
+ crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
+ BUILD_SIGNING_KEY, private, BUILD_SIGNING_CERT, ca,
+ BUILD_SERIAL, chunk_create(crl_serial, sizeof(crl_serial)),
+ BUILD_NOT_BEFORE_TIME, thisUpdate, BUILD_NOT_AFTER_TIME, nextUpdate,
+ BUILD_REVOKED_ENUMERATOR, enumerator, BUILD_DIGEST_ALG, digest,
+ lastenum ? BUILD_REVOKED_ENUMERATOR : BUILD_END, lastenum,
+ BUILD_END);
+ enumerator->destroy(enumerator);
+ DESTROY_IF(lastenum);
+ DESTROY_IF((certificate_t*)lastcrl);
+
+ if (!crl)
+ {
+ error = "generating CRL failed";
+ goto error;
+ }
+ if (!crl->get_encoding(crl, form, &encoding))
+ {
+ error = "encoding CRL failed";
+ goto error;
+ }
+ if (fwrite(encoding.ptr, encoding.len, 1, stdout) != 1)
+ {
+ error = "writing CRL failed";
+ goto error;
+ }
+
+error:
+ DESTROY_IF(public);
+ DESTROY_IF(private);
+ DESTROY_IF(ca);
+ DESTROY_IF(crl);
+ free(encoding.ptr);
+ list->destroy_function(list, (void*)revoked_destroy);
+ if (error)
+ {
+ fprintf(stderr, "%s\n", error);
+ return 1;
+ }
+ return 0;
+
+usage:
+ list->destroy_function(list, (void*)revoked_destroy);
+ return command_usage(error);
+}
+
+/**
+ * Register the command.
+ */
+static void __attribute__ ((constructor))reg()
+{
+ command_register((command_t) {
+ sign_crl, 'c', "signcrl",
+ "issue a CRL using a CA certificate and key",
+ {"--cacert file --cakey file --lifetime days",
+ "[ [--reason key-compromise|ca-compromise|affiliation-changed|",
+ " superseded|cessation-of-operation|certificate-hold]",
+ " [--date timestamp]",
+ " --cert file | --serial hex ]*",
+ "[--digest md5|sha1|sha224|sha256|sha384|sha512] [--outform der|pem]"},
+ {
+ {"help", 'h', 0, "show usage information"},
+ {"cacert", 'c', 1, "CA certificate file"},
+ {"cakey", 'k', 1, "CA private key file"},
+ {"lifetime",'l', 1, "days the CRL gets a nextUpdate, default: 15"},
+ {"lastcrl", 'a', 1, "CRL of lastUpdate to copy revocations from"},
+ {"cert", 'z', 1, "certificate file to revoke"},
+ {"serial", 's', 1, "hex encoded certificate serial number to revoke"},
+ {"reason", 'r', 1, "reason for certificate revocation"},
+ {"date", 'd', 1, "revocation date as unix timestamp, default: now"},
+ {"digest", 'g', 1, "digest for signature creation, default: sha1"},
+ {"outform", 'f', 1, "encoding of generated crl, default: der"},
+ }
+ });
+}