#include #include #include "creddy.h" #define KEY_SUFFIX "_private.der" #define CERT_SUFFIX "_ID.pem" static private_key_t *_generate_key(char *filename); static void _generate_cert(char *filename, private_key_t *private, char *cn, int validity); void generate_main(options_t *opts) { int ret; chunk_t encoding; char *cn = opts->cn, *filename; int cn_len; private_key_t *key; if (cn == NULL) usage(opts); if (!clean_name(cn)) { printf("Invalid CN: must start with a letter and be alphanumeric\n"); usage(opts); } if (opts->validity <= 0) { printf("Validity must be >= 1 day\n"); usage(opts); } cn_len = strlen(cn); // key output filename: "${cn}_private.der" filename = xmalloc(cn_len + sizeof(KEY_SUFFIX)); memcpy(filename, cn, cn_len); memcpy(filename + cn_len, KEY_SUFFIX, sizeof(KEY_SUFFIX)); key = _generate_key(filename); // cert filename: ${cn}_ID.pem memcpy(filename +cn_len, CERT_SUFFIX, sizeof(CERT_SUFFIX)); _generate_cert(filename, key, cn, opts->validity); free(filename); DESTROY_IF(key); } static private_key_t *_generate_key(char *filename) { private_key_t *key; int ret; chunk_t encoding; printf("Generating key, this will take a while. Create entropy!\n"); printf(" - move the mouse\n"); printf(" - generate disk activity (run find)\n"); // generate the key key = lib->creds->create( lib->creds, CRED_PRIVATE_KEY, KEY_RSA, BUILD_KEY_SIZE, 2048, BUILD_END ); if (key == NULL) errx(1, "Key generation failed"); // get the key encoding ret = key->get_encoding(key, KEY_PRIV_ASN1_DER, &encoding); if (!ret) errx(1, "Couldn't encode private key"); // write it to a file (with mode 600) int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd < 0) err(1, "Couldn't open %s for writing private key", filename); ret = write(fd, encoding.ptr, encoding.len); if (ret < encoding.len) errx(1, "Couldn't write all the bytes of the private key"); close(fd); free(encoding.ptr); return key; } static char *_create_dn(char *cn) { #define DN "cn=" char *dn = xmalloc(sizeof(DN) + strlen(cn)); memcpy(dn, DN, sizeof(DN)); strcat(dn, cn); return dn; } #define BYTES_PER_LINE 64 // thx libstrongswan static void _encode_base64(char *filename, chunk_t encoding) { int start; chunk_t b64 = chunk_to_base64(encoding, NULL); FILE *out = fopen(filename, "w"); if (out == NULL) err(1, "couldn't open %s for writing", filename); fprintf(out, "-----BEGIN CERTIFICATE-----\n"); for (start = 0; start < b64.len; start += BYTES_PER_LINE) { int left = b64.len - start; int len = left < BYTES_PER_LINE ? left : BYTES_PER_LINE; fwrite(b64.ptr + start, len, 1, out); fprintf(out, "\n"); } fprintf(out, "-----END CERTIFICATE-----\n"); fclose(out); free(b64.ptr); } static void _generate_cert(char *filename, private_key_t *private, char *cn, int validity) { // build the DN char *dn_string = _create_dn(cn); identification_t *id = identification_create_from_string(dn_string); if (id == NULL) errx(1, "couldn't create ID from DN %s", dn_string); free(dn_string); // get the public key public_key_t *public = private->get_public_key(private); if (public == NULL) errx(1, "couldn't get public key from private key"); // create a serial (stolen from strongswan pki) rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) errx(1, "no random number generator"); // random serial chunk_t serial = generate_serial(); // validity period time_t not_before = time(NULL); time_t not_after = not_before + validity * 24 * 60 * 60; // create! certificate_t *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, BUILD_NOT_AFTER_TIME, not_after, BUILD_SERIAL, serial, BUILD_DIGEST_ALG, HASH_SHA1, BUILD_X509_FLAG, X509_CA, BUILD_PATHLEN, X509_NO_PATH_LEN_CONSTRAINT, BUILD_END ); if (cert == NULL) errx(1, "couldn't build cert :("); chunk_t encoding = cert->get_encoding(cert); // sigh _encode_base64(filename, encoding); DESTROY_IF(id); DESTROY_IF(cert); DESTROY_IF(public); free(encoding.ptr); free(serial.ptr); } chunk_t generate_serial() { chunk_t serial = chunk_empty; // create a serial (stolen from strongswan pki) rng_t *rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); if (!rng) errx(1, "no random number generator"); rng->allocate_bytes(rng, 8, &serial); while (serial.ptr[0] == 0) // don't get leading 0's rng->get_bytes(rng, 1, serial.ptr); rng->destroy(rng); return serial; }