From 39f868fc610141015b93f2af16c8fd7e18bea987 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Fri, 3 May 2024 13:26:43 +0200 Subject: [PATCH] gencert: C replacement for python+rust gencert Smaller, fewer dependencies, and quicker to cross-compile. Signed-off-by: Joachim Wiberg --- src/gencert/.gitignore | 28 +++++++ src/gencert/LICENSE | 14 ++++ src/gencert/Makefile.am | 8 ++ src/gencert/autogen.sh | 3 + src/gencert/configure.ac | 64 +++++++++++++++ src/gencert/gencert.c | 172 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 289 insertions(+) create mode 100644 src/gencert/.gitignore create mode 100644 src/gencert/LICENSE create mode 100644 src/gencert/Makefile.am create mode 100755 src/gencert/autogen.sh create mode 100644 src/gencert/configure.ac create mode 100644 src/gencert/gencert.c diff --git a/src/gencert/.gitignore b/src/gencert/.gitignore new file mode 100644 index 00000000..a4c6db8e --- /dev/null +++ b/src/gencert/.gitignore @@ -0,0 +1,28 @@ +*~ +*.o +gencert + +/aclocal.m4 +/autom4te.cache/ +/aux +/clixon.xml +/compile +/config.h +/config.h.in +/config.guess +/config.log +/config.status +/config.sub +/configure +/depcomp +/install-sh +/libtool +/ltmain.sh +/m4 +/missing + +GPATH +GRTAGS +GTAGS +Makefile +Makefile.in diff --git a/src/gencert/LICENSE b/src/gencert/LICENSE new file mode 100644 index 00000000..65e3295d --- /dev/null +++ b/src/gencert/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2024 The KernelKit Authors + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + diff --git a/src/gencert/Makefile.am b/src/gencert/Makefile.am new file mode 100644 index 00000000..447219c1 --- /dev/null +++ b/src/gencert/Makefile.am @@ -0,0 +1,8 @@ +DISTCLEANFILES = *~ *.d +ACLOCAL_AMFLAGS = -I m4 + +bin_PROGRAMS = gencert +gencert_SOURCES = gencert.c +gencert_CFLAGS = -W -Wall -Wextra -Wno-unused +gencert_CFLAGS = $(ssl_CFLAGS) +gencert_LDADD = $(ssl_LIBS) diff --git a/src/gencert/autogen.sh b/src/gencert/autogen.sh new file mode 100755 index 00000000..69ad0e18 --- /dev/null +++ b/src/gencert/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +autoreconf -W portability -vifm diff --git a/src/gencert/configure.ac b/src/gencert/configure.ac new file mode 100644 index 00000000..7ba787bd --- /dev/null +++ b/src/gencert/configure.ac @@ -0,0 +1,64 @@ +AC_PREREQ(2.61) +AC_INIT([gencert], [3.0], [https://github.com/kernelkit/infix/issues]) +AM_INIT_AUTOMAKE(1.11 foreign) +AM_SILENT_RULES(yes) + +AC_CONFIG_FILES([ + Makefile +]) + +AC_PROG_CC +AC_PROG_INSTALL + +# Check for pkg-config first, warn if it's not installed +PKG_PROG_PKG_CONFIG + +PKG_CHECK_MODULES([ssl], [openssl >= 3.0.0]) + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DATAROOTDIR=`eval echo $datarootdir` +DATAROOTDIR=`eval echo $DATAROOTDIR` +AC_SUBST(DATAROOTDIR) + +LIBDIR=`eval echo $libdir` +LIBDIR=`eval echo $LIBDIR` +AC_SUBST(LIBDIR) + +LOCALSTATEDIR=`eval echo $localstatedir` +LOCALSTATEDIR=`eval echo $LOCALSTATEDIR` +AC_SUBST(LOCALSTATEDIR) + +RUNSTATEDIR=`eval echo $runstatedir` +RUNSTATEDIR=`eval echo $RUNSTATEDIR` +AC_SUBST(RUNSTATEDIR) + +SYSCONFDIR=`eval echo $sysconfdir` +SYSCONFDIR=`eval echo $SYSCONFDIR` +AC_SUBST(SYSCONFDIR) + +AC_OUTPUT + +cat < +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern char *__progname; + +static int usage(int rc) +{ + fprintf(stderr, + "Usage: %s\n" + "\n" + "Options:\n" + " --country COUNTRY Set country, default: US\n" + " --state STATE Set state or province, default: California\n" + " --city CITY Set city name, default: Berkeley\n" + " --organization ORG Set organization name, default: Acme, Inc.\n" + " --organization-unit UNIT Set organization unit name, default: Second\n" + " --common-name CN Set common name, default: none (required!)\n" + " --out-certificate FILE Output certificate\n" + " --out-key FILE Output private key\n" + , __progname); + return rc; +} + +int main(int argc, char *argv[]) +{ + struct option opts[] = { + {"city", required_argument, 0, 'l'}, + {"common-name", required_argument, 0, 'n'}, + {"country", required_argument, 0, 'c'}, + {"help", no_argument, 0, 'h'}, + {"organization", required_argument, 0, 'o'}, + {"organisation", required_argument, 0, 'o'}, + {"organization-unit", required_argument, 0, 'u'}, + {"organisation-unit", required_argument, 0, 'u'}, + {"out-certificate", required_argument, 0, 'p'}, + {"out-key", required_argument, 0, 'k'}, + {"state", required_argument, 0, 's'}, + {0, 0, 0, 0} + }; + char *common_name = NULL, *cert = "certificate.pem", *key = "private.key"; + char *country = "US", *state = "California", *locality = "Berkeley"; + char *organization = "Acme, Inc.", *unit = "Second"; + ASN1_INTEGER *asn1_serno; + EVP_PKEY_CTX *pkey_ctx; + BIGNUM *bn_serial; + X509_NAME *name; + EVP_PKEY *pkey; + X509 *x509; + FILE *fp; + int c; + + while ((c = getopt_long(argc, argv, "c:hs:l:o:u:n:", opts, NULL)) != -1) { + switch (c) { + case 'c': + country = optarg; + break; + case 'h': + case '?': + return usage(0); + case 'k': + key = optarg; + break; + case 'l': + locality = optarg; + break; + case 'n': + common_name = optarg; + break; + case 'o': + organization = optarg; + break; + case 'p': + cert = optarg; + break; + case 's': + state = optarg; + break; + case 'u': + unit = optarg; + break; + default: + fprintf(stderr, "got here: '%c'\n", c); + abort(); + } + } + + if (!common_name) + return usage(1); + + OpenSSL_add_all_algorithms(); + + /* Key generation using EVP */ + pkey = EVP_PKEY_new(); + pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (!EVP_PKEY_keygen_init(pkey_ctx) || !EVP_PKEY_CTX_set_rsa_keygen_bits(pkey_ctx, 2048) || + !EVP_PKEY_keygen(pkey_ctx, &pkey)) { + fprintf(stderr, "Error generating RSA key\n"); + EVP_PKEY_CTX_free(pkey_ctx); + return 1; + } + EVP_PKEY_CTX_free(pkey_ctx); + + /* Certificate creation */ + x509 = X509_new(); + assert(x509); + X509_set_version(x509, 2); + + /* Generating a random serial number */ + bn_serial = BN_new(); + assert(bn_serial); + BN_rand(bn_serial, 160, 0, 0); /* Generate a 160-bit serial number */ + asn1_serno = BN_to_ASN1_INTEGER(bn_serial, NULL); + assert(asn1_serno); + X509_set_serialNumber(x509, asn1_serno); + + ASN1_TIME_set_string(X509_getm_notBefore(x509), "20000101000000Z"); + ASN1_TIME_set_string(X509_getm_notAfter(x509), "99990101000000Z"); + + name = X509_get_subject_name(x509); + X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)country, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "ST", MBSTRING_ASC, (unsigned char *)state, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "L", MBSTRING_ASC, (unsigned char *)locality, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, (unsigned char *)organization, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "OU", MBSTRING_ASC, (unsigned char *)unit, -1, -1, 0); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0); + + X509_set_issuer_name(x509, name); + X509_set_pubkey(x509, pkey); + + if (!X509_sign(x509, pkey, EVP_sha256())) { + fprintf(stderr, "Error signing certificate\n"); + return 1; + } + + /* Saving the certificate */ + fp = fopen(cert, "w"); + if (!fp) { + fprintf(stderr, "Failed opening %s for writing: %s\n", cert, strerror(errno)); + goto done; + } + PEM_write_X509(fp, x509); + fclose(fp); + + /* Saving the private key */ + fp = fopen(key, "w"); + if (!fp) { + fprintf(stderr, "Failed opening %s for writing: %s\n", key, strerror(errno)); + goto done; + } + PEM_write_PrivateKey(fp, pkey, NULL, NULL, 0, NULL, NULL); + fclose(fp); + +done: /* Cleanup */ + X509_free(x509); + EVP_PKEY_free(pkey); + BN_free(bn_serial); + ASN1_INTEGER_free(asn1_serno); + + return 0; +}